From c042a172e3e9a0beb7f3a7f466b19e596c95b404 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Thu, 1 Feb 2024 14:21:37 +0400 Subject: [PATCH 01/12] [RM-84612] Updated dependencies --- README.md | 17 +++++- build.gradle | 53 +++++++++++++++++-- gradle.properties | 2 +- .../com/exactpro/th2/hand/HandServer.java | 2 + .../builders/events/EventPayloadBuilder.java | 1 + .../mstore/DefaultMessageStoreBuilder.java | 1 + .../services/mstore/MessageStoreSender.java | 1 + 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6708e6e..3795db6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# th2 hand (4.0.0) +# th2 hand (5.0.0) th2-hand is used to interpret and transmit commands from th2-act to Selenium or Windows Application Driver and vice versa. All incoming and outgoing data is stored in Cradle as messages. @@ -92,6 +92,21 @@ Example of `rabbitMQ.json`: ## Release Notes +### 5.0.0 + ++ Added th2 transport support + +#### Updated lib: ++ bom: `4.5.0` ++ common: 5.8.0-dev ++ grpc-hand: 3.0.0-dev + +#### Added plugin: ++ org.owasp.dependencycheck: `9.0.9` ++ com.gorylenko.gradle-git-properties: `2.4.1` ++ com.github.jk1.dependency-license-report: `2.5` ++ de.undercouch.download: `5.4.0` + ### 4.0.0 + Migrated to Books & Pages concept diff --git a/build.gradle b/build.gradle index 0276079..e431a49 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright 2009-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2009-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,18 @@ * limitations under the License. ******************************************************************************/ +import com.github.jk1.license.filter.LicenseBundleNormalizer +import com.github.jk1.license.render.JsonReportRenderer + plugins { id 'java' id 'java-library' id 'application' id 'com.palantir.docker' version '0.34.0' + id "org.owasp.dependencycheck" version "9.0.9" + id "com.gorylenko.gradle-git-properties" version "2.4.1" + id 'com.github.jk1.dependency-license-report' version '2.5' + id "de.undercouch.download" version "5.4.0" } group 'com.exactpro.th2' @@ -45,18 +52,24 @@ repositories { } dependencies { - api platform('com.exactpro.th2:bom:4.1.0') + api platform('com.exactpro.th2:bom:4.5.0') implementation('com.exactpro.remotehand:remotehand:1.7.3-TH2-4662-4046816762-SNAPSHOT') { exclude group: "org.slf4j", module: "slf4j-log4j12" } - implementation("com.exactpro.th2:grpc-hand:2.11.0-TH2-3884-2590730423-SNAPSHOT") { + implementation("com.exactpro.th2:grpc-hand:3.0.0-RM-84612-+") { exclude group: "com.google.guava", module: "guava" // for compatibility with Selenium 3.141.59 } - implementation("com.exactpro.th2:common:5.0.0-dev-version-5-3838510969-SNAPSHOT") { + implementation("com.exactpro.th2:common:5.8.0-dev") { exclude group: "com.google.guava", module: "guava" // for compatibility with Selenium 3.141.59 } + implementation 'org.slf4j:slf4j-api' + + implementation "com.fasterxml.jackson.core:jackson-core" + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-annotations" + implementation 'org.apache.commons:commons-lang3' implementation "org.apache.commons:commons-csv:1.9.0" } @@ -95,4 +108,36 @@ jar { 'Implementation-Version': project.version ) } +} + +dependencyCheck { + formats = ['SARIF', 'JSON', 'HTML'] + failBuildOnCVSS = 5 + + analyzers { + assemblyEnabled = false + nugetconfEnabled = false + nodeEnabled = false + } +} + +licenseReport { + def licenseNormalizerBundlePath = "$buildDir/license-normalizer-bundle.json" + + if (!file(licenseNormalizerBundlePath).exists()) { + download.run { + src 'https://raw.githubusercontent.com/th2-net/.github/main/license-compliance/gradle-license-report/license-normalizer-bundle.json' + dest "$buildDir/license-normalizer-bundle.json" + overwrite false + } + } + + filters = [ + new LicenseBundleNormalizer(licenseNormalizerBundlePath, false) + ] + renderers = [ + new JsonReportRenderer('licenses.json', false), + ] + excludeOwnGroup = false + allowedLicensesFile = new URL("https://raw.githubusercontent.com/th2-net/.github/main/license-compliance/gradle-license-report/allowed-licenses.json") } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index def873a..bb05d67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ -release_version = 4.0.0 +release_version = 5.0.0 docker_image_name = th2-hand \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/HandServer.java b/src/main/java/com/exactpro/th2/hand/HandServer.java index 290e69c..8b51d64 100644 --- a/src/main/java/com/exactpro/th2/hand/HandServer.java +++ b/src/main/java/com/exactpro/th2/hand/HandServer.java @@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +// FIXME: implements auto closable public class HandServer { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -56,6 +57,7 @@ protected Server buildServer() throws Exception { /** Start serving requests. */ public void start() throws IOException { + // FIXME: close resource new Thread(SessionWatcher.getWatcher()).start(); server.start(); logger.info("Server started, listening on port {}", server.getPort()); diff --git a/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java index 3523f24..f36d9e4 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java @@ -59,6 +59,7 @@ public EventPayloadBuilder printTable(String tableHeader, Map ta public byte[] toByteArray() { try { + //FIXME: use MAPPER from AbstractCommonFactory return new ObjectMapper().writeValueAsBytes(this.data); } catch (JsonProcessingException e) { logger.error("Error while creating body", e); diff --git a/src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java index 08849e1..2a4db84 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java @@ -42,6 +42,7 @@ public final class DefaultMessageStoreBuilder implements MessageStoreBuilder { private static final Logger logger = LoggerFactory.getLogger(DefaultMessageStoreBuilder.class); + // FIXME: use MAPPER from common private final ObjectMapper mapper = new ObjectMapper(); private final AtomicLong seqNum; private final CommonFactory factory; diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java index ff0c073..f038fa1 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java @@ -112,6 +112,7 @@ private long calculateSize(RawMessage message) { return message.getBody().size(); } + // TODO: redundant method, because common already prints data into log private void writeToLogAboutConnection(CommonFactory factory) { if (!logger.isInfoEnabled()) return; From 09bbcb6b913b3ea6d03875691bef22755cd211f8 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Fri, 2 Feb 2024 14:55:16 +0400 Subject: [PATCH 02/12] [RM-84612] added useTransport option --- README.md | 17 ++- build.gradle | 3 + gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/com/exactpro/th2/hand/Config.java | 6 + ....java => ProtobufMessageStoreBuilder.java} | 15 +-- .../mstore/TransportMessageStoreBuilder.java | 97 ++++++++++++++ .../th2/hand/schema/CustomConfiguration.java | 35 ++++-- .../th2/hand/services/MessageHandler.java | 35 ++++-- .../services/mstore/MessageStoreHandler.java | 45 ++++--- .../services/mstore/MessageStoreSender.java | 119 +----------------- .../mstore/ProtobufMessageStoreSender.java | 108 ++++++++++++++++ .../mstore/TransportMessageStoreSender.java | 111 ++++++++++++++++ 12 files changed, 430 insertions(+), 163 deletions(-) rename src/main/java/com/exactpro/th2/hand/builders/mstore/{DefaultMessageStoreBuilder.java => ProtobufMessageStoreBuilder.java} (87%) create mode 100644 src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java create mode 100644 src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java create mode 100644 src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java diff --git a/README.md b/README.md index 3795db6..4b420d7 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,15 @@ This project uses the Schema API to get its settings. For local run it needs `custom.json`, `grpc.json`, `rabbitMQ.json` and `mq.json` files. The `custom.json` file contains RemoteHand URLs map and has the following format: -- **session-alias == "th2-hand" by default** + +- **session-alias / sessionAlias == "th2-hand" by default** - hand publishes messages related to UI command under this session alias +- **screenshot-session-alias / screenshotSessionAlias = "th2-hand-screenshot" by default** - hand publishes messages with a screenshot under this session alias +- **session-group / sessionGroup = "th2-hand-group" by default** - hand publishes all messages under this session group. +- **message-batch-limit / messageBatchLimit = 1048576 by default** - limit size for batching messages. +- **drivers-mapping / driversMapping** - UI drivers settings. +- **rh-options / rhOptions** - remote hand options settings. +- **response-timeout-sec / responseTimeoutSec = 120 by default** - timeout for waiting result form remote hand. +- **use-transport / useTransport = true by default** - if true, hand used th2 transport protocol to publish messages via MQ. ``` { "session-alias": "aliasName", @@ -98,8 +106,11 @@ Example of `rabbitMQ.json`: #### Updated lib: + bom: `4.5.0` -+ common: 5.8.0-dev -+ grpc-hand: 3.0.0-dev ++ common: `5.8.0-dev` ++ grpc-hand: `3.0.0-dev` + +#### Added lib: ++ common-utils: `2.2.2-dev` #### Added plugin: + org.owasp.dependencycheck: `9.0.9` diff --git a/build.gradle b/build.gradle index e431a49..53cc56e 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,9 @@ dependencies { implementation("com.exactpro.th2:common:5.8.0-dev") { exclude group: "com.google.guava", module: "guava" // for compatibility with Selenium 3.141.59 } + implementation("com.exactpro.th2:common-utils:2.2.2-dev") { + exclude group: "com.google.guava", module: "guava" // for compatibility with Selenium 3.141.59 + } implementation 'org.slf4j:slf4j-api' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ad7440b..0671300 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/src/main/java/com/exactpro/th2/hand/Config.java b/src/main/java/com/exactpro/th2/hand/Config.java index 2b0f09e..6267df0 100644 --- a/src/main/java/com/exactpro/th2/hand/Config.java +++ b/src/main/java/com/exactpro/th2/hand/Config.java @@ -65,10 +65,16 @@ public String getSessionGroup() { return customConfiguration.getSessionGroup(); } + public String getBook() { + return factory.getBoxConfiguration().getBookName(); + } + public String getScreenshotSessionAlias() { return customConfiguration.getScreenshotSessionAlias(); } + public boolean isUseTransport() { return customConfiguration.isUseTransport(); } + public static class DriverMapping { public final RemoteManagerType type; public final String url; diff --git a/src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java similarity index 87% rename from src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java rename to src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java index 2a4db84..da38b6c 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/mstore/DefaultMessageStoreBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,12 @@ import com.exactpro.remotehand.Configuration; import com.exactpro.th2.common.grpc.ConnectionID; +import com.exactpro.th2.common.grpc.Direction; import com.exactpro.th2.common.grpc.MessageID; import com.exactpro.th2.common.grpc.RawMessage; import com.exactpro.th2.common.grpc.RawMessageMetadata; -import com.exactpro.th2.common.grpc.Direction; import com.exactpro.th2.common.schema.factory.CommonFactory; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.protobuf.ByteString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,15 +38,13 @@ import static com.exactpro.th2.hand.utils.Utils.getTimestamp; -public final class DefaultMessageStoreBuilder implements MessageStoreBuilder { - private static final Logger logger = LoggerFactory.getLogger(DefaultMessageStoreBuilder.class); +public final class ProtobufMessageStoreBuilder implements MessageStoreBuilder { + private static final Logger logger = LoggerFactory.getLogger(ProtobufMessageStoreBuilder.class); - // FIXME: use MAPPER from common - private final ObjectMapper mapper = new ObjectMapper(); private final AtomicLong seqNum; private final CommonFactory factory; - public DefaultMessageStoreBuilder(CommonFactory factory, AtomicLong seqNum) { + public ProtobufMessageStoreBuilder(CommonFactory factory, AtomicLong seqNum) { this.factory = factory; this.seqNum = seqNum; } @@ -55,7 +52,7 @@ public DefaultMessageStoreBuilder(CommonFactory factory, AtomicLong seqNum) { @Override public RawMessage buildMessage(Map fields, Direction direction, String sessionId, String sessionGroup) { try { - byte[] bytes = mapper.writeValueAsBytes(fields); + byte[] bytes = CommonFactory.MAPPER.writeValueAsBytes(fields); return buildMessage(bytes, direction, sessionId, sessionGroup); } catch (JsonProcessingException e) { logger.error("Could not encode message as JSON", e); diff --git a/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java new file mode 100644 index 0000000..f268039 --- /dev/null +++ b/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java @@ -0,0 +1,97 @@ +/* + * Copyright 2024 Exactpro (Exactpro Systems Limited) + * + * 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.exactpro.th2.hand.builders.mstore; + +import com.exactpro.remotehand.Configuration; +import com.exactpro.th2.common.grpc.Direction; +import com.exactpro.th2.common.schema.factory.CommonFactory; +import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.MessageId; +import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.RawMessage; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import static com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.TransportUtilsKt.getTransport; + +public final class TransportMessageStoreBuilder implements MessageStoreBuilder { + private static final Logger logger = LoggerFactory.getLogger(TransportMessageStoreBuilder.class); + + private final AtomicLong seqNum; + + public TransportMessageStoreBuilder(AtomicLong seqNum) { + this.seqNum = seqNum; + } + + @Override + public RawMessage buildMessage(Map fields, Direction direction, String sessionId, String sessionGroup) { + try { + byte[] bytes = CommonFactory.MAPPER.writeValueAsBytes(fields); + return buildMessage(bytes, direction, sessionId, sessionGroup); + } catch (JsonProcessingException e) { + logger.error("Could not encode message as JSON", e); + return null; + } + } + + @Override + public RawMessage buildMessage(byte[] bytes, Direction direction, String sessionId, String sessionGroup) { + RawMessage.Builder builder = RawMessage.builder() + .setId(MessageId.builder() + .setSessionAlias(sessionId) + .setDirection(getTransport(direction)) + .setSequence(seqNum.incrementAndGet()) + .setTimestamp(Instant.now()) + .build()) + .setBody(bytes); + return builder.build(); + } + + @Override + public RawMessage buildMessageFromFile(Path path, Direction direction, String sessionId, String sessionGroup) { + String protocol = "image/" + Configuration.getInstance().getDefaultScreenWriter().getScreenshotExtension(); + + try (InputStream is = Files.newInputStream(path)) { + int length = Math.toIntExact(path.toFile().length()); + ByteBuf buffer = Unpooled.buffer(length); + buffer.writeBytes(is, length); + + return RawMessage.builder() + .setId(MessageId.builder() + .setSessionAlias(sessionId) + .setDirection(getTransport(direction)) + .setSequence(seqNum.incrementAndGet()) + .setTimestamp(Instant.now()) + .build()) + .setProtocol(protocol) + .setBody(buffer) + .build(); + } catch (IOException e) { + logger.error("Cannot encode screenshot", e); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java b/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java index 5dc9258..81ff1e0 100644 --- a/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java +++ b/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,33 +20,48 @@ import java.util.Map; import com.exactpro.th2.hand.Config; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; @SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"}) public class CustomConfiguration { + private static final String DEFAULT_SESSION_GROUP = "th2-hand-group"; private static final String DEFAULT_SESSION_ALIAS = "th2-hand"; + private static final String DEFAULT_SCREENSHOT_SESSION_ALIAS = "th2-hand-screenshot"; private static final int DEFAULT_RESPONSE_TIMEOUT = 120; private static final long DEFAULT_MESSAGE_BATCH_LIMIT = 1024 * 1024; // 1 MB @JsonProperty(value="session-alias", required = true, defaultValue = DEFAULT_SESSION_ALIAS) + @JsonAlias("sessionAlias") private String sessionAlias = DEFAULT_SESSION_ALIAS; - private String screenshotSessionAlias = null; + @JsonProperty(value="screenshot-session-alias", required = true, defaultValue = DEFAULT_SCREENSHOT_SESSION_ALIAS) + @JsonAlias("screenshotSessionAlias") + private String screenshotSessionAlias = DEFAULT_SCREENSHOT_SESSION_ALIAS; - @JsonProperty(value="sessionGroup") - private String sessionGroup = null; + @JsonProperty(value="session-group", defaultValue = DEFAULT_SESSION_GROUP) + @JsonAlias("sessionGroup") + private String sessionGroup = DEFAULT_SESSION_GROUP; @JsonProperty(value="message-batch-limit") + @JsonAlias("messageBatchLimit") private long messageBatchLimit = DEFAULT_MESSAGE_BATCH_LIMIT; - @JsonProperty(value="driversMapping", required = true) + @JsonProperty(value="drivers-mapping", required = true) + @JsonAlias("driversMapping") private Map driversMapping; - @JsonProperty(value="rhOptions") - private Map rhOptions = Collections.emptyMap();; + @JsonProperty(value="rh-options") + @JsonAlias("rhOptions") + private Map rhOptions = Collections.emptyMap(); - @JsonProperty(value="responseTimeoutSec") + @JsonProperty(value="response-timeout-sec") + @JsonAlias("responseTimeoutSec") private int responseTimeout = DEFAULT_RESPONSE_TIMEOUT; + @JsonProperty(value="use-transport") + @JsonAlias("responseTimeoutSec") + private boolean useTransport = true; + public Map getDriversMapping() { return driversMapping; } @@ -77,4 +92,8 @@ public String getScreenshotSessionAlias() { public long getMessageBatchLimit() { return messageBatchLimit; } + + public boolean isUseTransport() { + return useTransport; + } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java index 8ab605c..c27e6a8 100644 --- a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,25 @@ import com.exactpro.th2.act.grpc.hand.RhActionsBatch; import com.exactpro.th2.act.grpc.hand.RhBatchResponse; import com.exactpro.th2.common.schema.factory.CommonFactory; +import com.exactpro.th2.common.utils.message.transport.MessageUtilsKt; import com.exactpro.th2.hand.Config; import com.exactpro.th2.hand.RhConnectionManager; import com.exactpro.th2.hand.builders.events.DefaultEventBuilder; -import com.exactpro.th2.hand.builders.mstore.DefaultMessageStoreBuilder; +import com.exactpro.th2.hand.builders.mstore.ProtobufMessageStoreBuilder; +import com.exactpro.th2.hand.builders.mstore.TransportMessageStoreBuilder; import com.exactpro.th2.hand.builders.script.ScriptBuilder; import com.exactpro.th2.hand.requestexecutors.ActionsBatchExecutor; import com.exactpro.th2.hand.services.estore.EventStoreHandler; import com.exactpro.th2.hand.services.estore.EventStoreSender; import com.exactpro.th2.hand.services.mstore.MessageStoreHandler; -import com.exactpro.th2.hand.services.mstore.MessageStoreSender; +import com.exactpro.th2.hand.services.mstore.ProtobufMessageStoreSender; +import com.exactpro.th2.hand.services.mstore.TransportMessageStoreSender; import java.util.concurrent.atomic.AtomicLong; public class MessageHandler implements AutoCloseable { private final Config config; - private final MessageStoreHandler messageStoreHandler; + private final MessageStoreHandler messageStoreHandler; private final EventStoreHandler eventStoreHandler; private final RhConnectionManager rhConnectionManager; private final ScriptBuilder scriptBuilder = new ScriptBuilder(); @@ -43,15 +46,27 @@ public MessageHandler(Config config, AtomicLong seqNum) { this.config = config; rhConnectionManager = new RhConnectionManager(config); CommonFactory factory = config.getFactory(); - this.messageStoreHandler = new MessageStoreHandler( - config.getSessionGroup(), - new MessageStoreSender(factory), - new DefaultMessageStoreBuilder(config.getFactory(), seqNum) - ); + if (config.isUseTransport()) { + this.messageStoreHandler = new MessageStoreHandler<>( + config.getSessionGroup(), + new TransportMessageStoreSender(factory), + new TransportMessageStoreBuilder(seqNum), + message -> MessageUtilsKt.toProto(message.getId(), config.getBook(), config.getSessionGroup()) + ); + } else { + this.messageStoreHandler = new MessageStoreHandler<>( + config.getSessionGroup(), + new ProtobufMessageStoreSender(factory), + new ProtobufMessageStoreBuilder(config.getFactory(), seqNum), + message -> message.getMetadata().getId() + ); + } + + this.eventStoreHandler = new EventStoreHandler(new EventStoreSender(factory.getEventBatchRouter()), new DefaultEventBuilder(factory)); } - public MessageStoreHandler getMessageStoreHandler() { + public MessageStoreHandler getMessageStoreHandler() { return messageStoreHandler; } diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java index f923ea3..c7e96d3 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,7 @@ import com.exactpro.th2.act.grpc.hand.RhActionsBatch; import com.exactpro.th2.common.grpc.Direction; import com.exactpro.th2.common.grpc.MessageID; -import com.exactpro.th2.common.grpc.RawMessage; -import com.exactpro.th2.hand.builders.mstore.DefaultMessageStoreBuilder; +import com.exactpro.th2.hand.builders.mstore.MessageStoreBuilder; import com.exactpro.th2.hand.messages.RhResponseMessageBody; import com.google.protobuf.Descriptors; import com.google.protobuf.GeneratedMessageV3; @@ -36,23 +35,28 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.ArrayList; -import java.util.LinkedHashMap; -public class MessageStoreHandler implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(MessageStoreSender.class); +public class MessageStoreHandler implements AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(ProtobufMessageStoreSender.class); private final String sessionGroup; - private final MessageStoreSender messageStoreSender; - private final DefaultMessageStoreBuilder messageStoreBuilder; - - public MessageStoreHandler(String sessionGroup, MessageStoreSender messageStoreSender, DefaultMessageStoreBuilder defaultMessageStoreBuilder) { + private final MessageStoreSender messageStoreSender; + private final MessageStoreBuilder messageStoreBuilder; + private final MessageIdExtractor messageIdExtractor; + + public MessageStoreHandler(String sessionGroup, + MessageStoreSender messageStoreSender, + MessageStoreBuilder defaultMessageStoreBuilder, + MessageIdExtractor messageIdExtractor) { this.sessionGroup = sessionGroup; this.messageStoreSender = messageStoreSender; this.messageStoreBuilder = defaultMessageStoreBuilder; + this.messageIdExtractor = messageIdExtractor; } private List getActionsList (RhActionsBatch actionsList) { @@ -93,12 +97,12 @@ public List onRequest(RhActionsBatch actionsList, String sessionId) { } } - RawMessage message = messageStoreBuilder.buildMessage(Collections.singletonMap("messages", allMessages), + T message = messageStoreBuilder.buildMessage(Collections.singletonMap("messages", allMessages), Direction.SECOND, sessionId, sessionGroup); if (message != null) { messageStoreSender.sendMessages(message); - return Collections.singletonList(message.getMetadata().getId()); + return Collections.singletonList(messageIdExtractor.getId(message)); } else { logger.debug("Nothing to store to mstore"); return Collections.emptyList(); @@ -112,7 +116,7 @@ public List storeScreenshots(List screenshotIds, String } List messageIDS = new ArrayList<>(); - List rawMessages = new ArrayList<>(); + List rawMessages = new ArrayList<>(); for (ActionResult screenshotId : screenshotIds) { logger.debug("Storing screenshot id {}", screenshotId); Path screenPath = Configuration.SCREENSHOTS_DIR_PATH.resolve(screenshotId.getData()); @@ -120,9 +124,9 @@ public List storeScreenshots(List screenshotIds, String logger.warn("Screenshot with id {} does not exists", screenshotId); continue; } - RawMessage rawMessage = messageStoreBuilder.buildMessageFromFile(screenPath, Direction.FIRST, sessionAlias, sessionGroup); + T rawMessage = messageStoreBuilder.buildMessageFromFile(screenPath, Direction.FIRST, sessionAlias, sessionGroup); if (rawMessage != null) { - messageIDS.add(rawMessage.getMetadata().getId()); + messageIDS.add(messageIdExtractor.getId(rawMessage)); rawMessages.add(rawMessage); } removeScreenshot(screenPath); @@ -135,9 +139,9 @@ public List storeScreenshots(List screenshotIds, String public MessageID onResponse(RhScriptResult response, String sessionId, String rhSessionId) { RhResponseMessageBody body = RhResponseMessageBody.fromRhScriptResult(response).setRhSessionId(rhSessionId); try { - RawMessage message = messageStoreBuilder.buildMessage(body.getFields(), Direction.FIRST, sessionId, sessionGroup); + T message = messageStoreBuilder.buildMessage(body.getFields(), Direction.FIRST, sessionId, sessionGroup); messageStoreSender.sendMessages(message); - return message.getMetadata().getId(); + return messageIdExtractor.getId(message); } catch (Exception e) { logger.error("Cannot send message to message-storage", e); } @@ -183,4 +187,9 @@ private List> processList(List list) { public void close() throws Exception { this.messageStoreSender.close(); } + + @FunctionalInterface + public interface MessageIdExtractor { + MessageID getId(T message); + } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java index f038fa1..6b542e6 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,121 +16,12 @@ package com.exactpro.th2.hand.services.mstore; -import com.exactpro.th2.common.grpc.AnyMessage; -import com.exactpro.th2.common.grpc.MessageGroup; -import com.exactpro.th2.common.grpc.MessageGroupBatch; -import com.exactpro.th2.common.grpc.RawMessage; -import com.exactpro.th2.common.schema.factory.CommonFactory; -import com.exactpro.th2.common.schema.message.MessageRouter; -import com.exactpro.th2.hand.schema.CustomConfiguration; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Collection; -import java.util.Collections; - -public class MessageStoreSender implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(MessageStoreSender.class); - - public static final String RAW_MESSAGE_ATTRIBUTE = "raw"; - - private final MessageRouter messageRouterGroupBatch; - private final long batchLimit; - - - public MessageStoreSender(CommonFactory factory) { - messageRouterGroupBatch = factory.getMessageRouterMessageGroupBatch(); - CustomConfiguration customConfiguration = factory.getCustomConfiguration(CustomConfiguration.class); - this.batchLimit = customConfiguration.getMessageBatchLimit(); - writeToLogAboutConnection(factory); - } - - public void sendMessages(RawMessage messages) { - sendMessages(Collections.singleton(messages)); - } - - public void sendMessages(Collection messages) { - try { - sendRawMessages(messages); - } catch (Exception e) { - logger.error("Cannot store to mstore", e); - } - } - - @Override - public void close() throws Exception { - messageRouterGroupBatch.close(); - } - - - private void sendRawMessages(Collection messages) throws Exception { - MessageGroupBatch.Builder currentBatchBuilder = MessageGroupBatch.newBuilder(); - long currentBatchLength = 0; - long totalLength = 0; - int count = 0; - int batchesCount = 0; - for (RawMessage message : messages) { - if (message == null) - continue; - - long size = this.calculateSize(message); - - MessageGroup.Builder mgBuilder = MessageGroup.newBuilder() - .addMessages(AnyMessage.newBuilder().setRawMessage(message)); - //if batchlimit has incorrect value, sender should pack each message to batch - //if one message is bigger that batchLimit it is should send to mstore anyway and reject by it - if (currentBatchLength + size > batchLimit && currentBatchLength != 0) { - this.messageRouterGroupBatch.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); - currentBatchBuilder = MessageGroupBatch.newBuilder(); - currentBatchLength = 0; - batchesCount++; - } - - currentBatchBuilder.addGroups(mgBuilder); - currentBatchLength += size; - totalLength += size; - count++; - } - - if (currentBatchLength != 0) { - this.messageRouterGroupBatch.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); - batchesCount++; - } - if (count == 0) { - logger.debug("There are no valid messages to send"); - return; - } - - logger.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", - count, batchesCount, totalLength); - } +public interface MessageStoreSender extends AutoCloseable { + String RAW_MESSAGE_ATTRIBUTE = "raw"; - private long calculateSize(RawMessage message) { - return message.getBody().size(); - } + void sendMessages(T messages); - // TODO: redundant method, because common already prints data into log - private void writeToLogAboutConnection(CommonFactory factory) { - if (!logger.isInfoEnabled()) - return; - StringBuilder connectionInfo = new StringBuilder("Connection to RabbitMQ with "); - connectionInfo.append(factory.getRabbitMqConfiguration()).append(" is established \n"); - connectionInfo.append("Queues: \n"); - factory.getMessageRouterConfiguration().getQueues().forEach((name, queue) -> { - connectionInfo.append(name).append(" : "); - try { - ObjectMapper mapper = new ObjectMapper(); - connectionInfo.append(mapper.writeValueAsString(queue)); - } - catch (JsonProcessingException e) { - logger.warn("Error occurs while convert QueueConfiguration to JSON string", e); - connectionInfo.append("QueueConfiguration is not available"); - } - connectionInfo.append('\n'); - }); - logger.info(connectionInfo.toString()); - } + void sendMessages(Collection messages); } diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java new file mode 100644 index 0000000..b3d114d --- /dev/null +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java @@ -0,0 +1,108 @@ +/* + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) + * + * 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.exactpro.th2.hand.services.mstore; + +import com.exactpro.th2.common.grpc.AnyMessage; +import com.exactpro.th2.common.grpc.MessageGroup; +import com.exactpro.th2.common.grpc.MessageGroupBatch; +import com.exactpro.th2.common.grpc.RawMessage; +import com.exactpro.th2.common.schema.factory.CommonFactory; +import com.exactpro.th2.common.schema.message.MessageRouter; +import com.exactpro.th2.hand.schema.CustomConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; + +public class ProtobufMessageStoreSender implements MessageStoreSender { + private static final Logger logger = LoggerFactory.getLogger(ProtobufMessageStoreSender.class); + private final MessageRouter messageRouterGroupBatch; + private final long batchLimit; + + + public ProtobufMessageStoreSender(CommonFactory factory) { + messageRouterGroupBatch = factory.getMessageRouterMessageGroupBatch(); + CustomConfiguration customConfiguration = factory.getCustomConfiguration(CustomConfiguration.class); + this.batchLimit = customConfiguration.getMessageBatchLimit(); + } + + public void sendMessages(RawMessage messages) { + sendMessages(Collections.singleton(messages)); + } + + public void sendMessages(Collection messages) { + try { + sendRawMessages(messages); + } catch (Exception e) { + logger.error("Cannot store to mstore", e); + } + } + + @Override + public void close() throws Exception { + messageRouterGroupBatch.close(); + } + + + private void sendRawMessages(Collection messages) throws Exception { + MessageGroupBatch.Builder currentBatchBuilder = MessageGroupBatch.newBuilder(); + long currentBatchLength = 0; + long totalLength = 0; + int count = 0; + int batchesCount = 0; + for (RawMessage message : messages) { + if (message == null) + continue; + + long size = this.calculateSize(message); + + MessageGroup.Builder mgBuilder = MessageGroup.newBuilder() + .addMessages(AnyMessage.newBuilder().setRawMessage(message)); + //if batch limit has incorrect value, sender should pack each message to batch + //if one message is bigger that batchLimit it is should send to mstore anyway and reject by it + if (currentBatchLength + size > batchLimit && currentBatchLength != 0) { + this.messageRouterGroupBatch.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); + currentBatchBuilder = MessageGroupBatch.newBuilder(); + currentBatchLength = 0; + batchesCount++; + } + + currentBatchBuilder.addGroups(mgBuilder); + currentBatchLength += size; + totalLength += size; + count++; + } + + if (currentBatchLength != 0) { + this.messageRouterGroupBatch.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); + batchesCount++; + } + + if (count == 0) { + logger.debug("There are no valid messages to send"); + return; + } + + logger.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", + count, batchesCount, totalLength); + } + + private long calculateSize(RawMessage message) { + return message.getBody().size(); + } +} diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java new file mode 100644 index 0000000..0a4a64e --- /dev/null +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java @@ -0,0 +1,111 @@ +/* + * Copyright 2024 Exactpro (Exactpro Systems Limited) + * + * 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.exactpro.th2.hand.services.mstore; + +import com.exactpro.th2.common.schema.factory.CommonFactory; +import com.exactpro.th2.common.schema.message.MessageRouter; +import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.GroupBatch; +import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.RawMessage; +import com.exactpro.th2.common.utils.message.transport.MessageUtilsKt; +import com.exactpro.th2.hand.schema.CustomConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; + +public class TransportMessageStoreSender implements MessageStoreSender { + private static final Logger logger = LoggerFactory.getLogger(TransportMessageStoreSender.class); + private final MessageRouter messageRouter; + private final long batchLimit; + private final String book; + private final String sessionGroup; + + + public TransportMessageStoreSender(CommonFactory factory) { + messageRouter = factory.getTransportGroupBatchRouter(); + CustomConfiguration customConfiguration = factory.getCustomConfiguration(CustomConfiguration.class); + this.batchLimit = customConfiguration.getMessageBatchLimit(); + this.book = factory.getBoxConfiguration().getBookName(); + this.sessionGroup = customConfiguration.getSessionGroup(); + } + + public void sendMessages(RawMessage messages) { + sendMessages(Collections.singleton(messages)); + } + + public void sendMessages(Collection messages) { + try { + sendRawMessages(messages); + } catch (Exception e) { + logger.error("Cannot store to mstore", e); + } + } + + @Override + public void close() throws Exception { + messageRouter.close(); + } + + + private void sendRawMessages(Collection messages) throws Exception { + GroupBatch.Builder currentBatchBuilder = createBatchBuilder(); + long currentBatchLength = 0; + long totalLength = 0; + int count = 0; + int batchesCount = 0; + for (RawMessage message : messages) { + if (message == null) + continue; + + long size = message.getBody().readableBytes(); + + //if batch limit has incorrect value, sender should pack each message to batch + //if one message is bigger that batchLimit it is should send to mstore anyway and reject by it + if (currentBatchLength + size > batchLimit && currentBatchLength != 0) { + this.messageRouter.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); + currentBatchBuilder = createBatchBuilder(); + currentBatchLength = 0; + batchesCount++; + } + + currentBatchBuilder.addGroup(MessageUtilsKt.toGroup(message)); + currentBatchLength += size; + totalLength += size; + count++; + } + + if (currentBatchLength != 0) { + this.messageRouter.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); + batchesCount++; + } + + if (count == 0) { + logger.debug("There are no valid messages to send"); + return; + } + + logger.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", + count, batchesCount, totalLength); + } + + private GroupBatch.Builder createBatchBuilder() { + return GroupBatch.builder() + .setBook(book) + .setSessionGroup(sessionGroup); + } +} From 848389815a2b8c20922c02d3f3f09f4d61a908e2 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Fri, 2 Feb 2024 15:54:25 +0400 Subject: [PATCH 03/12] [RM-84612] publish hand as lib * updated github workflow --- .github/workflows/build-dev-release.yml | 18 +++++ .github/workflows/build-release.yml | 18 +++++ .github/workflows/build-sanpshot.yml | 23 ++++++ .github/workflows/dev-docker-publish.yml | 54 ------------- .github/workflows/docker-publish.yml | 39 ---------- Dockerfile | 4 +- build.gradle | 96 ++++++++++++++++++++++++ gradle.properties | 4 +- 8 files changed, 160 insertions(+), 96 deletions(-) create mode 100644 .github/workflows/build-dev-release.yml create mode 100644 .github/workflows/build-release.yml create mode 100644 .github/workflows/build-sanpshot.yml delete mode 100644 .github/workflows/dev-docker-publish.yml delete mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/build-dev-release.yml b/.github/workflows/build-dev-release.yml new file mode 100644 index 0000000..86f34c1 --- /dev/null +++ b/.github/workflows/build-dev-release.yml @@ -0,0 +1,18 @@ +name: Build and publish dev release jar to sonatype repository + +on: workflow_dispatch + +jobs: + build: + uses: th2-net/.github/.github/workflows/compound-java.yml@main + with: + build-target: 'Sonatype,Docker' + devRelease: true + createTag: true + docker-username: ${{ github.actor }} + secrets: + docker-password: ${{ secrets.GITHUB_TOKEN }} + sonatypeUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} + sonatypePassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} + sonatypeSigningKey: ${{ secrets.SONATYPE_GPG_ARMORED_KEY }} + sonatypeSigningPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..a72a337 --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,18 @@ +name: Build and publish release jar to sonatype repository + +on: workflow_dispatch + +jobs: + build: + uses: th2-net/.github/.github/workflows/compound-java.yml@main + with: + build-target: 'Sonatype,Docker' + devRelease: false + createTag: true + docker-username: ${{ github.actor }} + secrets: + docker-password: ${{ secrets.GITHUB_TOKEN }} + sonatypeUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} + sonatypePassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} + sonatypeSigningKey: ${{ secrets.SONATYPE_GPG_ARMORED_KEY }} + sonatypeSigningPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/build-sanpshot.yml b/.github/workflows/build-sanpshot.yml new file mode 100644 index 0000000..46c237d --- /dev/null +++ b/.github/workflows/build-sanpshot.yml @@ -0,0 +1,23 @@ +name: Build and publish jar to sonatype snapshot repository + +on: + push: + branches-ignore: + - master + - version-* + - dependabot* + paths-ignore: + - README.md + +jobs: + build-job: + uses: th2-net/.github/.github/workflows/compound-java-dev.yml@main + with: + build-target: 'Sonatype,Docker' + docker-username: ${{ github.actor }} + secrets: + docker-password: ${{ secrets.GITHUB_TOKEN }} + sonatypeUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} + sonatypePassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} + sonatypeSigningKey: ${{ secrets.SONATYPE_GPG_ARMORED_KEY }} + sonatypeSigningPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/dev-docker-publish.yml b/.github/workflows/dev-docker-publish.yml deleted file mode 100644 index 10a95ab..0000000 --- a/.github/workflows/dev-docker-publish.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Dev build and publish Docker distributions to Github Container Registry ghcr.io - -on: - push: - branches-ignore: - - master - - version-* - - dependabot** - paths-ignore: - - README.md - - gradle.properties - -jobs: - build: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - # Prepare custom build version - - name: Get branch name - id: branch - run: echo ::set-output name=branch_name::${GITHUB_REF#refs/*/} - - name: Get release_version - id: ver - uses: christian-draeger/read-properties@1.1.1 - with: - path: gradle.properties - properties: release_version - - name: Get SHA of the commit - id: sha - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Build custom release version - id: release_ver - run: echo value="${{ steps.ver.outputs.release_version }}-${{ steps.branch.outputs.branch_name }}-${{ github.run_id }}-${{ steps.sha.outputs.sha_short }}" >> $GITHUB_OUTPUT - - name: Show custom release version - run: echo ${{ steps.release_ver.outputs.value }} - # Build and publish image - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - run: echo "::set-output name=REPOSITORY_NAME::$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" - id: meta - - name: Build and push - id: docker_build - uses: docker/build-push-action@v3 - with: - push: true - tags: ghcr.io/${{ github.repository }}:${{ steps.release_ver.outputs.value }} - labels: com.exactpro.th2.${{ steps.meta.outputs.REPOSITORY_NAME }}=${{ steps.ver.outputs.value }} - build-args: | - release_version=${{ steps.read_property.outputs.value }} \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml deleted file mode 100644 index 3d8706e..0000000 --- a/.github/workflows/docker-publish.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Build and publish Docker distributions to Github Container Registry ghcr.io - -on: - push: - branches: - - master - - version-* - paths: - - gradle.properties - -jobs: - build: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_OUTPUT - id: meta - - name: Read version from gradle.properties - id: read_property - uses: christian-draeger/read-properties@1.1.1 - with: - path: ./gradle.properties - properties: release_version - - name: Build and push - id: docker_build - uses: docker/build-push-action@v3 - with: - push: true - tags: ghcr.io/${{ github.repository }}:${{ steps.read_property.outputs.release_version }} - labels: com.exactpro.th2.${{ steps.meta.outputs.REPOSITORY_NAME }}=${{ steps.read_property.outputs.release_version }} - build-args: | - release_version=${{ steps.read_property.outputs.value }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4d80c26..e5ac521 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,9 +14,9 @@ RUN apk add --no-cache make git gcc musl-dev swig \ -I../src -L../src libwebp_java_wrap.c -lwebp -o libwebp.so FROM gradle:7.6-jdk11 AS build -ARG app_version=0.0.0 +ARG release_version=0.0.0 COPY ./ . -RUN gradle dockerPrepare -Prelease_version=${app_version} +RUN gradle dockerPrepare -Prelease_version=${release_version} FROM adoptopenjdk/openjdk11:alpine WORKDIR /home diff --git a/build.gradle b/build.gradle index 53cc56e..e5df93f 100644 --- a/build.gradle +++ b/build.gradle @@ -21,11 +21,15 @@ plugins { id 'java' id 'java-library' id 'application' + id 'maven-publish' + id "io.github.gradle-nexus.publish-plugin" version "1.3.0" id 'com.palantir.docker' version '0.34.0' id "org.owasp.dependencycheck" version "9.0.9" id "com.gorylenko.gradle-git-properties" version "2.4.1" id 'com.github.jk1.dependency-license-report' version '2.5' id "de.undercouch.download" version "5.4.0" + id 'signing' + id "com.google.protobuf" version "0.9.3" } group 'com.exactpro.th2' @@ -77,6 +81,98 @@ dependencies { implementation "org.apache.commons:commons-csv:1.9.0" } +java { + withJavadocJar() + withSourcesJar() +} + +// conditionals for publications +tasks.withType(PublishToMavenRepository).configureEach { + onlyIf { + (repository == publishing.repositories.nexusRepository && + project.hasProperty('nexus_user') && + project.hasProperty('nexus_password') && + project.hasProperty('nexus_url')) || + (repository == publishing.repositories.sonatype && + project.hasProperty('sonatypeUsername') && + project.hasProperty('sonatypePassword')) + } +} +tasks.withType(Sign).configureEach { + onlyIf { + project.hasProperty('signingKey') && + project.hasProperty('signingPassword') + } +} +// disable running task 'initializeSonatypeStagingRepository' on a gitlab +tasks.configureEach { task -> + if (task.name == 'initializeSonatypeStagingRepository' && + !(project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) + ) { + task.enabled = false + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from(components.java) + pom { + name = rootProject.name + packaging = 'jar' + description = rootProject.description + url = vcs_url + scm { + url = vcs_url + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = 'developer' + name = 'developer' + email = 'developer@exactpro.com' + } + } + scm { + url = vcs_url + } + } + } + } + repositories { + //Nexus repo to publish from gitlab + maven { + name = 'nexusRepository' + credentials { + username = project.findProperty('nexus_user') + password = project.findProperty('nexus_password') + } + url = project.findProperty('nexus_url') + } + } +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + } + } +} + +signing { + String signingKey = findProperty("signingKey") + String signingPassword = findProperty("signingPassword") + useInMemoryPgpKeys(signingKey, signingPassword) + sign publishing.publications.mavenJava +} + applicationName = 'service' application { diff --git a/gradle.properties b/gradle.properties index bb05d67..c8ad482 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,4 @@ release_version = 5.0.0 -docker_image_name = th2-hand \ No newline at end of file +docker_image_name = th2-hand + +vcs_url=https://github.com/th2-net/th2-hand \ No newline at end of file From dba6a04d4b151bc582f48fba32c162233ba4df26 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Thu, 8 Feb 2024 13:36:57 +0400 Subject: [PATCH 04/12] [RM-84612] added DefaultEventBuilderTest --- build.gradle | 16 ++ .../events/DefaultEventBuilderTest.kt | 144 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt diff --git a/build.gradle b/build.gradle index e5df93f..7879e11 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ plugins { id 'java-library' id 'application' id 'maven-publish' + id 'org.jetbrains.kotlin.jvm' version '1.8.22' id "io.github.gradle-nexus.publish-plugin" version "1.3.0" id 'com.palantir.docker' version '0.34.0' id "org.owasp.dependencycheck" version "9.0.9" @@ -79,6 +80,12 @@ dependencies { implementation 'org.apache.commons:commons-lang3' implementation "org.apache.commons:commons-csv:1.9.0" + + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' + testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1' + testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' + testImplementation 'io.strikt:strikt-core:0.34.1' + } java { @@ -86,6 +93,15 @@ java { withSourcesJar() } +test { + useJUnitPlatform() +} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + kotlinOptions.jvmTarget = "11" + kotlinOptions.freeCompilerArgs += "-Xjvm-default=all" +} + // conditionals for publications tasks.withType(PublishToMavenRepository).configureEach { onlyIf { diff --git a/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt b/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt new file mode 100644 index 0000000..d4caaa0 --- /dev/null +++ b/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2024 Exactpro (Exactpro Systems Limited) + * + * 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.exactpro.th2.hand.builders.events + +import com.exactpro.remotehand.rhdata.RhResponseCode +import com.exactpro.remotehand.rhdata.RhScriptResult +import com.exactpro.th2.act.grpc.hand.RhActionsBatch +import com.exactpro.th2.act.grpc.hand.RhBatchResponse +import com.exactpro.th2.common.grpc.EventID +import com.exactpro.th2.common.grpc.EventStatus +import com.exactpro.th2.common.grpc.MessageID +import com.exactpro.th2.common.schema.factory.CommonFactory +import com.exactpro.th2.common.utils.message.toTimestamp +import com.exactpro.th2.hand.messages.responseexecutor.ActionsBatchExecutorResponse +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import strikt.api.expectThat +import strikt.assertions.isEqualTo +import strikt.assertions.isNotBlank +import strikt.assertions.isSameInstanceAs +import strikt.assertions.isTrue +import java.time.Instant + +internal class DefaultEventBuilderTest { + + private val factory = mock { + on { newEventIDBuilder() }.thenAnswer { + EventID.newBuilder().setBookName(BOOK).setScope(SCOPE) + } + } + private val builder = DefaultEventBuilder(factory) + + @AfterEach + fun afterEach() { + verifyNoMoreInteractions(factory) + } + + @Test + fun `build event test`() { + val now = Instant.now() + val rhActionsBatch = RhActionsBatch.newBuilder().apply { + eventName = "test-event-name" + parentEventIdBuilder.setBookName("$BOOK-1").setScope("$SCOPE-1") + additionalEventInfoBuilder + .setDescription("test-description") + .setPrintTable(true) + .addKeys("test-key") + .addValues("test-value") + .setRequestParamsTableTitle("test-title") + storeActionMessages = true + }.build() + val rhBatchResponse = RhBatchResponse.newBuilder().apply { + scriptStatus = RhBatchResponse.ScriptExecutionStatus.EXECUTION_ERROR + sessionId = "test-session-id" + addResultBuilder() + .setActionId("test-action-id") + .setResult("test-result") + }.build() + val scriptResult = RhScriptResult().apply { + code = RhResponseCode.TOOL_BUSY.code + errorMessage = "test-error-message" + } + val messageIDs = listOf( + MessageID.newBuilder().setBookName("$BOOK-2").build() + ) + val actionsBatchExecutorResponse = ActionsBatchExecutorResponse( + rhBatchResponse, + scriptResult, + messageIDs + ) + + val event = builder.buildEvent(now, rhActionsBatch, actionsBatchExecutorResponse) + verify(factory).newEventIDBuilder() + + expectThat(event) { + get { id }.and { + get { id }.isNotBlank() + get { startTimestamp }.isEqualTo(now.toTimestamp()) + get { scope }.isSameInstanceAs(rhActionsBatch.parentEventId.scope) + get { bookName }.isSameInstanceAs(BOOK) + } + get { name }.isSameInstanceAs(rhActionsBatch.eventName) + get { parentId }.isSameInstanceAs(rhActionsBatch.parentEventId) + get { status }.isEqualTo(EventStatus.FAILED) + get { attachedMessageIdsList }.isEqualTo(messageIDs) + get { hasEndTimestamp() }.isTrue() + @Suppress("SpellCheckingInspection") + get { body.toStringUtf8() }.isEqualTo(""" + |[ + |{"data":"Description: \ntest-description","type":"message"}, + |{"data":"test-title","type":"message"}, + |{"rows": + |[ + |{ + |"Name":"test-key", + |"Value":"test-value" + |} + |], + |"type":"table" + |}, + |{"data":"Result","type":"message"}, + |{"rows": + |[ + |{"Name":"Action status","Value":"EXECUTION_ERROR"}, + |{"Name":"Errors","Value":"test-error-message"}, + |{"Name":"SessionId","Value":"test-session-id"} + |], + |"type":"table" + |}, + |{"data":"Action messages","type":"message"}, + |{ + |"rows": + |[ + |{"Name":"test-action-id","Value":"test-result"} + |], + |"type":"table" + |} + |] + """.trimMargin().replace("\n", "")) + } + } + + companion object { + const val BOOK = "test-book" + const val SCOPE = "test-scope" + } +} \ No newline at end of file From 5378d31a9f9e089aa04f8a270d92ec645949cfa7 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Thu, 8 Feb 2024 16:18:26 +0400 Subject: [PATCH 05/12] [RM-84612] migrated DefaultEventBuilder to common Event builder --- .../com/exactpro/th2/hand/HandServer.java | 13 +- .../builders/events/DefaultEventBuilder.java | 132 +++++++++-------- .../hand/builders/events/EventBuilder.java | 7 +- .../builders/events/EventPayloadBuilder.java | 74 ---------- .../ActionsBatchExecutor.java | 7 +- .../requestexecutors/RequestExecutor.java | 6 +- .../th2/hand/services/HandBaseService.java | 20 ++- .../th2/hand/services/MessageHandler.java | 3 +- .../th2/hand/builders/events/TableRow.kt | 27 ++++ .../events/DefaultEventBuilderTest.kt | 136 +++++++++++++++--- 10 files changed, 250 insertions(+), 175 deletions(-) delete mode 100644 src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java create mode 100644 src/main/kotlin/com/exactpro/th2/hand/builders/events/TableRow.kt diff --git a/src/main/java/com/exactpro/th2/hand/HandServer.java b/src/main/java/com/exactpro/th2/hand/HandServer.java index 8b51d64..db8be62 100644 --- a/src/main/java/com/exactpro/th2/hand/HandServer.java +++ b/src/main/java/com/exactpro/th2/hand/HandServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,10 @@ protected Server buildServer() throws Exception { return config.getFactory().getGrpcRouter().startServer(services.toArray(new IHandService[0])); } - /** Start serving requests. */ + /** + * Start serving requests. + * @throws IOException - if unable to bind + */ public void start() throws IOException { // FIXME: close resource new Thread(SessionWatcher.getWatcher()).start(); @@ -72,7 +75,10 @@ public void start() throws IOException { })); } - /** Stop serving requests and shutdown resources. */ + /** + * Stop serving requests and shutdown resources. + * @throws InterruptedException - if server can't be shutdown + */ public void stop() throws InterruptedException { if (server != null) { server.shutdown().awaitTermination(30, TimeUnit.SECONDS); @@ -82,6 +88,7 @@ public void stop() throws InterruptedException { /** * Await termination on the main thread since the grpc library uses daemon * threads. + * @throws InterruptedException - if current thread is interrupted */ public void blockUntilShutdown() throws InterruptedException { if (server != null) { diff --git a/src/main/java/com/exactpro/th2/hand/builders/events/DefaultEventBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/events/DefaultEventBuilder.java index 613a047..4060c7d 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/events/DefaultEventBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/events/DefaultEventBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,104 +20,116 @@ import com.exactpro.th2.act.grpc.hand.ResultDetails; import com.exactpro.th2.act.grpc.hand.RhActionsBatch; import com.exactpro.th2.act.grpc.hand.RhBatchResponse; -import com.exactpro.th2.common.grpc.Event; -import com.exactpro.th2.common.grpc.EventID; -import com.exactpro.th2.common.grpc.EventStatus; +import com.exactpro.th2.common.event.bean.IRow; +import com.exactpro.th2.common.event.bean.Message; +import com.exactpro.th2.common.event.bean.Table; +import com.exactpro.th2.common.event.Event; +import com.exactpro.th2.common.schema.box.configuration.BoxConfiguration; import com.exactpro.th2.common.schema.factory.CommonFactory; import com.exactpro.th2.hand.messages.responseexecutor.ActionsBatchExecutorResponse; -import org.apache.commons.lang3.StringUtils; +import java.io.IOException; import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.LinkedHashMap; +import java.util.ArrayList; import java.util.Iterator; -import java.util.UUID; +import java.util.List; import java.util.stream.Collectors; -import static com.exactpro.th2.hand.utils.Utils.getTimestamp; +import static com.exactpro.th2.common.event.Event.Status.FAILED; +import static com.exactpro.th2.common.event.Event.Status.PASSED; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; -public final class DefaultEventBuilder implements EventBuilder { +public final class DefaultEventBuilder implements EventBuilder { + public static final Message ACTION_MESSAGES_EVENT_MESSAGE = createMessage("Action messages"); + public static final Message RESULT_EVENT_MESSAGE = createMessage("Result"); - private final CommonFactory factory; + private final String book; + private final String scope; public DefaultEventBuilder(CommonFactory factory) { - this.factory = factory; + BoxConfiguration boxConfiguration = factory.getBoxConfiguration(); + this.book = boxConfiguration.getBookName(); + this.scope = boxConfiguration.getBoxName(); } @Override - public Event buildEvent(RhActionsBatch request, ActionsBatchExecutorResponse executorResponse) { - return buildEvent(Instant.now(), request, executorResponse); - } + public com.exactpro.th2.common.grpc.Event buildEvent(Instant startTime, RhActionsBatch request, ActionsBatchExecutorResponse executorResponse) throws IOException { + RhScriptResult scriptResult = executorResponse.getScriptResult(); + RhBatchResponse response = executorResponse.getHandResponse(); - @Override - public Event buildEvent(Instant startTime, RhActionsBatch request, ActionsBatchExecutorResponse executorResponse) { - EventID.Builder eventId = factory.newEventIDBuilder() - .setId(UUID.randomUUID().toString()) - .setStartTimestamp(getTimestamp(startTime)); + Event builder = Event.from(startTime) + .name(request.getEventName()) + .status(scriptResult.isSuccess() ? PASSED : FAILED); + + createAdditionalEventInfo(builder, request.getAdditionalEventInfo()); + createResultPayload(builder, scriptResult, response.getSessionId(), response.getScriptStatus()); + createActionMessagesPayload(builder, request.getStoreActionMessages(), response.getResultList()); - Event.Builder eventBuilder = Event.newBuilder().setName(request.getEventName()); + executorResponse.getMessageIds().forEach(builder::messageID); if (request.hasParentEventId()) { - eventId.setScope(request.getParentEventId().getScope()); - eventBuilder.setParentId(request.getParentEventId()); + return builder.toProto(request.getParentEventId()); + } else { + return builder.toProto(book, scope); } - - eventBuilder.setId(eventId); - - RhScriptResult scriptResult = executorResponse.getScriptResult(); - RhBatchResponse response = executorResponse.getHandResponse(); - eventBuilder.setStatus(scriptResult.isSuccess() ? EventStatus.SUCCESS : EventStatus.FAILED); - - EventPayloadBuilder payloadBuilder = new EventPayloadBuilder(); - createAdditionalEventInfo(payloadBuilder, request.getAdditionalEventInfo()); - createResultPayload(payloadBuilder, scriptResult, response.getSessionId(), response.getScriptStatus()); - createActionMessagesPayload(payloadBuilder, request.getStoreActionMessages(), response.getResultList()); - eventBuilder.setBody(payloadBuilder.toByteString()); - - eventBuilder.addAllAttachedMessageIds(executorResponse.getMessageIds()); - eventBuilder.setEndTimestamp(getTimestamp(Instant.now())); - - return eventBuilder.build(); } - private void createResultPayload(EventPayloadBuilder payloadBuilder, RhScriptResult scriptResult, String sessionId, - RhBatchResponse.ScriptExecutionStatus scriptStatus) { - Map responseMap = new LinkedHashMap<>(); - responseMap.put("Action status", scriptStatus.name()); - String errorMessage; - if (StringUtils.isNotEmpty(errorMessage = scriptResult.getErrorMessage())) - responseMap.put("Errors", errorMessage); - responseMap.put("SessionId", sessionId); + private void createResultPayload(com.exactpro.th2.common.event.Event builder, RhScriptResult scriptResult, String sessionId, + RhBatchResponse.ScriptExecutionStatus scriptStatus) { + List rows = new ArrayList<>(); + rows.add(new TableRow("Action status", scriptStatus.name())); - payloadBuilder.printTable("Result", responseMap); + String errorMessage = scriptResult.getErrorMessage(); + if (isNotEmpty(errorMessage)) { + rows.add(new TableRow("Errors", errorMessage)); + } + rows.add(new TableRow("SessionId", sessionId)); + + builder.bodyData(RESULT_EVENT_MESSAGE); + builder.bodyData(createTable(rows)); } - private void createActionMessagesPayload(EventPayloadBuilder payloadBuilder, boolean storeActionMessages, - List resultDetails) { + private void createActionMessagesPayload(com.exactpro.th2.common.event.Event builder, boolean storeActionMessages, + List resultDetails) { if (storeActionMessages && !resultDetails.isEmpty()) { - Map actionMessages = resultDetails.stream().collect( - Collectors.toMap(ResultDetails::getActionId, ResultDetails::getResult)); - payloadBuilder.printTable("Action messages", actionMessages); + List rows = resultDetails.stream() + .map(result -> new TableRow(result.getActionId(), result.getResult())) + .collect(Collectors.toList()); + builder.bodyData(ACTION_MESSAGES_EVENT_MESSAGE); + builder.bodyData(createTable(rows)); } } - private void createAdditionalEventInfo(EventPayloadBuilder payloadBuilder, RhActionsBatch.AdditionalEventInfo info) { + private void createAdditionalEventInfo(com.exactpro.th2.common.event.Event builder, RhActionsBatch.AdditionalEventInfo info) { String description = info.getDescription(); if (!description.isEmpty()) { - payloadBuilder.printText("Description: \n" + description); + builder.bodyData(createMessage("Description: \n" + description)); } if (info.getPrintTable()) { - Map table = new LinkedHashMap<>(info.getKeysCount()); + List rows = new ArrayList<>(); Iterator keys = info.getKeysList().iterator(); Iterator values = info.getValuesList().iterator(); while (keys.hasNext() && values.hasNext()) { - table.put(keys.next(), values.next()); + rows.add(new TableRow(keys.next(), values.next())); } - payloadBuilder.printTable(info.getRequestParamsTableTitle(), table); + builder.bodyData(createMessage(info.getRequestParamsTableTitle())); + builder.bodyData(createTable(rows)); } } + + private static Message createMessage(String text) { + Message message = new Message(); + message.setData(text); + return message; + } + + private static Table createTable(List rows) { + Table table = new Table(); + table.setFields(rows); + return table; + } + } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/builders/events/EventBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/events/EventBuilder.java index f6685de..6a3c500 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/events/EventBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/events/EventBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,9 @@ import com.exactpro.th2.hand.messages.responseexecutor.BaseExecutorResponse; +import java.io.IOException; import java.time.Instant; public interface EventBuilder> { - T buildEvent(R request, E executorResponse); - - T buildEvent(Instant startTime, R request, E executorResponse); + T buildEvent(Instant startTime, R request, E executorResponse) throws IOException; } diff --git a/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java deleted file mode 100644 index f36d9e4..0000000 --- a/src/main/java/com/exactpro/th2/hand/builders/events/EventPayloadBuilder.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2021-2021 Exactpro (Exactpro Systems Limited) - * - * 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.exactpro.th2.hand.builders.events; - -import com.exactpro.th2.hand.messages.eventpayload.EventPayloadMessage; -import com.exactpro.th2.hand.messages.eventpayload.EventPayloadTable; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.protobuf.ByteString; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class EventPayloadBuilder { - - private static final Logger logger = LoggerFactory.getLogger(EventPayloadBuilder.class); - - private final List data; - - public EventPayloadBuilder() { - this.data = new ArrayList<>(); - } - - public EventPayloadBuilder printText(String text) { - this.data.add(new EventPayloadMessage(text)); - return this; - } - - public EventPayloadBuilder printText(String title, String text) { - this.data.add(new EventPayloadMessage(title)); - this.data.add(new EventPayloadMessage(text)); - return this; - } - - public EventPayloadBuilder printTable(String tableHeader, Map table) { - this.data.add(new EventPayloadMessage(tableHeader)); - this.data.add(new EventPayloadTable(table, false)); - return this; - } - - - public byte[] toByteArray() { - try { - //FIXME: use MAPPER from AbstractCommonFactory - return new ObjectMapper().writeValueAsBytes(this.data); - } catch (JsonProcessingException e) { - logger.error("Error while creating body", e); - return e.getMessage().getBytes(StandardCharsets.UTF_8); - } - } - - public ByteString toByteString() { - return ByteString.copyFrom(toByteArray()); - } - -} diff --git a/src/main/java/com/exactpro/th2/hand/requestexecutors/ActionsBatchExecutor.java b/src/main/java/com/exactpro/th2/hand/requestexecutors/ActionsBatchExecutor.java index 21ae671..de29d03 100644 --- a/src/main/java/com/exactpro/th2/hand/requestexecutors/ActionsBatchExecutor.java +++ b/src/main/java/com/exactpro/th2/hand/requestexecutors/ActionsBatchExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -60,7 +61,7 @@ public ActionsBatchExecutor(MessageHandler messageHandler) { @Override - public ActionsBatchExecutorResponse execute(RhActionsBatch request) { + public ActionsBatchExecutorResponse execute(RhActionsBatch request) throws IOException { Instant executionStartTime = Instant.now(); RhScriptResult scriptResult; String sessionId = "th2_hand"; @@ -140,7 +141,7 @@ private List parseResultDetails(List actionData) { return details; } - private void buildAndSendEvent(Instant startTime, RhActionsBatch request, ActionsBatchExecutorResponse executorResponse) { + private void buildAndSendEvent(Instant startTime, RhActionsBatch request, ActionsBatchExecutorResponse executorResponse) throws IOException { EventStoreHandler eventStoreHandler = messageHandler.getEventStoreHandler(); Event event = eventStoreHandler.getEventBuilder().buildEvent(startTime, request, executorResponse); eventStoreHandler.getEventStoreSender().storeEvent(event); diff --git a/src/main/java/com/exactpro/th2/hand/requestexecutors/RequestExecutor.java b/src/main/java/com/exactpro/th2/hand/requestexecutors/RequestExecutor.java index 5d08c1d..6f1d5e2 100644 --- a/src/main/java/com/exactpro/th2/hand/requestexecutors/RequestExecutor.java +++ b/src/main/java/com/exactpro/th2/hand/requestexecutors/RequestExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ import com.exactpro.th2.hand.messages.responseexecutor.BaseExecutorResponse; +import java.io.IOException; + public interface RequestExecutor> { - Res execute(Req request); + Res execute(Req request) throws IOException; } diff --git a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java index f644893..4c7b6b1 100644 --- a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java +++ b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,11 +24,14 @@ import com.exactpro.th2.hand.HandException; import com.exactpro.th2.hand.IHandService; import com.google.protobuf.Empty; -import com.google.protobuf.TextFormat; import io.grpc.stub.StreamObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; + +import static com.exactpro.th2.common.message.MessageUtils.toJson; + public class HandBaseService extends RhBatchImplBase implements IHandService { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -37,7 +40,7 @@ public class HandBaseService extends RhBatchImplBase implements IHandService { private MessageHandler messageHandler; @Override - public void init(MessageHandler messageHandler) throws Exception { + public void init(MessageHandler messageHandler) { this.messageHandler = messageHandler; } @@ -64,9 +67,14 @@ public void unregister(RhSessionID request, StreamObserver responseObserv @Override public void executeRhActionsBatch(RhActionsBatch request, StreamObserver responseObserver) { - logger.trace("Action: '{}', request: '{}'", "executeRhActionsBatch", TextFormat.shortDebugString(request)); - responseObserver.onNext(messageHandler.handleActionsBatchRequest(request)); - responseObserver.onCompleted(); + logger.trace("Action: '{}', request: '{}'", "executeRhActionsBatch", toJson(request)); + try { + responseObserver.onNext(messageHandler.handleActionsBatchRequest(request)); + responseObserver.onCompleted(); + } catch (IOException e) { + logger.error("Action: '{}', request: '{}'", "executeRhActionsBatch", toJson(request), e); + responseObserver.onError(e); + } } @Override diff --git a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java index c27e6a8..a5b5aba 100644 --- a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java @@ -33,6 +33,7 @@ import com.exactpro.th2.hand.services.mstore.ProtobufMessageStoreSender; import com.exactpro.th2.hand.services.mstore.TransportMessageStoreSender; +import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; public class MessageHandler implements AutoCloseable { @@ -86,7 +87,7 @@ public RhConnectionManager getRhConnectionManager() { return rhConnectionManager; } - public RhBatchResponse handleActionsBatchRequest(RhActionsBatch request) { + public RhBatchResponse handleActionsBatchRequest(RhActionsBatch request) throws IOException { ActionsBatchExecutor actionsBatchExecutor = new ActionsBatchExecutor(this); return actionsBatchExecutor.execute(request).getHandResponse(); } diff --git a/src/main/kotlin/com/exactpro/th2/hand/builders/events/TableRow.kt b/src/main/kotlin/com/exactpro/th2/hand/builders/events/TableRow.kt new file mode 100644 index 0000000..34ea018 --- /dev/null +++ b/src/main/kotlin/com/exactpro/th2/hand/builders/events/TableRow.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Exactpro (Exactpro Systems Limited) + * + * 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.exactpro.th2.hand.builders.events + +import com.exactpro.th2.common.event.bean.IRow +import com.fasterxml.jackson.annotation.JsonProperty + +class TableRow( + @JsonProperty("Name") + val name: String, + @JsonProperty("Value") + val value: String, +): IRow \ No newline at end of file diff --git a/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt b/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt index d4caaa0..e1c7769 100644 --- a/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt +++ b/src/test/kotlin/com/exactpro/th2/hand/builders/events/DefaultEventBuilderTest.kt @@ -23,20 +23,26 @@ import com.exactpro.th2.act.grpc.hand.RhBatchResponse import com.exactpro.th2.common.grpc.EventID import com.exactpro.th2.common.grpc.EventStatus import com.exactpro.th2.common.grpc.MessageID +import com.exactpro.th2.common.message.toJson +import com.exactpro.th2.common.schema.box.configuration.BoxConfiguration import com.exactpro.th2.common.schema.factory.CommonFactory import com.exactpro.th2.common.utils.message.toTimestamp import com.exactpro.th2.hand.messages.responseexecutor.ActionsBatchExecutorResponse import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions import strikt.api.expectThat +import strikt.api.expectThrows import strikt.assertions.isEqualTo import strikt.assertions.isNotBlank import strikt.assertions.isSameInstanceAs import strikt.assertions.isTrue import java.time.Instant +import kotlin.test.assertEquals internal class DefaultEventBuilderTest { @@ -44,9 +50,21 @@ internal class DefaultEventBuilderTest { on { newEventIDBuilder() }.thenAnswer { EventID.newBuilder().setBookName(BOOK).setScope(SCOPE) } + on { boxConfiguration }.thenReturn( + BoxConfiguration().apply { + bookName = BOOK + boxName = BOX_NAME + } + ) } private val builder = DefaultEventBuilder(factory) + @BeforeEach + fun beforeEach() { + verify(factory).boxConfiguration + clearInvocations(factory) + } + @AfterEach fun afterEach() { verifyNoMoreInteractions(factory) @@ -78,7 +96,12 @@ internal class DefaultEventBuilderTest { errorMessage = "test-error-message" } val messageIDs = listOf( - MessageID.newBuilder().setBookName("$BOOK-2").build() + MessageID.newBuilder().apply { + bookName = "$BOOK-1" + sequence = 1 + timestamp = Instant.now().toTimestamp() + connectionIdBuilder.setSessionAlias("test-session-alias") + }.build() ) val actionsBatchExecutorResponse = ActionsBatchExecutorResponse( rhBatchResponse, @@ -87,16 +110,15 @@ internal class DefaultEventBuilderTest { ) val event = builder.buildEvent(now, rhActionsBatch, actionsBatchExecutorResponse) - verify(factory).newEventIDBuilder() expectThat(event) { get { id }.and { get { id }.isNotBlank() get { startTimestamp }.isEqualTo(now.toTimestamp()) get { scope }.isSameInstanceAs(rhActionsBatch.parentEventId.scope) - get { bookName }.isSameInstanceAs(BOOK) + get { bookName }.isSameInstanceAs(rhActionsBatch.parentEventId.bookName) } - get { name }.isSameInstanceAs(rhActionsBatch.eventName) + get { name }.isEqualTo(rhActionsBatch.eventName) get { parentId }.isSameInstanceAs(rhActionsBatch.parentEventId) get { status }.isEqualTo(EventStatus.FAILED) get { attachedMessageIdsList }.isEqualTo(messageIDs) @@ -106,39 +128,109 @@ internal class DefaultEventBuilderTest { |[ |{"data":"Description: \ntest-description","type":"message"}, |{"data":"test-title","type":"message"}, - |{"rows": - |[ - |{ - |"Name":"test-key", - |"Value":"test-value" - |} - |], - |"type":"table" + |{ + |"type":"table", + |"rows": + |[ + |{ + |"Name":"test-key", + |"Value":"test-value" + |} + |] |}, |{"data":"Result","type":"message"}, - |{"rows": - |[ - |{"Name":"Action status","Value":"EXECUTION_ERROR"}, - |{"Name":"Errors","Value":"test-error-message"}, - |{"Name":"SessionId","Value":"test-session-id"} - |], - |"type":"table" + |{ + |"type":"table", + |"rows": + |[ + |{"Name":"Action status","Value":"EXECUTION_ERROR"}, + |{"Name":"Errors","Value":"test-error-message"}, + |{"Name":"SessionId","Value":"test-session-id"} + |] |}, |{"data":"Action messages","type":"message"}, |{ + |"type":"table", |"rows": |[ |{"Name":"test-action-id","Value":"test-result"} - |], - |"type":"table" + |] |} |] """.trimMargin().replace("\n", "")) } } + @Test + fun `build event when message book doesn't mismatch to default book test`() { + val now = Instant.now() + + val messageId = MessageID.newBuilder().apply { + bookName = "$BOOK-2" + sequence = 1 + timestamp = Instant.now().toTimestamp() + connectionIdBuilder.setSessionAlias("test-session-alias") + }.build() + val messageIDs = listOf(messageId) + + expectThrows { + builder.buildEvent( + now, + RhActionsBatch.getDefaultInstance(), + ActionsBatchExecutorResponse( + RhBatchResponse.getDefaultInstance(), + RhScriptResult(), + messageIDs + ) + ) + }.assert("Message") { + assertEquals( + """ + |Build event failure, book: '$BOOK', scope: '$SCOPE', + |name: 'Unknown event name', type: 'Unknown event type', + |problems: [Book name mismatch in '${messageId.toJson()}' message id] + """.trimMargin().replace("\n", ""), + it.message) + } + } + + @Test + fun `build event when message book doesn't mismatch to parent event book test`() { + val now = Instant.now() + + val messageId = MessageID.newBuilder().apply { + bookName = "$BOOK-2" + sequence = 1 + timestamp = Instant.now().toTimestamp() + connectionIdBuilder.setSessionAlias("test-session-alias") + }.build() + val eventId = EventID.newBuilder().setBookName("$BOOK-1").setScope("$SCOPE-1").build() + val messageIDs = listOf(messageId) + + expectThrows { + builder.buildEvent( + now, + RhActionsBatch.newBuilder().setParentEventId(eventId).build(), + ActionsBatchExecutorResponse( + RhBatchResponse.getDefaultInstance(), + RhScriptResult(), + messageIDs + ) + ) + }.assert("Message") { + assertEquals( + """ + |Build event failure, book: '${eventId.bookName}', scope: '${eventId.scope}', + |name: 'Unknown event name', type: 'Unknown event type', + |problems: [Book name mismatch in '${messageId.toJson()}' message id] + """.trimMargin().replace("\n", ""), + it.message) + } + } + companion object { + const val BOX_NAME = "test-box-name" const val BOOK = "test-book" - const val SCOPE = "test-scope" + const val SCOPE = BOX_NAME } } \ No newline at end of file From fa53aaf6428a9e2f66a630ba347ed4ed66b0a877 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Thu, 8 Feb 2024 16:52:24 +0400 Subject: [PATCH 06/12] [RM-84612] corrected after review --- .../com/exactpro/th2/hand/Application.java | 13 +++-- .../com/exactpro/th2/hand/HandServer.java | 50 +++++++++---------- .../builders/mstore/MessageStoreBuilder.java | 5 +- .../mstore/ProtobufMessageStoreBuilder.java | 8 +-- .../mstore/TransportMessageStoreBuilder.java | 9 ++-- .../th2/hand/services/HandBaseService.java | 28 ++++++----- .../th2/hand/services/MessageHandler.java | 4 +- .../services/estore/EventStoreHandler.java | 9 +--- .../services/estore/EventStoreSender.java | 9 +--- .../services/mstore/MessageStoreHandler.java | 23 ++++----- .../services/mstore/MessageStoreSender.java | 2 +- .../mstore/ProtobufMessageStoreSender.java | 14 ++---- .../mstore/TransportMessageStoreSender.java | 15 ++---- 13 files changed, 80 insertions(+), 109 deletions(-) diff --git a/src/main/java/com/exactpro/th2/hand/Application.java b/src/main/java/com/exactpro/th2/hand/Application.java index 8e9e5a2..6a0560d 100644 --- a/src/main/java/com/exactpro/th2/hand/Application.java +++ b/src/main/java/com/exactpro/th2/hand/Application.java @@ -42,18 +42,17 @@ public void run(String[] args) { Config config = getConfig(factory); final HandServer handServer = new HandServer(config, getCurrentTime()); Runtime.getRuntime().addShutdownHook(new Thread(() -> { + LOGGER.info("*** Closing hand server because JVM is shutting down"); try { - LOGGER.info("Disposing Hand server"); - handServer.dispose(); - } - catch (Exception e) { - LOGGER.error("Error while disposing Hand server", e); + handServer.close(); + } catch (InterruptedException e) { + LOGGER.warn("Server termination await was interrupted", e); } + LOGGER.info("*** hand server closed"); })); handServer.start(); handServer.blockUntilShutdown(); - } - catch (Exception e) { + } catch (Exception e) { LOGGER.error("Could not to start Hand server", e); closeApp(); } diff --git a/src/main/java/com/exactpro/th2/hand/HandServer.java b/src/main/java/com/exactpro/th2/hand/HandServer.java index db8be62..7001366 100644 --- a/src/main/java/com/exactpro/th2/hand/HandServer.java +++ b/src/main/java/com/exactpro/th2/hand/HandServer.java @@ -28,15 +28,16 @@ import java.util.ServiceLoader; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; -// FIXME: implements auto closable -public class HandServer { - private final Logger logger = LoggerFactory.getLogger(getClass()); +public class HandServer implements AutoCloseable { + private static final Logger LOGGER = LoggerFactory.getLogger(HandServer.class); private final Config config; private final MessageHandler messageHandler; private final Server server; private final List services; + private final AtomicReference watcher = new AtomicReference<>(); public HandServer(Config config, long startSequences) throws Exception { this.config = config; @@ -49,7 +50,7 @@ protected Server buildServer() throws Exception { for (IHandService rhService : ServiceLoader.load(IHandService.class)) { services.add(rhService); rhService.init(messageHandler); - logger.info("Service '{}' loaded", rhService.getClass().getName()); + LOGGER.info("Service '{}' loaded", rhService.getClass().getName()); } return config.getFactory().getGrpcRouter().startServer(services.toArray(new IHandService[0])); @@ -60,28 +61,13 @@ protected Server buildServer() throws Exception { * @throws IOException - if unable to bind */ public void start() throws IOException { - // FIXME: close resource - new Thread(SessionWatcher.getWatcher()).start(); - server.start(); - logger.info("Server started, listening on port {}", server.getPort()); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - logger.info("*** shutting down gRPC server because JVM is shutting down"); - try { - stop(); - } catch (InterruptedException e) { - logger.warn("Server termination await was interrupted", e); - } - logger.info("*** server shut down"); - })); - } - - /** - * Stop serving requests and shutdown resources. - * @throws InterruptedException - if server can't be shutdown - */ - public void stop() throws InterruptedException { - if (server != null) { - server.shutdown().awaitTermination(30, TimeUnit.SECONDS); + Thread thread = new Thread(SessionWatcher.getWatcher()); + if (watcher.compareAndSet(null, thread)) { + thread.start(); + server.start(); + LOGGER.info("Server started, listening on port {}", server.getPort()); + } else { + throw new IllegalStateException(getClass().getSimpleName() + " was started once"); } } @@ -96,9 +82,19 @@ public void blockUntilShutdown() throws InterruptedException { } } - public void dispose() { + @Override + public void close() throws InterruptedException { + if (!server.isShutdown()) { + server.shutdown().awaitTermination(30, TimeUnit.SECONDS); + } + for (IHandService service : this.services) { service.dispose(); } + + Thread thread = watcher.get(); + if (thread != null && !thread.isInterrupted()) { + thread.interrupt(); + } } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/builders/mstore/MessageStoreBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/mstore/MessageStoreBuilder.java index 9113bbe..4c0835f 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/mstore/MessageStoreBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/mstore/MessageStoreBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,14 @@ package com.exactpro.th2.hand.builders.mstore; import com.exactpro.th2.common.grpc.Direction; +import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.file.Path; import java.util.Map; public interface MessageStoreBuilder { + ObjectMapper MAPPER = new ObjectMapper(); + T buildMessage(Map fields, Direction direction, String sessionId, String sessionGroup); T buildMessage(byte[] bytes, Direction direction, String sessionId, String sessionGroup); diff --git a/src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java index da38b6c..1acc9bc 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/mstore/ProtobufMessageStoreBuilder.java @@ -39,7 +39,7 @@ import static com.exactpro.th2.hand.utils.Utils.getTimestamp; public final class ProtobufMessageStoreBuilder implements MessageStoreBuilder { - private static final Logger logger = LoggerFactory.getLogger(ProtobufMessageStoreBuilder.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufMessageStoreBuilder.class); private final AtomicLong seqNum; private final CommonFactory factory; @@ -52,10 +52,10 @@ public ProtobufMessageStoreBuilder(CommonFactory factory, AtomicLong seqNum) { @Override public RawMessage buildMessage(Map fields, Direction direction, String sessionId, String sessionGroup) { try { - byte[] bytes = CommonFactory.MAPPER.writeValueAsBytes(fields); + byte[] bytes = MAPPER.writeValueAsBytes(fields); return buildMessage(bytes, direction, sessionId, sessionGroup); } catch (JsonProcessingException e) { - logger.error("Could not encode message as JSON", e); + LOGGER.error("Could not encode message as JSON", e); return null; } } @@ -74,7 +74,7 @@ public RawMessage buildMessageFromFile(Path path, Direction direction, String se try (InputStream is = Files.newInputStream(path)) { return RawMessage.newBuilder().setMetadata(messageMetadata).setBody(ByteString.readFrom(is, 0x1000)).build(); } catch (IOException e) { - logger.error("Cannot encode screenshot", e); + LOGGER.error("Cannot encode screenshot", e); return null; } } diff --git a/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java b/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java index f268039..b616baf 100644 --- a/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java +++ b/src/main/java/com/exactpro/th2/hand/builders/mstore/TransportMessageStoreBuilder.java @@ -18,7 +18,6 @@ import com.exactpro.remotehand.Configuration; import com.exactpro.th2.common.grpc.Direction; -import com.exactpro.th2.common.schema.factory.CommonFactory; import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.MessageId; import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.RawMessage; import com.fasterxml.jackson.core.JsonProcessingException; @@ -38,7 +37,7 @@ import static com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.TransportUtilsKt.getTransport; public final class TransportMessageStoreBuilder implements MessageStoreBuilder { - private static final Logger logger = LoggerFactory.getLogger(TransportMessageStoreBuilder.class); + private static final Logger LOGGER = LoggerFactory.getLogger(TransportMessageStoreBuilder.class); private final AtomicLong seqNum; @@ -49,10 +48,10 @@ public TransportMessageStoreBuilder(AtomicLong seqNum) { @Override public RawMessage buildMessage(Map fields, Direction direction, String sessionId, String sessionGroup) { try { - byte[] bytes = CommonFactory.MAPPER.writeValueAsBytes(fields); + byte[] bytes = MAPPER.writeValueAsBytes(fields); return buildMessage(bytes, direction, sessionId, sessionGroup); } catch (JsonProcessingException e) { - logger.error("Could not encode message as JSON", e); + LOGGER.error("Could not encode message as JSON", e); return null; } } @@ -90,7 +89,7 @@ public RawMessage buildMessageFromFile(Path path, Direction direction, String se .setBody(buffer) .build(); } catch (IOException e) { - logger.error("Cannot encode screenshot", e); + LOGGER.error("Cannot encode screenshot", e); return null; } } diff --git a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java index 4c7b6b1..f3c4796 100644 --- a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java +++ b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java @@ -28,12 +28,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; - import static com.exactpro.th2.common.message.MessageUtils.toJson; public class HandBaseService extends RhBatchImplBase implements IHandService { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final static Logger LOGGER = LoggerFactory.getLogger(HandBaseService.class); public static final String RH_SESSION_PREFIX = "/Ses"; @@ -50,29 +48,35 @@ public void register(RhTargetServer targetServer, StreamObserver re String sessionId = messageHandler.getRhConnectionManager().createSessionHandler(targetServer.getTarget()).getId(); RhSessionID result = RhSessionID.newBuilder().setId(sessionId).setSessionAlias(messageHandler.getConfig().getSessionAlias()).build(); responseObserver.onNext(result); + responseObserver.onCompleted(); } catch (Exception e) { - logger.error("Error while creating session", e); + LOGGER.error("Error while creating session", e); Exception responseException = new HandException("Error while creating session", e); responseObserver.onError(responseException); } - responseObserver.onCompleted(); } @Override public void unregister(RhSessionID request, StreamObserver responseObserver) { - messageHandler.getRhConnectionManager().closeSessionHandler(request.getId()); - responseObserver.onNext(Empty.getDefaultInstance()); - responseObserver.onCompleted(); + try { + messageHandler.getRhConnectionManager().closeSessionHandler(request.getId()); + responseObserver.onNext(Empty.getDefaultInstance()); + responseObserver.onCompleted(); + } catch (Exception e) { + LOGGER.error("Action failure, request: '{}'", toJson(request), e); + responseObserver.onError(e); + } + } @Override public void executeRhActionsBatch(RhActionsBatch request, StreamObserver responseObserver) { - logger.trace("Action: '{}', request: '{}'", "executeRhActionsBatch", toJson(request)); + LOGGER.trace("Action: 'executeRhActionsBatch', request: '{}'", toJson(request)); try { responseObserver.onNext(messageHandler.handleActionsBatchRequest(request)); responseObserver.onCompleted(); - } catch (IOException e) { - logger.error("Action: '{}', request: '{}'", "executeRhActionsBatch", toJson(request), e); + } catch (Exception e) { + LOGGER.error("Action failure, request: '{}'", toJson(request), e); responseObserver.onError(e); } } @@ -82,7 +86,7 @@ public void dispose() { try { this.messageHandler.close(); } catch (Exception e) { - logger.error("Error while disposing message handler", e); + LOGGER.error("Error while disposing message handler", e); } } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java index a5b5aba..137fc9c 100644 --- a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java @@ -93,9 +93,7 @@ public RhBatchResponse handleActionsBatchRequest(RhActionsBatch request) throws } @Override - public void close() throws Exception { + public void close() { rhConnectionManager.dispose(); - messageStoreHandler.close(); - eventStoreHandler.close(); } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreHandler.java b/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreHandler.java index 967366f..282bde2 100644 --- a/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import com.exactpro.th2.hand.builders.events.DefaultEventBuilder; -public class EventStoreHandler implements AutoCloseable { +public class EventStoreHandler { private final EventStoreSender eventStoreSender; private final DefaultEventBuilder eventBuilder; @@ -35,9 +35,4 @@ public EventStoreSender getEventStoreSender() { public DefaultEventBuilder getEventBuilder() { return eventBuilder; } - - @Override - public void close() throws Exception { - this.eventStoreSender.close(); - } } diff --git a/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreSender.java index 4868648..7c2d17e 100644 --- a/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/estore/EventStoreSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import java.io.IOException; -public class EventStoreSender implements AutoCloseable { +public class EventStoreSender { private static final Logger LOGGER = LoggerFactory.getLogger(EventStoreSender.class); private final MessageRouter eventBatchRouter; @@ -41,9 +41,4 @@ public void storeEvent(Event event) { LOGGER.error("Could not store event with id: " + event.getId(), e); } } - - @Override - public void close() throws Exception { - eventBatchRouter.close(); - } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java index c7e96d3..67fa861 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreHandler.java @@ -41,8 +41,8 @@ import java.util.List; import java.util.Map; -public class MessageStoreHandler implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(ProtobufMessageStoreSender.class); +public class MessageStoreHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(MessageStoreHandler.class); private final String sessionGroup; private final MessageStoreSender messageStoreSender; @@ -67,7 +67,7 @@ private List getActionsList (RhActionsBatch action case WEB: return rhActionList.getWeb().getWebActionListList(); default: - logger.warn("Actions list is not set"); + LOGGER.warn("Actions list is not set"); return Collections.emptyList(); } } @@ -104,24 +104,24 @@ public List onRequest(RhActionsBatch actionsList, String sessionId) { messageStoreSender.sendMessages(message); return Collections.singletonList(messageIdExtractor.getId(message)); } else { - logger.debug("Nothing to store to mstore"); + LOGGER.debug("Nothing to store to mstore"); return Collections.emptyList(); } } public List storeScreenshots(List screenshotIds, String sessionAlias) { if (screenshotIds == null || screenshotIds.isEmpty()) { - logger.debug("No screenshots to store"); + LOGGER.debug("No screenshots to store"); return Collections.emptyList(); } List messageIDS = new ArrayList<>(); List rawMessages = new ArrayList<>(); for (ActionResult screenshotId : screenshotIds) { - logger.debug("Storing screenshot id {}", screenshotId); + LOGGER.debug("Storing screenshot id {}", screenshotId); Path screenPath = Configuration.SCREENSHOTS_DIR_PATH.resolve(screenshotId.getData()); if (!Files.exists(screenPath)) { - logger.warn("Screenshot with id {} does not exists", screenshotId); + LOGGER.warn("Screenshot with id {} does not exists", screenshotId); continue; } T rawMessage = messageStoreBuilder.buildMessageFromFile(screenPath, Direction.FIRST, sessionAlias, sessionGroup); @@ -143,7 +143,7 @@ public MessageID onResponse(RhScriptResult response, String sessionId, String rh messageStoreSender.sendMessages(message); return messageIdExtractor.getId(message); } catch (Exception e) { - logger.error("Cannot send message to message-storage", e); + LOGGER.error("Cannot send message to message-storage", e); } return null; @@ -153,7 +153,7 @@ private void removeScreenshot(Path file) { try { Files.delete(file); } catch (IOException e) { - logger.warn("Error deleting file: " + file.toAbsolutePath(), e); + LOGGER.warn("Error deleting file: " + file.toAbsolutePath(), e); } } @@ -183,11 +183,6 @@ private List> processList(List list) { return processed; } - @Override - public void close() throws Exception { - this.messageStoreSender.close(); - } - @FunctionalInterface public interface MessageIdExtractor { MessageID getId(T message); diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java index 6b542e6..f12553c 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java @@ -18,7 +18,7 @@ import java.util.Collection; -public interface MessageStoreSender extends AutoCloseable { +public interface MessageStoreSender { String RAW_MESSAGE_ATTRIBUTE = "raw"; void sendMessages(T messages); diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java index b3d114d..52df234 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java @@ -30,7 +30,7 @@ import java.util.Collections; public class ProtobufMessageStoreSender implements MessageStoreSender { - private static final Logger logger = LoggerFactory.getLogger(ProtobufMessageStoreSender.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufMessageStoreSender.class); private final MessageRouter messageRouterGroupBatch; private final long batchLimit; @@ -49,16 +49,10 @@ public void sendMessages(Collection messages) { try { sendRawMessages(messages); } catch (Exception e) { - logger.error("Cannot store to mstore", e); + LOGGER.error("Cannot store to mstore", e); } } - @Override - public void close() throws Exception { - messageRouterGroupBatch.close(); - } - - private void sendRawMessages(Collection messages) throws Exception { MessageGroupBatch.Builder currentBatchBuilder = MessageGroupBatch.newBuilder(); long currentBatchLength = 0; @@ -94,11 +88,11 @@ private void sendRawMessages(Collection messages) throws Exception { } if (count == 0) { - logger.debug("There are no valid messages to send"); + LOGGER.debug("There are no valid messages to send"); return; } - logger.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", + LOGGER.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", count, batchesCount, totalLength); } diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java index 0a4a64e..95841be 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java @@ -29,13 +29,12 @@ import java.util.Collections; public class TransportMessageStoreSender implements MessageStoreSender { - private static final Logger logger = LoggerFactory.getLogger(TransportMessageStoreSender.class); + private static final Logger LOGGER = LoggerFactory.getLogger(TransportMessageStoreSender.class); private final MessageRouter messageRouter; private final long batchLimit; private final String book; private final String sessionGroup; - public TransportMessageStoreSender(CommonFactory factory) { messageRouter = factory.getTransportGroupBatchRouter(); CustomConfiguration customConfiguration = factory.getCustomConfiguration(CustomConfiguration.class); @@ -52,16 +51,10 @@ public void sendMessages(Collection messages) { try { sendRawMessages(messages); } catch (Exception e) { - logger.error("Cannot store to mstore", e); + LOGGER.error("Cannot store to mstore", e); } } - @Override - public void close() throws Exception { - messageRouter.close(); - } - - private void sendRawMessages(Collection messages) throws Exception { GroupBatch.Builder currentBatchBuilder = createBatchBuilder(); long currentBatchLength = 0; @@ -95,11 +88,11 @@ private void sendRawMessages(Collection messages) throws Exception { } if (count == 0) { - logger.debug("There are no valid messages to send"); + LOGGER.debug("There are no valid messages to send"); return; } - logger.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", + LOGGER.debug("Group with {} message(s) separated by {} batches to mstore was sent ({} bytes)", count, batchesCount, totalLength); } From f9ae3ab7680c6e6b56332b27fceab0c74503f35f Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Thu, 8 Feb 2024 16:53:20 +0400 Subject: [PATCH 07/12] [RM-84612] corrected after review --- .../com/exactpro/th2/hand/Application.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/exactpro/th2/hand/Application.java b/src/main/java/com/exactpro/th2/hand/Application.java index 6a0560d..d110673 100644 --- a/src/main/java/com/exactpro/th2/hand/Application.java +++ b/src/main/java/com/exactpro/th2/hand/Application.java @@ -40,18 +40,19 @@ public long getCurrentTime() { public void run(String[] args) { try (CommonFactory factory = CommonFactory.createFromArguments(args)) { Config config = getConfig(factory); - final HandServer handServer = new HandServer(config, getCurrentTime()); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - LOGGER.info("*** Closing hand server because JVM is shutting down"); - try { - handServer.close(); - } catch (InterruptedException e) { - LOGGER.warn("Server termination await was interrupted", e); - } - LOGGER.info("*** hand server closed"); - })); - handServer.start(); - handServer.blockUntilShutdown(); + try (HandServer handServer = new HandServer(config, getCurrentTime())) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + LOGGER.info("*** Closing hand server because JVM is shutting down"); + try { + handServer.close(); + } catch (InterruptedException e) { + LOGGER.warn("Server termination await was interrupted", e); + } + LOGGER.info("*** hand server closed"); + })); + handServer.start(); + handServer.blockUntilShutdown(); + } } catch (Exception e) { LOGGER.error("Could not to start Hand server", e); closeApp(); From 1c75c24f15e62fbe1ad4ee7093d530e9016d9d92 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Mon, 12 Feb 2024 12:53:55 +0400 Subject: [PATCH 08/12] [RM-84612] jar name --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index bd149a8..2201784 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'th2-hand' +rootProject.name = 'hand' From 4cfdb52cab1b96b8a3cbc7897cbc6a8db56e3d83 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Mon, 12 Feb 2024 14:56:33 +0400 Subject: [PATCH 09/12] [RM-84612] HandBaseService implements RhBatchService to call service from the same process without network --- .../com/exactpro/th2/hand/Application.java | 15 +--- .../java/com/exactpro/th2/hand/Config.java | 13 ++-- .../com/exactpro/th2/hand/HandServer.java | 5 +- .../th2/hand/services/HandBaseService.java | 72 +++++++++++++++++-- .../th2/hand/services/MessageHandler.java | 10 ++- 5 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/exactpro/th2/hand/Application.java b/src/main/java/com/exactpro/th2/hand/Application.java index d110673..103ee61 100644 --- a/src/main/java/com/exactpro/th2/hand/Application.java +++ b/src/main/java/com/exactpro/th2/hand/Application.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,10 @@ package com.exactpro.th2.hand; +import com.exactpro.th2.common.schema.factory.CommonFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.exactpro.th2.common.schema.factory.CommonFactory; - -import java.time.Instant; -import java.util.concurrent.TimeUnit; - public class Application { private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); @@ -32,15 +28,10 @@ public static void main(String[] args) { new Application().run(args); } - public long getCurrentTime() { - Instant now = Instant.now(); - return TimeUnit.SECONDS.toNanos(now.getEpochSecond()) + now.getNano(); - } - public void run(String[] args) { try (CommonFactory factory = CommonFactory.createFromArguments(args)) { Config config = getConfig(factory); - try (HandServer handServer = new HandServer(config, getCurrentTime())) { + try (HandServer handServer = new HandServer(config)) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { LOGGER.info("*** Closing hand server because JVM is shutting down"); try { diff --git a/src/main/java/com/exactpro/th2/hand/Config.java b/src/main/java/com/exactpro/th2/hand/Config.java index 6267df0..38c365b 100644 --- a/src/main/java/com/exactpro/th2/hand/Config.java +++ b/src/main/java/com/exactpro/th2/hand/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2024 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,7 @@ public class Config { protected final CommonFactory factory; protected final CustomConfiguration customConfiguration; - public Config(CommonFactory factory) throws ConfigurationException { - this.factory = factory; - this.customConfiguration = factory.getCustomConfiguration(CustomConfiguration.class); + public Config(CommonFactory factory, CustomConfiguration customConfiguration) throws ConfigurationException { if (customConfiguration == null) { throw new ConfigurationException("Custom configuration is not found"); } @@ -39,6 +37,13 @@ public Config(CommonFactory factory) throws ConfigurationException { if (customConfiguration.getDriversMapping().isEmpty()) { throw new ConfigurationException("Drivers mapping should be provided in custom config."); } + + this.factory = factory; + this.customConfiguration = customConfiguration; + } + + public Config(CommonFactory factory) throws ConfigurationException { + this(factory, factory.getCustomConfiguration(CustomConfiguration.class)); } public CommonFactory getFactory() { diff --git a/src/main/java/com/exactpro/th2/hand/HandServer.java b/src/main/java/com/exactpro/th2/hand/HandServer.java index 7001366..22a8098 100644 --- a/src/main/java/com/exactpro/th2/hand/HandServer.java +++ b/src/main/java/com/exactpro/th2/hand/HandServer.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.ServiceLoader; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public class HandServer implements AutoCloseable { @@ -39,9 +38,9 @@ public class HandServer implements AutoCloseable { private final List services; private final AtomicReference watcher = new AtomicReference<>(); - public HandServer(Config config, long startSequences) throws Exception { + public HandServer(Config config) throws Exception { this.config = config; - this.messageHandler = new MessageHandler(config, new AtomicLong(startSequences)); + this.messageHandler = new MessageHandler(config); this.services = new ArrayList<>(); this.server = buildServer(); } diff --git a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java index f3c4796..993686d 100644 --- a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java +++ b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java @@ -16,9 +16,11 @@ package com.exactpro.th2.hand.services; +import com.exactpro.remotehand.RhConfigurationException; import com.exactpro.th2.act.grpc.hand.RhActionsBatch; import com.exactpro.th2.act.grpc.hand.RhBatchGrpc.RhBatchImplBase; import com.exactpro.th2.act.grpc.hand.RhBatchResponse; +import com.exactpro.th2.act.grpc.hand.RhBatchService; import com.exactpro.th2.act.grpc.hand.RhSessionID; import com.exactpro.th2.act.grpc.hand.RhTargetServer; import com.exactpro.th2.hand.HandException; @@ -28,9 +30,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.Map; + import static com.exactpro.th2.common.message.MessageUtils.toJson; -public class HandBaseService extends RhBatchImplBase implements IHandService { +public class HandBaseService extends RhBatchImplBase implements IHandService, RhBatchService { private final static Logger LOGGER = LoggerFactory.getLogger(HandBaseService.class); public static final String RH_SESSION_PREFIX = "/Ses"; @@ -45,9 +50,7 @@ public void init(MessageHandler messageHandler) { @Override public void register(RhTargetServer targetServer, StreamObserver responseObserver) { try { - String sessionId = messageHandler.getRhConnectionManager().createSessionHandler(targetServer.getTarget()).getId(); - RhSessionID result = RhSessionID.newBuilder().setId(sessionId).setSessionAlias(messageHandler.getConfig().getSessionAlias()).build(); - responseObserver.onNext(result); + responseObserver.onNext(register(targetServer)); responseObserver.onCompleted(); } catch (Exception e) { LOGGER.error("Error while creating session", e); @@ -59,8 +62,7 @@ public void register(RhTargetServer targetServer, StreamObserver re @Override public void unregister(RhSessionID request, StreamObserver responseObserver) { try { - messageHandler.getRhConnectionManager().closeSessionHandler(request.getId()); - responseObserver.onNext(Empty.getDefaultInstance()); + responseObserver.onNext(unregister(request)); responseObserver.onCompleted(); } catch (Exception e) { LOGGER.error("Action failure, request: '{}'", toJson(request), e); @@ -73,7 +75,7 @@ public void unregister(RhSessionID request, StreamObserver responseObserv public void executeRhActionsBatch(RhActionsBatch request, StreamObserver responseObserver) { LOGGER.trace("Action: 'executeRhActionsBatch', request: '{}'", toJson(request)); try { - responseObserver.onNext(messageHandler.handleActionsBatchRequest(request)); + responseObserver.onNext(executeRhActionsBatch(request)); responseObserver.onCompleted(); } catch (Exception e) { LOGGER.error("Action failure, request: '{}'", toJson(request), e); @@ -89,4 +91,60 @@ public void dispose() { LOGGER.error("Error while disposing message handler", e); } } + + /** + * @throws RhConfigurationException + */ + @SuppressWarnings("JavadocDeclaration") + @Override + public RhSessionID register(RhTargetServer targetServer) { + try { + String sessionId = messageHandler.getRhConnectionManager().createSessionHandler(targetServer.getTarget()).getId(); + return RhSessionID.newBuilder().setId(sessionId).setSessionAlias(messageHandler.getConfig().getSessionAlias()).build(); + } catch (RhConfigurationException e) { + sneakyThrow(e); + return null; + } + } + + @Override + public RhSessionID register(RhTargetServer input, Map properties) { + return register(input); + } + + @Override + public Empty unregister(RhSessionID request) { + messageHandler.getRhConnectionManager().closeSessionHandler(request.getId()); + return Empty.getDefaultInstance(); + } + + @Override + public Empty unregister(RhSessionID input, Map properties) { + return unregister(input); + } + + /** + * @throws IOException + */ + @SuppressWarnings("JavadocDeclaration") + @Override + public RhBatchResponse executeRhActionsBatch(RhActionsBatch request) { + try { + return messageHandler.handleActionsBatchRequest(request); + } catch (IOException e) { + sneakyThrow(e); + return null; + } + } + + @Override + public RhBatchResponse executeRhActionsBatch(RhActionsBatch input, Map properties) { + return executeRhActionsBatch(input); + } + + // This hack is used to avoid wrapping cause error + private static void sneakyThrow(Throwable e) throws E { + //noinspection unchecked + throw (E) e; + } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java index 137fc9c..03dbc4d 100644 --- a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java @@ -34,6 +34,8 @@ import com.exactpro.th2.hand.services.mstore.TransportMessageStoreSender; import java.io.IOException; +import java.time.Instant; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; public class MessageHandler implements AutoCloseable { @@ -43,10 +45,11 @@ public class MessageHandler implements AutoCloseable { private final RhConnectionManager rhConnectionManager; private final ScriptBuilder scriptBuilder = new ScriptBuilder(); - public MessageHandler(Config config, AtomicLong seqNum) { + public MessageHandler(Config config) { this.config = config; rhConnectionManager = new RhConnectionManager(config); CommonFactory factory = config.getFactory(); + AtomicLong seqNum = new AtomicLong(getCurrentTime()); if (config.isUseTransport()) { this.messageStoreHandler = new MessageStoreHandler<>( config.getSessionGroup(), @@ -96,4 +99,9 @@ public RhBatchResponse handleActionsBatchRequest(RhActionsBatch request) throws public void close() { rhConnectionManager.dispose(); } + + private static long getCurrentTime() { + Instant now = Instant.now(); + return TimeUnit.SECONDS.toNanos(now.getEpochSecond()) + now.getNano(); + } } \ No newline at end of file From ec60f10e620a9e12383cdc9ef2cbff2f762e4161 Mon Sep 17 00:00:00 2001 From: "nikita.smirnov" Date: Tue, 13 Feb 2024 14:11:17 +0400 Subject: [PATCH 10/12] [RM-84612] Corrected after review --- build.gradle | 2 -- .../com/exactpro/th2/hand/HandServer.java | 3 ++- .../th2/hand/services/HandBaseService.java | 20 ++----------------- .../th2/hand/services/HandRuntimeException.kt | 19 ++++++++++++++++++ 4 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/com/exactpro/th2/hand/services/HandRuntimeException.kt diff --git a/build.gradle b/build.gradle index 7879e11..70edc56 100644 --- a/build.gradle +++ b/build.gradle @@ -211,8 +211,6 @@ jar { archivesBaseName = applicationName manifest { attributes('Specification-Title': 'TH2 Hand') - attributes('Main-Class': 'com.exactpro.th2.hand.Application') - attributes("Class-Path": configurations.compileClasspath.collect { "lib/" + it.getName() }.join(' ')) attributes( 'Created-By': "${System.getProperty('java.version')} (${System.getProperty('java.vendor')})", 'Specification-Title': '', diff --git a/src/main/java/com/exactpro/th2/hand/HandServer.java b/src/main/java/com/exactpro/th2/hand/HandServer.java index 22a8098..c539352 100644 --- a/src/main/java/com/exactpro/th2/hand/HandServer.java +++ b/src/main/java/com/exactpro/th2/hand/HandServer.java @@ -66,7 +66,7 @@ public void start() throws IOException { server.start(); LOGGER.info("Server started, listening on port {}", server.getPort()); } else { - throw new IllegalStateException(getClass().getSimpleName() + " was started once"); + throw new IllegalStateException(getClass().getSimpleName() + " is already started"); } } @@ -94,6 +94,7 @@ public void close() throws InterruptedException { Thread thread = watcher.get(); if (thread != null && !thread.isInterrupted()) { thread.interrupt(); + thread.join(30_000); } } } \ No newline at end of file diff --git a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java index 993686d..444a81b 100644 --- a/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java +++ b/src/main/java/com/exactpro/th2/hand/services/HandBaseService.java @@ -92,18 +92,13 @@ public void dispose() { } } - /** - * @throws RhConfigurationException - */ - @SuppressWarnings("JavadocDeclaration") @Override public RhSessionID register(RhTargetServer targetServer) { try { String sessionId = messageHandler.getRhConnectionManager().createSessionHandler(targetServer.getTarget()).getId(); return RhSessionID.newBuilder().setId(sessionId).setSessionAlias(messageHandler.getConfig().getSessionAlias()).build(); } catch (RhConfigurationException e) { - sneakyThrow(e); - return null; + throw new HandRuntimeException(e.getMessage(), e); } } @@ -123,17 +118,12 @@ public Empty unregister(RhSessionID input, Map properties) { return unregister(input); } - /** - * @throws IOException - */ - @SuppressWarnings("JavadocDeclaration") @Override public RhBatchResponse executeRhActionsBatch(RhActionsBatch request) { try { return messageHandler.handleActionsBatchRequest(request); } catch (IOException e) { - sneakyThrow(e); - return null; + throw new HandRuntimeException(e.getMessage(), e); } } @@ -141,10 +131,4 @@ public RhBatchResponse executeRhActionsBatch(RhActionsBatch request) { public RhBatchResponse executeRhActionsBatch(RhActionsBatch input, Map properties) { return executeRhActionsBatch(input); } - - // This hack is used to avoid wrapping cause error - private static void sneakyThrow(Throwable e) throws E { - //noinspection unchecked - throw (E) e; - } } \ No newline at end of file diff --git a/src/main/kotlin/com/exactpro/th2/hand/services/HandRuntimeException.kt b/src/main/kotlin/com/exactpro/th2/hand/services/HandRuntimeException.kt new file mode 100644 index 0000000..8d72729 --- /dev/null +++ b/src/main/kotlin/com/exactpro/th2/hand/services/HandRuntimeException.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 Exactpro (Exactpro Systems Limited) + * + * 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.exactpro.th2.hand.services + +class HandRuntimeException(message: String, cause: Throwable) : RuntimeException(message, cause) \ No newline at end of file From 8740dafa0038f3604e80b814a546e16383d4cb96 Mon Sep 17 00:00:00 2001 From: Oleg Smelov <45400511+lumber1000@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:08:46 +0400 Subject: [PATCH 11/12] [RM-84612] fixes for embedded mode (#53) --- README.md | 6 ++-- build.gradle | 17 +++++------ .../com/exactpro/th2/hand/Application.java | 8 ++--- .../th2/hand/{Config.java => Context.java} | 10 +++++-- .../com/exactpro/th2/hand/HandServer.java | 10 +++---- .../th2/hand/RhConnectionManager.java | 10 +++---- .../th2/hand/schema/CustomConfiguration.java | 8 ++--- .../th2/hand/services/MessageHandler.java | 30 +++++++++---------- .../services/mstore/MessageStoreSender.java | 2 -- .../mstore/ProtobufMessageStoreSender.java | 10 +++---- .../mstore/TransportMessageStoreSender.java | 14 ++++----- 11 files changed, 62 insertions(+), 63 deletions(-) rename src/main/java/com/exactpro/th2/hand/{Config.java => Context.java} (90%) diff --git a/README.md b/README.md index 4b420d7..1d7070d 100644 --- a/README.md +++ b/README.md @@ -132,9 +132,9 @@ Example of `rabbitMQ.json`: ### 3.1.0 -+ reads dictionaries from the /var/th2/config/dictionary folder -+ uses mq_router, grpc_router, cradle_manager optional JSON configs from the /var/th2/config folder -+ tries to load log4j.properties files from sources in order: '/var/th2/config', '/home/etc', configured path via cmd, default configuration ++ reads dictionaries from the /var/th2/context/dictionary folder ++ uses mq_router, grpc_router, cradle_manager optional JSON configs from the /var/th2/context folder ++ tries to load log4j.properties files from sources in order: '/var/th2/context', '/home/etc', configured path via cmd, default configuration + update Cradle version. Introduce async API for storing events + removed gRPC event loop handling + fixed dictionary reading \ No newline at end of file diff --git a/build.gradle b/build.gradle index 70edc56..57d983f 100644 --- a/build.gradle +++ b/build.gradle @@ -28,9 +28,9 @@ plugins { id "org.owasp.dependencycheck" version "9.0.9" id "com.gorylenko.gradle-git-properties" version "2.4.1" id 'com.github.jk1.dependency-license-report' version '2.5' - id "de.undercouch.download" version "5.4.0" + id "de.undercouch.download" version "5.6.0" id 'signing' - id "com.google.protobuf" version "0.9.3" + id "com.google.protobuf" version "0.9.4" } group 'com.exactpro.th2' @@ -57,15 +57,13 @@ repositories { } dependencies { - api platform('com.exactpro.th2:bom:4.5.0') + api platform('com.exactpro.th2:bom:4.6.0') - implementation('com.exactpro.remotehand:remotehand:1.7.3-TH2-4662-4046816762-SNAPSHOT') { - exclude group: "org.slf4j", module: "slf4j-log4j12" - } - implementation("com.exactpro.th2:grpc-hand:3.0.0-RM-84612-+") { + implementation('com.exactpro.remotehand:remotehand:1.8.0-dev') + implementation("com.exactpro.th2:grpc-hand:3.0.0-dev") { exclude group: "com.google.guava", module: "guava" // for compatibility with Selenium 3.141.59 } - implementation("com.exactpro.th2:common:5.8.0-dev") { + implementation("com.exactpro.th2:common:5.10.0-dev") { exclude group: "com.google.guava", module: "guava" // for compatibility with Selenium 3.141.59 } implementation("com.exactpro.th2:common-utils:2.2.2-dev") { @@ -79,13 +77,12 @@ dependencies { implementation "com.fasterxml.jackson.core:jackson-annotations" implementation 'org.apache.commons:commons-lang3' - implementation "org.apache.commons:commons-csv:1.9.0" + implementation "org.apache.commons:commons-csv:1.10.0" testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' testImplementation 'io.strikt:strikt-core:0.34.1' - } java { diff --git a/src/main/java/com/exactpro/th2/hand/Application.java b/src/main/java/com/exactpro/th2/hand/Application.java index 103ee61..4a28d31 100644 --- a/src/main/java/com/exactpro/th2/hand/Application.java +++ b/src/main/java/com/exactpro/th2/hand/Application.java @@ -30,8 +30,8 @@ public static void main(String[] args) { public void run(String[] args) { try (CommonFactory factory = CommonFactory.createFromArguments(args)) { - Config config = getConfig(factory); - try (HandServer handServer = new HandServer(config)) { + Context context = getConfig(factory); + try (HandServer handServer = new HandServer(context)) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { LOGGER.info("*** Closing hand server because JVM is shutting down"); try { @@ -50,8 +50,8 @@ public void run(String[] args) { } } - protected Config getConfig(CommonFactory factory) throws ConfigurationException { - return new Config(factory); + protected Context getConfig(CommonFactory factory) throws ConfigurationException { + return new Context(factory); } private static void closeApp() { diff --git a/src/main/java/com/exactpro/th2/hand/Config.java b/src/main/java/com/exactpro/th2/hand/Context.java similarity index 90% rename from src/main/java/com/exactpro/th2/hand/Config.java rename to src/main/java/com/exactpro/th2/hand/Context.java index 38c365b..867672f 100644 --- a/src/main/java/com/exactpro/th2/hand/Config.java +++ b/src/main/java/com/exactpro/th2/hand/Context.java @@ -24,12 +24,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -public class Config { +public class Context { protected final CommonFactory factory; protected final CustomConfiguration customConfiguration; - public Config(CommonFactory factory, CustomConfiguration customConfiguration) throws ConfigurationException { + public Context(CommonFactory factory, CustomConfiguration customConfiguration) throws ConfigurationException { if (customConfiguration == null) { throw new ConfigurationException("Custom configuration is not found"); } @@ -42,7 +42,7 @@ public Config(CommonFactory factory, CustomConfiguration customConfiguration) th this.customConfiguration = customConfiguration; } - public Config(CommonFactory factory) throws ConfigurationException { + public Context(CommonFactory factory) throws ConfigurationException { this(factory, factory.getCustomConfiguration(CustomConfiguration.class)); } @@ -50,6 +50,10 @@ public CommonFactory getFactory() { return factory; } + public CustomConfiguration getCustomConfiguration() { + return customConfiguration; + } + public Map getDriversMapping() { return customConfiguration.getDriversMapping(); } diff --git a/src/main/java/com/exactpro/th2/hand/HandServer.java b/src/main/java/com/exactpro/th2/hand/HandServer.java index c539352..1576df1 100644 --- a/src/main/java/com/exactpro/th2/hand/HandServer.java +++ b/src/main/java/com/exactpro/th2/hand/HandServer.java @@ -32,15 +32,15 @@ public class HandServer implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(HandServer.class); - private final Config config; + private final Context context; private final MessageHandler messageHandler; private final Server server; private final List services; private final AtomicReference watcher = new AtomicReference<>(); - public HandServer(Config config) throws Exception { - this.config = config; - this.messageHandler = new MessageHandler(config); + public HandServer(Context context) throws Exception { + this.context = context; + this.messageHandler = new MessageHandler(context); this.services = new ArrayList<>(); this.server = buildServer(); } @@ -52,7 +52,7 @@ protected Server buildServer() throws Exception { LOGGER.info("Service '{}' loaded", rhService.getClass().getName()); } - return config.getFactory().getGrpcRouter().startServer(services.toArray(new IHandService[0])); + return context.getFactory().getGrpcRouter().startServer(services.toArray(new IHandService[0])); } /** diff --git a/src/main/java/com/exactpro/th2/hand/RhConnectionManager.java b/src/main/java/com/exactpro/th2/hand/RhConnectionManager.java index a98c91c..2388040 100644 --- a/src/main/java/com/exactpro/th2/hand/RhConnectionManager.java +++ b/src/main/java/com/exactpro/th2/hand/RhConnectionManager.java @@ -33,12 +33,12 @@ public class RhConnectionManager { private final Map sessions = new ConcurrentHashMap<>(); private final GridRemoteHandManager gridRemoteHandManager; - private final Config config; + private final Context context; - public RhConnectionManager(Config config) { - this.config = config; + public RhConnectionManager(Context context) { + this.context = context; gridRemoteHandManager = new GridRemoteHandManager(); - gridRemoteHandManager.createConfigurations(null, config.getRhOptions()); + gridRemoteHandManager.createConfigurations(null, context.getRhOptions()); } public HandSessionHandler getSessionHandler(String sessionId) throws IllegalArgumentException { @@ -49,7 +49,7 @@ public HandSessionHandler getSessionHandler(String sessionId) throws IllegalArgu } public HandSessionHandler createSessionHandler(String targetServer) throws RhConfigurationException { - Config.DriverMapping driverSettings = config.getDriversMapping().get(targetServer); + Context.DriverMapping driverSettings = context.getDriversMapping().get(targetServer); if (driverSettings == null) { throw new RhConfigurationException("Unrecognized targetServer: " + targetServer); } diff --git a/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java b/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java index 81ff1e0..f540ed4 100644 --- a/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java +++ b/src/main/java/com/exactpro/th2/hand/schema/CustomConfiguration.java @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.Map; -import com.exactpro.th2.hand.Config; +import com.exactpro.th2.hand.Context; import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; @@ -48,7 +48,7 @@ public class CustomConfiguration { @JsonProperty(value="drivers-mapping", required = true) @JsonAlias("driversMapping") - private Map driversMapping; + private Map driversMapping; @JsonProperty(value="rh-options") @JsonAlias("rhOptions") @@ -59,10 +59,10 @@ public class CustomConfiguration { private int responseTimeout = DEFAULT_RESPONSE_TIMEOUT; @JsonProperty(value="use-transport") - @JsonAlias("responseTimeoutSec") + @JsonAlias("useTransport") private boolean useTransport = true; - public Map getDriversMapping() { + public Map getDriversMapping() { return driversMapping; } diff --git a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java index 03dbc4d..cc6e1ac 100644 --- a/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java +++ b/src/main/java/com/exactpro/th2/hand/services/MessageHandler.java @@ -20,7 +20,7 @@ import com.exactpro.th2.act.grpc.hand.RhBatchResponse; import com.exactpro.th2.common.schema.factory.CommonFactory; import com.exactpro.th2.common.utils.message.transport.MessageUtilsKt; -import com.exactpro.th2.hand.Config; +import com.exactpro.th2.hand.Context; import com.exactpro.th2.hand.RhConnectionManager; import com.exactpro.th2.hand.builders.events.DefaultEventBuilder; import com.exactpro.th2.hand.builders.mstore.ProtobufMessageStoreBuilder; @@ -39,29 +39,29 @@ import java.util.concurrent.atomic.AtomicLong; public class MessageHandler implements AutoCloseable { - private final Config config; + private final Context context; private final MessageStoreHandler messageStoreHandler; private final EventStoreHandler eventStoreHandler; private final RhConnectionManager rhConnectionManager; private final ScriptBuilder scriptBuilder = new ScriptBuilder(); - public MessageHandler(Config config) { - this.config = config; - rhConnectionManager = new RhConnectionManager(config); - CommonFactory factory = config.getFactory(); + public MessageHandler(Context context) { + this.context = context; + rhConnectionManager = new RhConnectionManager(context); + CommonFactory factory = context.getFactory(); AtomicLong seqNum = new AtomicLong(getCurrentTime()); - if (config.isUseTransport()) { + if (context.isUseTransport()) { this.messageStoreHandler = new MessageStoreHandler<>( - config.getSessionGroup(), - new TransportMessageStoreSender(factory), + context.getSessionGroup(), + new TransportMessageStoreSender(context), new TransportMessageStoreBuilder(seqNum), - message -> MessageUtilsKt.toProto(message.getId(), config.getBook(), config.getSessionGroup()) + message -> MessageUtilsKt.toProto(message.getId(), context.getBook(), context.getSessionGroup()) ); } else { this.messageStoreHandler = new MessageStoreHandler<>( - config.getSessionGroup(), - new ProtobufMessageStoreSender(factory), - new ProtobufMessageStoreBuilder(config.getFactory(), seqNum), + context.getSessionGroup(), + new ProtobufMessageStoreSender(context), + new ProtobufMessageStoreBuilder(context.getFactory(), seqNum), message -> message.getMetadata().getId() ); } @@ -82,8 +82,8 @@ public ScriptBuilder getScriptBuilder() { return scriptBuilder; } - public Config getConfig() { - return config; + public Context getConfig() { + return context; } public RhConnectionManager getRhConnectionManager() { diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java index f12553c..887b02a 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/MessageStoreSender.java @@ -19,8 +19,6 @@ import java.util.Collection; public interface MessageStoreSender { - String RAW_MESSAGE_ATTRIBUTE = "raw"; - void sendMessages(T messages); void sendMessages(Collection messages); diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java index 52df234..7280286 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/ProtobufMessageStoreSender.java @@ -20,8 +20,8 @@ import com.exactpro.th2.common.grpc.MessageGroup; import com.exactpro.th2.common.grpc.MessageGroupBatch; import com.exactpro.th2.common.grpc.RawMessage; -import com.exactpro.th2.common.schema.factory.CommonFactory; import com.exactpro.th2.common.schema.message.MessageRouter; +import com.exactpro.th2.hand.Context; import com.exactpro.th2.hand.schema.CustomConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,14 +30,14 @@ import java.util.Collections; public class ProtobufMessageStoreSender implements MessageStoreSender { + private static final String RAW_MESSAGE_ATTRIBUTE = "raw"; private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufMessageStoreSender.class); private final MessageRouter messageRouterGroupBatch; private final long batchLimit; - - public ProtobufMessageStoreSender(CommonFactory factory) { - messageRouterGroupBatch = factory.getMessageRouterMessageGroupBatch(); - CustomConfiguration customConfiguration = factory.getCustomConfiguration(CustomConfiguration.class); + public ProtobufMessageStoreSender(Context context) { + messageRouterGroupBatch = context.getFactory().getMessageRouterMessageGroupBatch(); + CustomConfiguration customConfiguration = context.getCustomConfiguration(); this.batchLimit = customConfiguration.getMessageBatchLimit(); } diff --git a/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java b/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java index 95841be..271a055 100644 --- a/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java +++ b/src/main/java/com/exactpro/th2/hand/services/mstore/TransportMessageStoreSender.java @@ -16,11 +16,11 @@ package com.exactpro.th2.hand.services.mstore; -import com.exactpro.th2.common.schema.factory.CommonFactory; import com.exactpro.th2.common.schema.message.MessageRouter; import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.GroupBatch; import com.exactpro.th2.common.schema.message.impl.rabbitmq.transport.RawMessage; import com.exactpro.th2.common.utils.message.transport.MessageUtilsKt; +import com.exactpro.th2.hand.Context; import com.exactpro.th2.hand.schema.CustomConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,11 +35,11 @@ public class TransportMessageStoreSender implements MessageStoreSender messages) throws Exception { //if batch limit has incorrect value, sender should pack each message to batch //if one message is bigger that batchLimit it is should send to mstore anyway and reject by it if (currentBatchLength + size > batchLimit && currentBatchLength != 0) { - this.messageRouter.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); + this.messageRouter.sendAll(currentBatchBuilder.build()); currentBatchBuilder = createBatchBuilder(); currentBatchLength = 0; batchesCount++; @@ -83,7 +83,7 @@ private void sendRawMessages(Collection messages) throws Exception { } if (currentBatchLength != 0) { - this.messageRouter.sendAll(currentBatchBuilder.build(), RAW_MESSAGE_ATTRIBUTE); + this.messageRouter.sendAll(currentBatchBuilder.build()); batchesCount++; } From af0bdfa7af5db05a5fc2b105d157b4d485ff46ec Mon Sep 17 00:00:00 2001 From: Oleg Smelov Date: Wed, 10 Apr 2024 13:30:54 +0400 Subject: [PATCH 12/12] plugins update --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 57d983f..0c4f2e8 100644 --- a/build.gradle +++ b/build.gradle @@ -24,8 +24,8 @@ plugins { id 'maven-publish' id 'org.jetbrains.kotlin.jvm' version '1.8.22' id "io.github.gradle-nexus.publish-plugin" version "1.3.0" - id 'com.palantir.docker' version '0.34.0' - id "org.owasp.dependencycheck" version "9.0.9" + id 'com.palantir.docker' version '0.36.0' + id "org.owasp.dependencycheck" version "9.1.0" id "com.gorylenko.gradle-git-properties" version "2.4.1" id 'com.github.jk1.dependency-license-report' version '2.5' id "de.undercouch.download" version "5.6.0"