From 842cc928ac74204244f557ce234053552df8c545 Mon Sep 17 00:00:00 2001 From: Kevin Doran Date: Tue, 9 Jun 2026 13:35:39 -0400 Subject: [PATCH] NIFI-15979 Add connectorAttributes to ConnectorStatus Carry a Map of Connector-level attributes (component, bundle coordinate, and ConnectorConfigurationProvider-supplied attributes) on ConnectorStatus so reporting consumers can enrich Connector-managed metrics. --- .../controller/status/ConnectorStatus.java | 21 +++++ .../status/ConnectorStatusTest.java | 78 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/test/java/org/apache/nifi/controller/status/ConnectorStatusTest.java diff --git a/src/main/java/org/apache/nifi/controller/status/ConnectorStatus.java b/src/main/java/org/apache/nifi/controller/status/ConnectorStatus.java index c2fc7fc..c0a8a3b 100644 --- a/src/main/java/org/apache/nifi/controller/status/ConnectorStatus.java +++ b/src/main/java/org/apache/nifi/controller/status/ConnectorStatus.java @@ -16,6 +16,10 @@ */ package org.apache.nifi.controller.status; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + /** * The status of a Connector, including the status of its managed root Process Group. * @@ -36,6 +40,7 @@ public class ConnectorStatus implements Cloneable { private String id; private String name; private ProcessGroupStatus rootGroupStatus; + private Map connectorAttributes = Map.of(); /** * @return the identifier of the Connector @@ -71,12 +76,27 @@ public void setRootGroupStatus(final ProcessGroupStatus rootGroupStatus) { this.rootGroupStatus = rootGroupStatus; } + /** + * @return an immutable map of Connector-level attributes (for example, the Connector identifier, name, component + * type, and bundle coordinate, along with any provider-supplied attributes). These describe the Connector + * as a whole and are intended to be applied to logs and metrics emitted for its managed flow. Never + * {@code null}. + */ + public Map getConnectorAttributes() { + return Collections.unmodifiableMap(connectorAttributes); + } + + public void setConnectorAttributes(final Map connectorAttributes) { + this.connectorAttributes = connectorAttributes == null ? Map.of() : new LinkedHashMap<>(connectorAttributes); + } + @Override public ConnectorStatus clone() { final ConnectorStatus clonedObj = new ConnectorStatus(); clonedObj.id = id; clonedObj.name = name; clonedObj.rootGroupStatus = rootGroupStatus == null ? null : rootGroupStatus.clone(); + clonedObj.connectorAttributes = new LinkedHashMap<>(connectorAttributes); return clonedObj; } @@ -85,6 +105,7 @@ public String toString() { return "ConnectorStatus [id=" + id + ", name=" + name + ", rootGroupStatus=" + rootGroupStatus + + ", connectorAttributes=" + connectorAttributes + "]"; } } diff --git a/src/test/java/org/apache/nifi/controller/status/ConnectorStatusTest.java b/src/test/java/org/apache/nifi/controller/status/ConnectorStatusTest.java new file mode 100644 index 0000000..e4dd1fd --- /dev/null +++ b/src/test/java/org/apache/nifi/controller/status/ConnectorStatusTest.java @@ -0,0 +1,78 @@ +/* + * 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.controller.status; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ConnectorStatusTest { + + @Test + void connectorAttributesDefaultToEmptyAndAreNeverNull() { + final ConnectorStatus status = new ConnectorStatus(); + assertNotNull(status.getConnectorAttributes()); + assertTrue(status.getConnectorAttributes().isEmpty()); + + status.setConnectorAttributes(null); + assertNotNull(status.getConnectorAttributes()); + assertTrue(status.getConnectorAttributes().isEmpty()); + } + + @Test + void setConnectorAttributesStoresDefensiveCopy() { + final ConnectorStatus status = new ConnectorStatus(); + final Map source = new HashMap<>(); + source.put("connectorId", "id-1"); + status.setConnectorAttributes(source); + + // Mutating the source after the call must not affect the stored attributes. + source.put("connectorName", "added-after"); + + assertEquals(Map.of("connectorId", "id-1"), status.getConnectorAttributes()); + } + + @Test + void getConnectorAttributesReturnsImmutableView() { + final ConnectorStatus status = new ConnectorStatus(); + status.setConnectorAttributes(Map.of("connectorId", "id-1")); + assertThrows(UnsupportedOperationException.class, () -> status.getConnectorAttributes().put("connectorName", "x")); + } + + @Test + void cloneCopiesConnectorAttributesIndependently() { + final ConnectorStatus status = new ConnectorStatus(); + final Map attributes = new HashMap<>(); + attributes.put("connectorId", "id-1"); + attributes.put("connectorDefinitionId", "def-1"); + status.setConnectorAttributes(attributes); + + final ConnectorStatus clone = status.clone(); + assertEquals(status.getConnectorAttributes(), clone.getConnectorAttributes()); + + // Mutating the original's attributes must not affect the clone. + status.setConnectorAttributes(Map.of("connectorId", "id-2")); + assertEquals("id-1", clone.getConnectorAttributes().get("connectorId")); + assertEquals("def-1", clone.getConnectorAttributes().get("connectorDefinitionId")); + } +}