You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by be...@apache.org on 2022/10/04 15:17:44 UTC
[nifi] branch main updated: NIFI-10531 Add supported operations to c2 heartbeat:
This is an automated email from the ASF dual-hosted git repository.
bejancsaba pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 504baae227 NIFI-10531 Add supported operations to c2 heartbeat:
504baae227 is described below
commit 504baae227860e2852c2740fed7c366803a89117
Author: Ferenc Erdei <er...@gmail.com>
AuthorDate: Wed Sep 21 15:09:09 2022 +0200
NIFI-10531 Add supported operations to c2 heartbeat:
This closes #6438
Signed-off-by: Csaba Bejan <be...@gmail.com>
---
.../nifi/c2/client/service/C2ClientService.java | 8 ++-
.../nifi/c2/client/service/C2HeartbeatFactory.java | 47 +++++--------
.../c2/client/service/ManifestHashProvider.java | 81 +++++++++++++++++++++
.../service/operation/C2OperationHandler.java | 8 +++
.../service/operation/C2OperationService.java | 14 ++++
.../service/operation/DebugOperationHandler.java | 16 ++++-
.../DescribeManifestOperationHandler.java | 15 ++--
...er.java => EmptyOperandPropertiesProvider.java} | 38 +++-------
...Handler.java => OperandPropertiesProvider.java} | 36 +++-------
.../operation/SupportedOperationsProvider.java | 54 ++++++++++++++
.../UpdateConfigurationOperationHandler.java | 10 ++-
.../c2/client/service/C2HeartbeatFactoryTest.java | 64 +++++++++--------
.../client/service/ManifestHashProviderTest.java | 65 +++++++++++++++++
.../service/operation/C2OperationServiceTest.java | 46 +++++++++---
.../operation/DebugOperationHandlerTest.java | 16 +++--
.../DescribeManifestOperationHandlerTest.java | 6 +-
.../EmptyOperandPropertiesProviderTest.java} | 39 +++-------
.../operation/SupportedOperationsProviderTest.java | 71 +++++++++++++++++++
.../UpdateConfigurationOperationHandlerTest.java | 16 +++--
.../apache/nifi/c2/protocol/api/AgentManifest.java | 82 ++++++++++++++++++++++
.../nifi/c2/protocol/api/SupportedOperation.java | 76 ++++++++++++++++++++
.../main/markdown/minifi-java-agent-quick-start.md | 15 ++++
.../org/apache/nifi/c2/C2NifiClientService.java | 33 +++++----
23 files changed, 666 insertions(+), 190 deletions(-)
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2ClientService.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2ClientService.java
index c9424c67c2..67d406086e 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2ClientService.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2ClientService.java
@@ -41,8 +41,12 @@ public class C2ClientService {
}
public void sendHeartbeat(RuntimeInfoWrapper runtimeInfoWrapper) {
- C2Heartbeat c2Heartbeat = c2HeartbeatFactory.create(runtimeInfoWrapper);
- client.publishHeartbeat(c2Heartbeat).ifPresent(this::processResponse);
+ try {
+ C2Heartbeat c2Heartbeat = c2HeartbeatFactory.create(runtimeInfoWrapper);
+ client.publishHeartbeat(c2Heartbeat).ifPresent(this::processResponse);
+ } catch (Exception e) {
+ logger.error("Failed to send/process heartbeat:", e);
+ }
}
private void processResponse(C2HeartbeatResponse response) {
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2HeartbeatFactory.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2HeartbeatFactory.java
index 063017ea5a..92d85720d0 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2HeartbeatFactory.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/C2HeartbeatFactory.java
@@ -23,12 +23,9 @@ import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.net.InetAddress;
import java.net.NetworkInterface;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -37,6 +34,7 @@ import org.apache.nifi.c2.client.C2ClientConfig;
import org.apache.nifi.c2.client.PersistentUuidGenerator;
import org.apache.nifi.c2.client.service.model.RuntimeInfoWrapper;
import org.apache.nifi.c2.protocol.api.AgentInfo;
+import org.apache.nifi.c2.protocol.api.AgentManifest;
import org.apache.nifi.c2.protocol.api.AgentRepositories;
import org.apache.nifi.c2.protocol.api.AgentStatus;
import org.apache.nifi.c2.protocol.api.C2Heartbeat;
@@ -44,8 +42,8 @@ import org.apache.nifi.c2.protocol.api.DeviceInfo;
import org.apache.nifi.c2.protocol.api.FlowInfo;
import org.apache.nifi.c2.protocol.api.FlowQueueStatus;
import org.apache.nifi.c2.protocol.api.NetworkInfo;
+import org.apache.nifi.c2.protocol.api.SupportedOperation;
import org.apache.nifi.c2.protocol.api.SystemInfo;
-import org.apache.nifi.c2.protocol.component.api.Bundle;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,14 +57,16 @@ public class C2HeartbeatFactory {
private final C2ClientConfig clientConfig;
private final FlowIdHolder flowIdHolder;
+ private final ManifestHashProvider manifestHashProvider;
private String agentId;
private String deviceId;
private File confDirectory;
- public C2HeartbeatFactory(C2ClientConfig clientConfig, FlowIdHolder flowIdHolder) {
+ public C2HeartbeatFactory(C2ClientConfig clientConfig, FlowIdHolder flowIdHolder, ManifestHashProvider manifestHashProvider) {
this.clientConfig = clientConfig;
this.flowIdHolder = flowIdHolder;
+ this.manifestHashProvider = manifestHashProvider;
}
public C2Heartbeat create(RuntimeInfoWrapper runtimeInfoWrapper) {
@@ -97,7 +97,7 @@ public class C2HeartbeatFactory {
agentStatus.setRepositories(repos);
agentInfo.setStatus(agentStatus);
- agentInfo.setAgentManifestHash(calculateManifestHash(manifest.getBundles()));
+ agentInfo.setAgentManifestHash(manifestHashProvider.calculateManifestHash(manifest.getBundles(), getSupportedOperations(manifest)));
if (clientConfig.isFullHeartbeat()) {
agentInfo.setAgentManifest(manifest);
@@ -106,6 +106,17 @@ public class C2HeartbeatFactory {
return agentInfo;
}
+ private Set<SupportedOperation> getSupportedOperations(RuntimeManifest manifest) {
+ Set<SupportedOperation> supportedOperations;
+ // supported operations has value only in case of minifi, therefore we return empty collection if
+ if (manifest instanceof AgentManifest) {
+ supportedOperations = ((AgentManifest) manifest).getSupportedOperations();
+ } else {
+ supportedOperations = Collections.emptySet();
+ }
+ return supportedOperations;
+ }
+
private String getAgentId() {
if (agentId == null) {
String rawAgentId = clientConfig.getAgentIdentifier();
@@ -235,26 +246,4 @@ public class C2HeartbeatFactory {
return confDirectory;
}
- private String calculateManifestHash(List<Bundle> loadedBundles) {
- byte[] bytes;
- try {
- bytes = MessageDigest.getInstance("SHA-512").digest(loadedBundles.stream()
- .map(bundle -> bundle.getGroup() + bundle.getArtifact() + bundle.getVersion())
- .sorted()
- .collect(Collectors.joining(","))
- .getBytes(StandardCharsets.UTF_8));
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("Unable to set up manifest hash calculation due to not having support for the chosen digest algorithm", e);
- }
-
- return bytesToHex(bytes);
- }
-
- private String bytesToHex(byte[] in) {
- final StringBuilder builder = new StringBuilder();
- for (byte b : in) {
- builder.append(String.format("%02x", b));
- }
- return builder.toString();
- }
}
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/ManifestHashProvider.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/ManifestHashProvider.java
new file mode 100644
index 0000000000..4def4970fb
--- /dev/null
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/ManifestHashProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.c2.client.service;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.nifi.c2.protocol.api.SupportedOperation;
+import org.apache.nifi.c2.protocol.component.api.Bundle;
+
+public class ManifestHashProvider {
+ private String currentBundles = null;
+ private Set<SupportedOperation> currentSupportedOperations = Collections.emptySet();
+ private int currentHashCode;
+ private String currentManifestHash;
+
+ public String calculateManifestHash(List<Bundle> loadedBundles, Set<SupportedOperation> supportedOperations) {
+ String bundleString = loadedBundles.stream()
+ .map(bundle -> bundle.getGroup() + bundle.getArtifact() + bundle.getVersion())
+ .sorted()
+ .collect(Collectors.joining(","));
+ int hashCode = Objects.hash(bundleString, supportedOperations);
+ if (hashCode != currentHashCode
+ || !(Objects.equals(bundleString, currentBundles) && Objects.equals(supportedOperations, currentSupportedOperations))) {
+ byte[] bytes;
+ try {
+ bytes = MessageDigest.getInstance("SHA-512").digest(getBytes(supportedOperations, bundleString));
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Unable to set up manifest hash calculation due to not having support for the chosen digest algorithm", e);
+ }
+ currentHashCode = hashCode;
+ currentManifestHash = bytesToHex(bytes);
+ currentBundles = bundleString;
+ currentSupportedOperations = supportedOperations;
+ }
+ return currentManifestHash;
+ }
+
+ private byte[] getBytes(Set<SupportedOperation> supportedOperations, String bundleString) {
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.write(bundleString.getBytes(StandardCharsets.UTF_8));
+ oos.writeObject(supportedOperations);
+ oos.flush();
+ return bos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to transform supportedOperations and bundles to byte array", e);
+ }
+ }
+
+ private String bytesToHex(byte[] in) {
+ final StringBuilder builder = new StringBuilder();
+ for (byte b : in) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
+}
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
index e9f2db29fb..586f0e624a 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.c2.client.service.operation;
+import java.util.Map;
import org.apache.nifi.c2.protocol.api.C2Operation;
import org.apache.nifi.c2.protocol.api.C2OperationAck;
import org.apache.nifi.c2.protocol.api.OperandType;
@@ -40,6 +41,13 @@ public interface C2OperationHandler {
*/
OperandType getOperandType();
+ /**
+ * Returns the properties context for the given operand
+ *
+ * @return the property map
+ */
+ Map<String, Object> getProperties();
+
/**
* Handler logic for the specific C2Operation
*
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationService.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationService.java
index 5ce9c51ef4..31e483929d 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationService.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationService.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.c2.client.service.operation;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -47,6 +48,19 @@ public class C2OperationService {
});
}
+ public Map<OperationType, Map<OperandType, C2OperationHandler>> getHandlers() {
+ Map<OperationType, Map<OperandType, C2OperationHandler>> handlers = new HashMap<>();
+ handlerMap.entrySet()
+ .forEach(operationEntry -> {
+ Map<OperandType, C2OperationHandler> operands = new HashMap<>();
+ operationEntry.getValue()
+ .entrySet()
+ .forEach(o -> operands.put(o.getKey(), o.getValue()));
+ handlers.put(operationEntry.getKey(), Collections.unmodifiableMap(operands));
+ });
+ return Collections.unmodifiableMap(handlers);
+ }
+
private Optional<C2OperationHandler> getHandlerForOperation(C2Operation operation) {
return Optional.ofNullable(handlerMap.get(operation.getOperation()))
.map(operandMap -> operandMap.get(operation.getOperand()));
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandler.java
index 4daa369e69..33ec839c31 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandler.java
@@ -39,6 +39,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -69,14 +70,18 @@ public class DebugOperationHandler implements C2OperationHandler {
private final C2Client c2Client;
private final List<Path> bundleFilePaths;
private final Predicate<String> contentFilter;
+ private final OperandPropertiesProvider operandPropertiesProvider;
- private DebugOperationHandler(C2Client c2Client, List<Path> bundleFilePaths, Predicate<String> contentFilter) {
+ private DebugOperationHandler(C2Client c2Client, List<Path> bundleFilePaths, Predicate<String> contentFilter,
+ OperandPropertiesProvider operandPropertiesProvider) {
this.c2Client = c2Client;
this.bundleFilePaths = bundleFilePaths;
this.contentFilter = contentFilter;
+ this.operandPropertiesProvider = operandPropertiesProvider;
}
- public static DebugOperationHandler create(C2Client c2Client, List<Path> bundleFilePaths, Predicate<String> contentFilter) {
+ public static DebugOperationHandler create(C2Client c2Client, List<Path> bundleFilePaths, Predicate<String> contentFilter,
+ OperandPropertiesProvider operandPropertiesProvider) {
if (c2Client == null) {
throw new IllegalArgumentException("C2Client should not be null");
}
@@ -87,7 +92,7 @@ public class DebugOperationHandler implements C2OperationHandler {
throw new IllegalArgumentException("Content filter should not be null");
}
- return new DebugOperationHandler(c2Client, bundleFilePaths, contentFilter);
+ return new DebugOperationHandler(c2Client, bundleFilePaths, contentFilter, operandPropertiesProvider);
}
@Override
@@ -100,6 +105,11 @@ public class DebugOperationHandler implements C2OperationHandler {
return DEBUG;
}
+ @Override
+ public Map<String, Object> getProperties() {
+ return operandPropertiesProvider.getProperties();
+ }
+
@Override
public C2OperationAck handle(C2Operation operation) {
String debugCallbackUrl = operation.getArgs().get(TARGET_ARG);
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandler.java
index 2f55b2510d..1045b4acf1 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandler.java
@@ -20,6 +20,7 @@ import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.nifi.c2.protocol.api.OperandType.MANIFEST;
import static org.apache.nifi.c2.protocol.api.OperationType.DESCRIBE;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.nifi.c2.client.service.C2HeartbeatFactory;
@@ -31,19 +32,18 @@ import org.apache.nifi.c2.protocol.api.C2OperationAck;
import org.apache.nifi.c2.protocol.api.C2OperationState;
import org.apache.nifi.c2.protocol.api.OperandType;
import org.apache.nifi.c2.protocol.api.OperationType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class DescribeManifestOperationHandler implements C2OperationHandler {
- private static final Logger logger = LoggerFactory.getLogger(DescribeManifestOperationHandler.class);
-
private final C2HeartbeatFactory heartbeatFactory;
private final Supplier<RuntimeInfoWrapper> runtimeInfoSupplier;
+ private final OperandPropertiesProvider operandPropertiesProvider;
- public DescribeManifestOperationHandler(C2HeartbeatFactory heartbeatFactory, Supplier<RuntimeInfoWrapper> runtimeInfoSupplier) {
+ public DescribeManifestOperationHandler(C2HeartbeatFactory heartbeatFactory, Supplier<RuntimeInfoWrapper> runtimeInfoSupplier,
+ OperandPropertiesProvider operandPropertiesProvider) {
this.heartbeatFactory = heartbeatFactory;
this.runtimeInfoSupplier = runtimeInfoSupplier;
+ this.operandPropertiesProvider = operandPropertiesProvider;
}
@Override
@@ -78,4 +78,9 @@ public class DescribeManifestOperationHandler implements C2OperationHandler {
return operationAck;
}
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return operandPropertiesProvider.getProperties();
+ }
}
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/EmptyOperandPropertiesProvider.java
similarity index 50%
copy from c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
copy to c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/EmptyOperandPropertiesProvider.java
index e9f2db29fb..b9bcef1686 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/EmptyOperandPropertiesProvider.java
@@ -14,37 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.nifi.c2.client.service.operation;
-import org.apache.nifi.c2.protocol.api.C2Operation;
-import org.apache.nifi.c2.protocol.api.C2OperationAck;
-import org.apache.nifi.c2.protocol.api.OperandType;
-import org.apache.nifi.c2.protocol.api.OperationType;
+package org.apache.nifi.c2.client.service.operation;
-/**
- * Handler interface for the different operation types
- */
-public interface C2OperationHandler {
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
- /**
- * Returns the supported OperationType by the handler
- *
- * @return the type of the operation
- */
- OperationType getOperationType();
+public class EmptyOperandPropertiesProvider implements OperandPropertiesProvider {
- /**
- * Returns the supported OperandType by the handler
- *
- * @return the type of the operand
- */
- OperandType getOperandType();
+ private static final Map<String, Object> EMPTY_MAP = Collections.unmodifiableMap(new HashMap<>());
- /**
- * Handler logic for the specific C2Operation
- *
- * @param operation the C2Operation to be handled
- * @return the result of the operation handling
- */
- C2OperationAck handle(C2Operation operation);
+ @Override
+ public Map<String, Object> getProperties() {
+ return EMPTY_MAP;
+ }
}
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/OperandPropertiesProvider.java
similarity index 51%
copy from c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
copy to c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/OperandPropertiesProvider.java
index e9f2db29fb..5bb853c681 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/OperandPropertiesProvider.java
@@ -14,37 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.nifi.c2.client.service.operation;
-import org.apache.nifi.c2.protocol.api.C2Operation;
-import org.apache.nifi.c2.protocol.api.C2OperationAck;
-import org.apache.nifi.c2.protocol.api.OperandType;
-import org.apache.nifi.c2.protocol.api.OperationType;
+import java.util.Map;
/**
- * Handler interface for the different operation types
- */
-public interface C2OperationHandler {
-
- /**
- * Returns the supported OperationType by the handler
- *
- * @return the type of the operation
- */
- OperationType getOperationType();
-
- /**
- * Returns the supported OperandType by the handler
- *
- * @return the type of the operand
- */
- OperandType getOperandType();
+ * Common interface to provide properties for different operands.
+ * */
+public interface OperandPropertiesProvider {
/**
- * Handler logic for the specific C2Operation
+ * Get the properties for the given operand.
+ * The Value of the Map must implement the Serializable interface.
*
- * @param operation the C2Operation to be handled
- * @return the result of the operation handling
- */
- C2OperationAck handle(C2Operation operation);
+ * @return the properties of the given operand
+ * */
+ Map<String, Object> getProperties();
}
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/SupportedOperationsProvider.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/SupportedOperationsProvider.java
new file mode 100644
index 0000000000..d60a9fee1a
--- /dev/null
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/SupportedOperationsProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.c2.client.service.operation;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.nifi.c2.protocol.api.OperandType;
+import org.apache.nifi.c2.protocol.api.OperationType;
+import org.apache.nifi.c2.protocol.api.SupportedOperation;
+
+public class SupportedOperationsProvider {
+ private final Map<OperationType, Map<OperandType, C2OperationHandler>> operationHandlers;
+
+ public SupportedOperationsProvider(Map<OperationType, Map<OperandType, C2OperationHandler>> handlers) {
+ operationHandlers = handlers;
+ }
+
+ public Set<SupportedOperation> getSupportedOperations() {
+ return operationHandlers.entrySet()
+ .stream()
+ .map(operationEntry -> getSupportedOperation(operationEntry.getKey(), operationEntry.getValue()))
+ .collect(Collectors.toSet());
+ }
+
+ private SupportedOperation getSupportedOperation(OperationType operationType, Map<OperandType, C2OperationHandler> operands) {
+ SupportedOperation supportedOperation = new SupportedOperation();
+ supportedOperation.setType(operationType);
+
+ Map<OperandType, Map<String, Object>> properties = operands.values()
+ .stream()
+ .collect(Collectors.toMap(C2OperationHandler::getOperandType, C2OperationHandler::getProperties));
+
+ supportedOperation.setProperties(properties);
+
+ return supportedOperation;
+ }
+
+}
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandler.java
index dc1d15f610..f04af88714 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandler.java
@@ -45,11 +45,14 @@ public class UpdateConfigurationOperationHandler implements C2OperationHandler {
private final C2Client client;
private final Function<byte[], Boolean> updateFlow;
private final FlowIdHolder flowIdHolder;
+ private final OperandPropertiesProvider operandPropertiesProvider;
- public UpdateConfigurationOperationHandler(C2Client client, FlowIdHolder flowIdHolder, Function<byte[], Boolean> updateFlow) {
+ public UpdateConfigurationOperationHandler(C2Client client, FlowIdHolder flowIdHolder, Function<byte[], Boolean> updateFlow,
+ OperandPropertiesProvider operandPropertiesProvider) {
this.client = client;
this.updateFlow = updateFlow;
this.flowIdHolder = flowIdHolder;
+ this.operandPropertiesProvider = operandPropertiesProvider;
}
@Override
@@ -128,4 +131,9 @@ public class UpdateConfigurationOperationHandler implements C2OperationHandler {
}
return null;
}
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return operandPropertiesProvider.getProperties();
+ }
}
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/C2HeartbeatFactoryTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/C2HeartbeatFactoryTest.java
index c1c8efb86d..ccc8a01e47 100644
--- a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/C2HeartbeatFactoryTest.java
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/C2HeartbeatFactoryTest.java
@@ -17,7 +17,6 @@
package org.apache.nifi.c2.client.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -26,28 +25,34 @@ import static org.mockito.Mockito.when;
import java.io.File;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import org.apache.nifi.c2.client.C2ClientConfig;
import org.apache.nifi.c2.client.service.model.RuntimeInfoWrapper;
+import org.apache.nifi.c2.protocol.api.AgentManifest;
import org.apache.nifi.c2.protocol.api.AgentRepositories;
import org.apache.nifi.c2.protocol.api.C2Heartbeat;
import org.apache.nifi.c2.protocol.api.FlowQueueStatus;
+import org.apache.nifi.c2.protocol.api.OperationType;
+import org.apache.nifi.c2.protocol.api.SupportedOperation;
import org.apache.nifi.c2.protocol.component.api.Bundle;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
-@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
public class C2HeartbeatFactoryTest {
private static final String AGENT_CLASS = "agentClass";
private static final String FLOW_ID = "flowId";
+ private static final String MANIFEST_HASH = "hash";
@Mock
private C2ClientConfig clientConfig;
@@ -58,6 +63,9 @@ public class C2HeartbeatFactoryTest {
@Mock
private RuntimeInfoWrapper runtimeInfoWrapper;
+ @Mock
+ private ManifestHashProvider manifestHashProvider;
+
@InjectMocks
private C2HeartbeatFactory c2HeartbeatFactory;
@@ -129,33 +137,27 @@ public class C2HeartbeatFactoryTest {
}
@Test
- void testManifestHashChangesWhenManifestBundleChanges() {
- Bundle bundle1 = new Bundle("group1", "artifact1", "version1");
- Bundle bundle2 = new Bundle("group2", "artifact2", "version2");
- RuntimeManifest manifest1 = createManifest(bundle1);
- RuntimeManifest manifest2 = createManifest(bundle2);
- RuntimeManifest manifest3 = createManifest(bundle1, bundle2);
-
- when(runtimeInfoWrapper.getManifest()).thenReturn(manifest1);
- C2Heartbeat heartbeat1 = c2HeartbeatFactory.create(runtimeInfoWrapper);
- String hash1 = heartbeat1.getAgentInfo().getAgentManifestHash();
- assertNotNull(hash1);
-
- // same manifest should result in the same hash
- assertEquals(hash1, c2HeartbeatFactory.create(runtimeInfoWrapper).getAgentInfo().getAgentManifestHash());
-
- // different manifest should result in hash change
- when(runtimeInfoWrapper.getManifest()).thenReturn(manifest2);
- C2Heartbeat heartbeat2 = c2HeartbeatFactory.create(runtimeInfoWrapper);
- String hash2 = heartbeat2.getAgentInfo().getAgentManifestHash();
- assertNotEquals(hash2, hash1);
-
- // different manifest with multiple bundles should result in hash change compared to all previous
- when(runtimeInfoWrapper.getManifest()).thenReturn(manifest3);
- C2Heartbeat heartbeat3 = c2HeartbeatFactory.create(runtimeInfoWrapper);
- String hash3 = heartbeat3.getAgentInfo().getAgentManifestHash();
- assertNotEquals(hash3, hash1);
- assertNotEquals(hash3, hash2);
+ void testAgentManifestHashIsPopulatedInCaseOfRuntimeManifest() {
+ RuntimeManifest manifest = createManifest();
+ when(manifestHashProvider.calculateManifestHash(manifest.getBundles(), Collections.emptySet())).thenReturn(MANIFEST_HASH);
+
+ C2Heartbeat heartbeat = c2HeartbeatFactory.create(new RuntimeInfoWrapper(new AgentRepositories(), manifest, new HashMap<>()));
+
+ assertEquals(MANIFEST_HASH, heartbeat.getAgentInfo().getAgentManifestHash());
+ }
+
+ @Test
+ void testAgentManifestHashIsPopulatedInCaseOfAgentManifest() {
+ AgentManifest manifest = new AgentManifest(createManifest());
+ SupportedOperation supportedOperation = new SupportedOperation();
+ supportedOperation.setType(OperationType.HEARTBEAT);
+ Set<SupportedOperation> supportedOperations = Collections.singleton(supportedOperation);
+ manifest.setSupportedOperations(supportedOperations);
+ when(manifestHashProvider.calculateManifestHash(manifest.getBundles(), supportedOperations)).thenReturn(MANIFEST_HASH);
+
+ C2Heartbeat heartbeat = c2HeartbeatFactory.create(new RuntimeInfoWrapper(new AgentRepositories(), manifest, new HashMap<>()));
+
+ assertEquals(MANIFEST_HASH, heartbeat.getAgentInfo().getAgentManifestHash());
}
private RuntimeManifest createManifest() {
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/ManifestHashProviderTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/ManifestHashProviderTest.java
new file mode 100644
index 0000000000..182423603c
--- /dev/null
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/ManifestHashProviderTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.c2.client.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Arrays;
+import java.util.Collections;
+import org.apache.nifi.c2.protocol.api.OperationType;
+import org.apache.nifi.c2.protocol.api.SupportedOperation;
+import org.apache.nifi.c2.protocol.component.api.Bundle;
+import org.junit.jupiter.api.Test;
+
+class ManifestHashProviderTest {
+ private ManifestHashProvider manifestHashProvider = new ManifestHashProvider();
+
+ @Test
+ void testManifestHashChangesWhenManifestBundleChanges() {
+ Bundle bundle1 = new Bundle("group1", "artifact1", "version1");
+ Bundle bundle2 = new Bundle("group2", "artifact2", "version2");
+
+ SupportedOperation supportedOperation1 = new SupportedOperation();
+ supportedOperation1.setType(OperationType.HEARTBEAT);
+ SupportedOperation supportedOperation2 = new SupportedOperation();
+ supportedOperation2.setType(OperationType.ACKNOWLEDGE);
+
+ String hash1 = manifestHashProvider.calculateManifestHash(Collections.singletonList(bundle1), Collections.singleton(supportedOperation1));
+ assertNotNull(hash1);
+
+ // same manifest should result in the same hash
+ assertEquals(hash1, manifestHashProvider.calculateManifestHash(Collections.singletonList(bundle1), Collections.singleton(supportedOperation1)));
+
+ // different manifest should result in hash change if only bundle change
+ String hash2 = manifestHashProvider.calculateManifestHash(Collections.singletonList(bundle2), Collections.singleton(supportedOperation1));
+ assertNotEquals(hash2, hash1);
+
+ // different manifest should result in hash change if only supported operation change
+ String hash3 = manifestHashProvider.calculateManifestHash(Collections.singletonList(bundle1), Collections.singleton(supportedOperation2));
+ assertNotEquals(hash3, hash1);
+
+ // different manifest with multiple bundles should result in hash change compared to all previous
+ String hash4 = manifestHashProvider.calculateManifestHash(Arrays.asList(bundle1, bundle2), Collections.singleton(supportedOperation1));
+
+ assertNotEquals(hash4, hash1);
+ assertNotEquals(hash4, hash2);
+ assertNotEquals(hash4, hash3);
+ }
+}
\ No newline at end of file
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/C2OperationServiceTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/C2OperationServiceTest.java
index 0249369e27..d821dd0738 100644
--- a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/C2OperationServiceTest.java
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/C2OperationServiceTest.java
@@ -16,11 +16,16 @@
*/
package org.apache.nifi.c2.client.service.operation;
+import static org.apache.nifi.c2.protocol.api.OperandType.CONFIGURATION;
+import static org.apache.nifi.c2.protocol.api.OperandType.MANIFEST;
+import static org.apache.nifi.c2.protocol.api.OperationType.DESCRIBE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.Map;
import java.util.Optional;
import org.apache.nifi.c2.protocol.api.C2Operation;
import org.apache.nifi.c2.protocol.api.C2OperationAck;
@@ -45,7 +50,7 @@ public class C2OperationServiceTest {
C2Operation operation = new C2Operation();
operation.setOperation(OperationType.UPDATE);
- operation.setOperand(OperandType.CONFIGURATION);
+ operation.setOperand(CONFIGURATION);
Optional<C2OperationAck> ack = service.handleOperation(operation);
assertFalse(ack.isPresent());
@@ -56,8 +61,8 @@ public class C2OperationServiceTest {
C2OperationService service = new C2OperationService(Collections.singletonList(new TestDescribeOperationHandler()));
C2Operation operation = new C2Operation();
- operation.setOperation(OperationType.DESCRIBE);
- operation.setOperand(OperandType.MANIFEST);
+ operation.setOperation(DESCRIBE);
+ operation.setOperand(MANIFEST);
Optional<C2OperationAck> ack = service.handleOperation(operation);
assertTrue(ack.isPresent());
@@ -69,23 +74,41 @@ public class C2OperationServiceTest {
C2OperationService service = new C2OperationService(Collections.singletonList(new TestInvalidOperationHandler()));
C2Operation operation = new C2Operation();
- operation.setOperation(OperationType.DESCRIBE);
- operation.setOperand(OperandType.MANIFEST);
+ operation.setOperation(DESCRIBE);
+ operation.setOperand(MANIFEST);
Optional<C2OperationAck> ack = service.handleOperation(operation);
assertFalse(ack.isPresent());
}
+ @Test
+ void testHandlersAreReturned() {
+ C2OperationService service = new C2OperationService(Arrays.asList(new TestDescribeOperationHandler(), new TestInvalidOperationHandler()));
+
+ Map<OperationType, Map<OperandType, C2OperationHandler>> handlers = service.getHandlers();
+
+ assertEquals(1, handlers.keySet().size());
+ assertTrue(handlers.keySet().contains(DESCRIBE));
+ Map<OperandType, C2OperationHandler> operands = handlers.values().stream().findFirst().get();
+ assertEquals(2, operands.size());
+ assertTrue(operands.keySet().containsAll(Arrays.asList(MANIFEST, CONFIGURATION)));
+ }
+
private static class TestDescribeOperationHandler implements C2OperationHandler {
@Override
public OperationType getOperationType() {
- return OperationType.DESCRIBE;
+ return DESCRIBE;
}
@Override
public OperandType getOperandType() {
- return OperandType.MANIFEST;
+ return MANIFEST;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return Collections.emptyMap();
}
@Override
@@ -98,12 +121,17 @@ public class C2OperationServiceTest {
@Override
public OperationType getOperationType() {
- return OperationType.DESCRIBE;
+ return DESCRIBE;
}
@Override
public OperandType getOperandType() {
- return OperandType.CONFIGURATION;
+ return CONFIGURATION;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return Collections.emptyMap();
}
@Override
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandlerTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandlerTest.java
index c28baee247..31ff4cf35b 100644
--- a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandlerTest.java
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DebugOperationHandlerTest.java
@@ -79,6 +79,9 @@ public class DebugOperationHandlerTest {
@Mock
private C2Client c2Client;
+ @Mock
+ private OperandPropertiesProvider operandPropertiesProvider;
+
@TempDir
private File tempDir;
@@ -96,13 +99,13 @@ public class DebugOperationHandlerTest {
@ParameterizedTest(name = "c2Client={0} bundleFileList={1} contentFilter={2}")
@MethodSource("invalidConstructorArguments")
public void testAttemptingCreateWithInvalidParametersWillThrowException(C2Client c2Client, List<Path> bundleFilePaths, Predicate<String> contentFilter) {
- assertThrows(IllegalArgumentException.class, () -> DebugOperationHandler.create(c2Client, bundleFilePaths, contentFilter));
+ assertThrows(IllegalArgumentException.class, () -> DebugOperationHandler.create(c2Client, bundleFilePaths, contentFilter, operandPropertiesProvider));
}
@Test
public void testOperationAndOperandTypesAreMatching() {
// given
- DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, VALID_BUNDLE_FILE_LIST, DEFAULT_CONTENT_FILTER);
+ DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, VALID_BUNDLE_FILE_LIST, DEFAULT_CONTENT_FILTER, operandPropertiesProvider);
// when + then
assertEquals(TRANSFER, testHandler.getOperationType());
@@ -112,7 +115,7 @@ public class DebugOperationHandlerTest {
@Test
public void testC2CallbackUrlIsNullInArgs() {
// given
- DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, VALID_BUNDLE_FILE_LIST, DEFAULT_CONTENT_FILTER);
+ DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, VALID_BUNDLE_FILE_LIST, DEFAULT_CONTENT_FILTER, operandPropertiesProvider);
C2Operation c2Operation = operation(null);
// when
@@ -131,7 +134,7 @@ public class DebugOperationHandlerTest {
List<Path> createBundleFiles = bundleFileNamesWithContents.entrySet().stream()
.map(entry -> placeFileWithContent(entry.getKey(), entry.getValue()))
.collect(toList());
- DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, createBundleFiles, DEFAULT_CONTENT_FILTER);
+ DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, createBundleFiles, DEFAULT_CONTENT_FILTER, operandPropertiesProvider);
C2Operation c2Operation = operation(C2_DEBUG_UPLOAD_ENDPOINT);
// when
@@ -151,7 +154,8 @@ public class DebugOperationHandlerTest {
@Test
public void testFileToCollectDoesNotExist() {
// given
- DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, singletonList(Paths.get(tempDir.getAbsolutePath(), "missing_file")), DEFAULT_CONTENT_FILTER);
+ DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, singletonList(Paths.get(tempDir.getAbsolutePath(), "missing_file")),
+ DEFAULT_CONTENT_FILTER, operandPropertiesProvider);
C2Operation c2Operation = operation(C2_DEBUG_UPLOAD_ENDPOINT);
// when
@@ -190,7 +194,7 @@ public class DebugOperationHandlerTest {
// given
Path bundleFile = placeFileWithContent(fileName, inputContent);
Predicate<String> testContentFilter = content -> !content.contains(filterKeyword);
- DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, singletonList(bundleFile), testContentFilter);
+ DebugOperationHandler testHandler = DebugOperationHandler.create(c2Client, singletonList(bundleFile), testContentFilter, operandPropertiesProvider);
C2Operation c2Operation = operation(C2_DEBUG_UPLOAD_ENDPOINT);
// when
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandlerTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandlerTest.java
index 2017cc18a5..f4508e7fed 100644
--- a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandlerTest.java
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/DescribeManifestOperationHandlerTest.java
@@ -43,10 +43,12 @@ public class DescribeManifestOperationHandlerTest {
@Mock
private C2HeartbeatFactory heartbeatFactory;
+ @Mock
+ private OperandPropertiesProvider operandPropertiesProvider;
@Test
void testDescribeManifestOperationHandlerCreateSuccess() {
- DescribeManifestOperationHandler handler = new DescribeManifestOperationHandler(null, null);
+ DescribeManifestOperationHandler handler = new DescribeManifestOperationHandler(null, null, operandPropertiesProvider);
assertEquals(OperationType.DESCRIBE, handler.getOperationType());
assertEquals(OperandType.MANIFEST, handler.getOperandType());
@@ -67,7 +69,7 @@ public class DescribeManifestOperationHandlerTest {
heartbeat.setFlowInfo(flowInfo);
when(heartbeatFactory.create(runtimeInfoWrapper)).thenReturn(heartbeat);
- DescribeManifestOperationHandler handler = new DescribeManifestOperationHandler(heartbeatFactory, () -> runtimeInfoWrapper);
+ DescribeManifestOperationHandler handler = new DescribeManifestOperationHandler(heartbeatFactory, () -> runtimeInfoWrapper, operandPropertiesProvider);
C2Operation operation = new C2Operation();
operation.setIdentifier(OPERATION_ID);
diff --git a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/EmptyOperandPropertiesProviderTest.java
similarity index 50%
copy from c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
copy to c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/EmptyOperandPropertiesProviderTest.java
index e9f2db29fb..9569838b7f 100644
--- a/c2/c2-client-bundle/c2-client-service/src/main/java/org/apache/nifi/c2/client/service/operation/C2OperationHandler.java
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/EmptyOperandPropertiesProviderTest.java
@@ -14,37 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.nifi.c2.client.service.operation;
-import org.apache.nifi.c2.protocol.api.C2Operation;
-import org.apache.nifi.c2.protocol.api.C2OperationAck;
-import org.apache.nifi.c2.protocol.api.OperandType;
-import org.apache.nifi.c2.protocol.api.OperationType;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-/**
- * Handler interface for the different operation types
- */
-public interface C2OperationHandler {
+import java.util.Collections;
+import org.junit.jupiter.api.Test;
- /**
- * Returns the supported OperationType by the handler
- *
- * @return the type of the operation
- */
- OperationType getOperationType();
+class EmptyOperandPropertiesProviderTest {
- /**
- * Returns the supported OperandType by the handler
- *
- * @return the type of the operand
- */
- OperandType getOperandType();
+ private final OperandPropertiesProvider operandPropertiesProvider = new EmptyOperandPropertiesProvider();
- /**
- * Handler logic for the specific C2Operation
- *
- * @param operation the C2Operation to be handled
- * @return the result of the operation handling
- */
- C2OperationAck handle(C2Operation operation);
-}
+ @Test
+ void testEmptyMapReturn() {
+ assertEquals(Collections.emptyMap(), operandPropertiesProvider.getProperties());
+ }
+}
\ No newline at end of file
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/SupportedOperationsProviderTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/SupportedOperationsProviderTest.java
new file mode 100644
index 0000000000..12d0a1b4ae
--- /dev/null
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/SupportedOperationsProviderTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.c2.client.service.operation;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.nifi.c2.protocol.api.OperandType;
+import org.apache.nifi.c2.protocol.api.OperationType;
+import org.apache.nifi.c2.protocol.api.SupportedOperation;
+import org.junit.jupiter.api.Test;
+
+class SupportedOperationsProviderTest {
+
+ @Test
+ void testSupportedOperationsAreProvided() {
+ C2OperationHandler describeManifestOperationHandler = mock(C2OperationHandler.class);
+ C2OperationHandler describeConfigurationOperationHandler = mock(C2OperationHandler.class);
+ Map<String, Object> describeManifestProperties = Collections.singletonMap("availableProperties", Arrays.asList("property1", "property2"));
+ Map<String, Object> describeConfigurationProperties = Collections.emptyMap();
+ when(describeManifestOperationHandler.getProperties()).thenReturn(describeManifestProperties);
+ when(describeManifestOperationHandler.getOperandType()).thenReturn(OperandType.MANIFEST);
+ when(describeConfigurationOperationHandler.getProperties()).thenReturn(describeConfigurationProperties);
+ when(describeConfigurationOperationHandler.getOperandType()).thenReturn(OperandType.CONFIGURATION);
+
+ Map<OperationType, Map<OperandType, C2OperationHandler>> operationHandlers = new HashMap<>();
+ operationHandlers.put(OperationType.PAUSE, Collections.emptyMap());
+ Map<OperandType, C2OperationHandler> operandHandlers = new HashMap<>();
+ operandHandlers.put(OperandType.MANIFEST, describeManifestOperationHandler);
+ operandHandlers.put(OperandType.CONFIGURATION, describeConfigurationOperationHandler);
+ operationHandlers.put(OperationType.DESCRIBE, operandHandlers);
+
+ SupportedOperationsProvider supportedOperationsProvider = new SupportedOperationsProvider(operationHandlers);
+
+ SupportedOperation pauseOperation = new SupportedOperation();
+ pauseOperation.setType(OperationType.PAUSE);
+ pauseOperation.setProperties(Collections.emptyMap());
+ SupportedOperation describeOperation = new SupportedOperation();
+ describeOperation.setType(OperationType.DESCRIBE);
+ Map<OperandType, Map<String, Object>> operands = new HashMap<>();
+ operands.put(OperandType.MANIFEST, describeManifestProperties);
+ operands.put(OperandType.CONFIGURATION, describeConfigurationProperties);
+ describeOperation.setProperties(operands);
+
+ Set<SupportedOperation> expected = new HashSet<>(Arrays.asList(pauseOperation, describeOperation));
+ assertEquals(expected, supportedOperationsProvider.getSupportedOperations());
+ }
+}
\ No newline at end of file
diff --git a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandlerTest.java b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandlerTest.java
index a37fc64998..11acc072dc 100644
--- a/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandlerTest.java
+++ b/c2/c2-client-bundle/c2-client-service/src/test/java/org/apache/nifi/c2/client/service/operation/UpdateConfigurationOperationHandlerTest.java
@@ -17,8 +17,8 @@
package org.apache.nifi.c2.client.service.operation;
import static org.apache.commons.lang3.StringUtils.EMPTY;
-import static org.apache.nifi.c2.client.service.operation.UpdateConfigurationOperationHandler.LOCATION;
import static org.apache.nifi.c2.client.service.operation.UpdateConfigurationOperationHandler.FLOW_ID;
+import static org.apache.nifi.c2.client.service.operation.UpdateConfigurationOperationHandler.LOCATION;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@@ -51,10 +51,12 @@ public class UpdateConfigurationOperationHandlerTest {
@Mock
private FlowIdHolder flowIdHolder;
+ @Mock
+ private OperandPropertiesProvider operandPropertiesProvider;
@Test
void testUpdateConfigurationOperationHandlerCreateSuccess() {
- UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(null, null, null);
+ UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(null, null, null, operandPropertiesProvider);
assertEquals(OperationType.UPDATE, handler.getOperationType());
assertEquals(OperandType.CONFIGURATION, handler.getOperandType());
@@ -62,7 +64,7 @@ public class UpdateConfigurationOperationHandlerTest {
@Test
void testHandleIncorrectArg() {
- UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(null, null, null);
+ UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(null, null, null, operandPropertiesProvider);
C2Operation operation = new C2Operation();
operation.setArgs(INCORRECT_LOCATION_MAP);
@@ -76,7 +78,7 @@ public class UpdateConfigurationOperationHandlerTest {
Function<byte[], Boolean> successUpdate = x -> true;
when(flowIdHolder.getFlowId()).thenReturn(FLOW_ID);
when(client.retrieveUpdateContent(any())).thenReturn(Optional.of("content".getBytes()));
- UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, successUpdate);
+ UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, successUpdate, operandPropertiesProvider);
C2Operation operation = new C2Operation();
operation.setIdentifier(OPERATION_ID);
@@ -95,7 +97,7 @@ public class UpdateConfigurationOperationHandlerTest {
void testHandleReturnsNotAppliedWithNoContent() {
when(flowIdHolder.getFlowId()).thenReturn(FLOW_ID);
when(client.retrieveUpdateContent(any())).thenReturn(Optional.empty());
- UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, null);
+ UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, null, operandPropertiesProvider);
C2Operation operation = new C2Operation();
operation.setArgs(CORRECT_LOCATION_MAP);
@@ -110,7 +112,7 @@ public class UpdateConfigurationOperationHandlerTest {
Function<byte[], Boolean> failedToUpdate = x -> false;
when(flowIdHolder.getFlowId()).thenReturn(FLOW_ID);
when(client.retrieveUpdateContent(any())).thenReturn(Optional.of("content".getBytes()));
- UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, failedToUpdate);
+ UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, failedToUpdate, operandPropertiesProvider);
C2Operation operation = new C2Operation();
operation.setIdentifier(OPERATION_ID);
operation.setArgs(CORRECT_LOCATION_MAP);
@@ -126,7 +128,7 @@ public class UpdateConfigurationOperationHandlerTest {
Function<byte[], Boolean> successUpdate = x -> true;
when(flowIdHolder.getFlowId()).thenReturn(FLOW_ID);
when(client.retrieveUpdateContent(any())).thenReturn(Optional.of("content".getBytes()));
- UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, successUpdate);
+ UpdateConfigurationOperationHandler handler = new UpdateConfigurationOperationHandler(client, flowIdHolder, successUpdate, operandPropertiesProvider);
C2Operation operation = new C2Operation();
operation.setIdentifier(OPERATION_ID);
operation.setArgs(CORRECT_LOCATION_MAP);
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java
new file mode 100644
index 0000000000..3263fd3889
--- /dev/null
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.c2.protocol.api;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
+
+@ApiModel
+public class AgentManifest extends RuntimeManifest {
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty("All supported operations by agent")
+ private Set<SupportedOperation> supportedOperations;
+
+ public AgentManifest() {
+ super();
+ }
+
+ public AgentManifest(RuntimeManifest manifest) {
+ super();
+ setAgentType(manifest.getAgentType());
+ setIdentifier(manifest.getIdentifier());
+ setBundles(manifest.getBundles());
+ setBuildInfo(manifest.getBuildInfo());
+ setSchedulingDefaults(manifest.getSchedulingDefaults());
+ setVersion(manifest.getVersion());
+ }
+
+ public Set<SupportedOperation> getSupportedOperations() {
+ return supportedOperations;
+ }
+
+ public void setSupportedOperations(Set<SupportedOperation> supportedOperations) {
+ this.supportedOperations = supportedOperations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ AgentManifest that = (AgentManifest) o;
+ return Objects.equals(supportedOperations, that.supportedOperations);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), supportedOperations);
+ }
+
+
+ @Override
+ public String toString() {
+ return "AgentManifest{" +
+ "supportedOperations=" + supportedOperations +
+ "}, " + super.toString();
+ }
+}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java
new file mode 100644
index 0000000000..ede22079a6
--- /dev/null
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.c2.protocol.api;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Objects;
+
+@ApiModel
+public class SupportedOperation implements Serializable {
+ private static final long serialVersionUID = 1;
+
+ @ApiModelProperty("The type of the operation supported by the agent")
+ private OperationType type;
+
+ @ApiModelProperty("Operand specific properties defined by the agent")
+ private Map<OperandType, Map<String, Object>> properties;
+
+ public OperationType getType() {
+ return type;
+ }
+
+ public void setType(OperationType type) {
+ this.type = type;
+ }
+
+ public Map<OperandType, Map<String, Object>> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<OperandType, Map<String, Object>> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SupportedOperation that = (SupportedOperation) o;
+ return type == that.type && Objects.equals(properties, that.properties);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, properties);
+ }
+
+ @Override
+ public String toString() {
+ return "SupportedOperation{" +
+ "type=" + type +
+ ", properties=" + properties +
+ '}';
+ }
+}
diff --git a/minifi/minifi-docs/src/main/markdown/minifi-java-agent-quick-start.md b/minifi/minifi-docs/src/main/markdown/minifi-java-agent-quick-start.md
index 3a24adf096..126a391634 100644
--- a/minifi/minifi-docs/src/main/markdown/minifi-java-agent-quick-start.md
+++ b/minifi/minifi-docs/src/main/markdown/minifi-java-agent-quick-start.md
@@ -152,6 +152,21 @@ To load a new dataflow for a MiNiFi instance to run:
1. Change the flow definition on the C2 Server
2. When a new flow is available on the C2 server, MiNiFi will download it via C2 and restart itself to pick up the changes
+## C2 Heartbeat
+Heartbeat provides status(agent, flowm device) and operational capabilities to C2 server(s)
+
+### Agent manifest
+The agent manifest is the descriptor of the available extensions. The size of the heartbeat
+might increase depending on the added extensions.
+
+With the `c2.full.heartbeat` parameter you can control whether to always include the manifest in the heartbeat or not.
+
+The `agentInfo.agentManifestHash` node can be used to detect in the C2 server whether the manifest changed compared to the previous heartbeat.
+
+If change is detected, a full heartbeat can be retrieved by sending a DESCRIBE MANIFEST Operation in the `requestedOperations` node of the C2 Heartbeat response.
+
+For more details about the C2 protocol please visit [Apache NiFi - MiNiFi C2 wiki page](https://cwiki.apache.org/confluence/display/MINIFI/C2).
+
## Using Processors Not Packaged with MiNiFi
MiNiFi is able to use the following processors out of the box:
* UpdateAttribute
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/c2/C2NifiClientService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/c2/C2NifiClientService.java
index 41db531ef8..0602a13f3e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/c2/C2NifiClientService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/c2/C2NifiClientService.java
@@ -45,11 +45,16 @@ import org.apache.nifi.c2.client.http.C2HttpClient;
import org.apache.nifi.c2.client.service.C2ClientService;
import org.apache.nifi.c2.client.service.C2HeartbeatFactory;
import org.apache.nifi.c2.client.service.FlowIdHolder;
+import org.apache.nifi.c2.client.service.ManifestHashProvider;
import org.apache.nifi.c2.client.service.model.RuntimeInfoWrapper;
import org.apache.nifi.c2.client.service.operation.C2OperationService;
import org.apache.nifi.c2.client.service.operation.DebugOperationHandler;
import org.apache.nifi.c2.client.service.operation.DescribeManifestOperationHandler;
+import org.apache.nifi.c2.client.service.operation.EmptyOperandPropertiesProvider;
+import org.apache.nifi.c2.client.service.operation.OperandPropertiesProvider;
+import org.apache.nifi.c2.client.service.operation.SupportedOperationsProvider;
import org.apache.nifi.c2.client.service.operation.UpdateConfigurationOperationHandler;
+import org.apache.nifi.c2.protocol.api.AgentManifest;
import org.apache.nifi.c2.protocol.api.AgentRepositories;
import org.apache.nifi.c2.protocol.api.AgentRepositoryStatus;
import org.apache.nifi.c2.protocol.api.FlowQueueStatus;
@@ -103,6 +108,8 @@ public class C2NifiClientService {
private final ExtensionManifestParser extensionManifestParser = new JAXBExtensionManifestParser();
private final RuntimeManifestService runtimeManifestService;
+
+ private final SupportedOperationsProvider supportedOperationsProvider;
private final long heartbeatPeriod;
public C2NifiClientService(final NiFiProperties niFiProperties, final FlowController flowController) {
@@ -118,16 +125,19 @@ public class C2NifiClientService {
this.heartbeatPeriod = clientConfig.getHeartbeatPeriod();
this.flowController = flowController;
C2HttpClient client = new C2HttpClient(clientConfig, new C2JacksonSerializer());
- C2HeartbeatFactory heartbeatFactory = new C2HeartbeatFactory(clientConfig, flowIdHolder);
+ C2HeartbeatFactory heartbeatFactory = new C2HeartbeatFactory(clientConfig, flowIdHolder, new ManifestHashProvider());
+ OperandPropertiesProvider emptyOperandPropertiesProvider = new EmptyOperandPropertiesProvider();
+ C2OperationService c2OperationService = new C2OperationService(Arrays.asList(
+ new UpdateConfigurationOperationHandler(client, flowIdHolder, this::updateFlowContent, emptyOperandPropertiesProvider),
+ new DescribeManifestOperationHandler(heartbeatFactory, this::generateRuntimeInfo, emptyOperandPropertiesProvider),
+ DebugOperationHandler.create(client, debugBundleFiles(niFiProperties), EXCLUDE_SENSITIVE_TEXT, emptyOperandPropertiesProvider)
+ ));
this.c2ClientService = new C2ClientService(
client,
heartbeatFactory,
- new C2OperationService(Arrays.asList(
- new UpdateConfigurationOperationHandler(client, flowIdHolder, this::updateFlowContent),
- new DescribeManifestOperationHandler(heartbeatFactory, this::generateRuntimeInfo),
- DebugOperationHandler.create(client, debugBundleFiles(niFiProperties), EXCLUDE_SENSITIVE_TEXT)
- ))
+ c2OperationService
);
+ this.supportedOperationsProvider = new SupportedOperationsProvider(c2OperationService.getHandlers());
}
private C2ClientConfig generateClientConfig(NiFiProperties properties) {
@@ -159,12 +169,7 @@ public class C2NifiClientService {
}
public void start() {
- try {
- scheduledExecutorService.scheduleAtFixedRate(() -> c2ClientService.sendHeartbeat(generateRuntimeInfo()), INITIAL_DELAY, heartbeatPeriod, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- logger.error("Could not start C2 Client Heartbeat Reporting", e);
- throw new RuntimeException(e);
- }
+ scheduledExecutorService.scheduleAtFixedRate(() -> c2ClientService.sendHeartbeat(generateRuntimeInfo()), INITIAL_DELAY, heartbeatPeriod, TimeUnit.MILLISECONDS);
}
public void stop() {
@@ -177,7 +182,9 @@ public class C2NifiClientService {
}
private RuntimeInfoWrapper generateRuntimeInfo() {
- return new RuntimeInfoWrapper(getAgentRepositories(), runtimeManifestService.getManifest(), getQueueStatus());
+ AgentManifest agentManifest = new AgentManifest(runtimeManifestService.getManifest());
+ agentManifest.setSupportedOperations(supportedOperationsProvider.getSupportedOperations());
+ return new RuntimeInfoWrapper(getAgentRepositories(), agentManifest, getQueueStatus());
}
private AgentRepositories getAgentRepositories() {