This is an automated email from the ASF dual-hosted git repository.

pvillard31 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 22a071f2c79 NIFI-16114 - Fix version change bug when controller 
service property has a parameter reference (#11432)
22a071f2c79 is described below

commit 22a071f2c79a632adabacea903642685caf99f3e
Author: Eric Secules <[email protected]>
AuthorDate: Wed Jul 29 13:40:19 2026 -0700

    NIFI-16114 - Fix version change bug when controller service property has a 
parameter reference (#11432)
---
 .../StandardVersionedComponentSynchronizer.java    |  20 ++-
 ...StandardVersionedComponentSynchronizerTest.java | 191 ++++++++++++++++++++-
 .../ExternalControllerServiceVersioningIT.java     |  53 ++++++
 3 files changed, 255 insertions(+), 9 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
index 7315d8d441d..62c666d664c 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
@@ -96,11 +96,13 @@ import org.apache.nifi.groups.VersionedComponentAdditions;
 import org.apache.nifi.logging.LogLevel;
 import org.apache.nifi.migration.ControllerServiceFactory;
 import org.apache.nifi.migration.StandardControllerServiceFactory;
+import org.apache.nifi.parameter.ExpressionLanguageAgnosticParameterParser;
 import org.apache.nifi.parameter.Parameter;
 import org.apache.nifi.parameter.ParameterContext;
 import org.apache.nifi.parameter.ParameterContextManager;
 import org.apache.nifi.parameter.ParameterContextNameUtils;
 import org.apache.nifi.parameter.ParameterDescriptor;
+import org.apache.nifi.parameter.ParameterParser;
 import org.apache.nifi.parameter.ParameterProviderConfiguration;
 import org.apache.nifi.parameter.ParameterReferenceManager;
 import org.apache.nifi.parameter.ParameterReferencedControllerServiceData;
@@ -164,6 +166,7 @@ public class StandardVersionedComponentSynchronizer 
implements VersionedComponen
     public static final String ENC_PREFIX = "enc{";
     public static final String ENC_SUFFIX = "}";
 
+    private static final ParameterParser agnosticParameterParser = new 
ExpressionLanguageAgnosticParameterParser();
     private final VersionedFlowSynchronizationContext context;
     private final Set<String> updatedVersionedComponentIds = new HashSet<>();
     private final List<CreatedOrModifiedExtension> 
createdAndModifiedExtensions = new ArrayList<>();
@@ -1666,11 +1669,11 @@ public class StandardVersionedComponentSynchronizer 
implements VersionedComponen
                     || (versionedDescriptor != null && 
versionedDescriptor.getIdentifiesControllerService());
                 final boolean sensitive = (descriptor != null && 
descriptor.isSensitive())
                     || (versionedDescriptor != null && 
versionedDescriptor.isSensitive());
-
+                final String proposedValue = 
proposedProperties.get(propertyName);
                 final String value;
-                if (descriptor != null && referencesService && 
(proposedProperties.get(propertyName) != null)) {
+                if (descriptor != null && referencesService && proposedValue 
!= null && !isReferencingParameter(proposedValue)) {
                     // Need to determine if the component's property 
descriptor for this service is already set to an id
-                    // of an existing service that is outside the current 
processor group, and if it is we want to leave
+                    // of an existing service that is outside the current 
processor group, and if it is, we want to leave
                     // the property set to that value
                     String existingExternalServiceId = null;
                     final String componentDescriptorValue = 
componentNode.getEffectivePropertyValue(descriptor);
@@ -1687,9 +1690,8 @@ public class StandardVersionedComponentSynchronizer 
implements VersionedComponen
                     // If the component's property descriptor is not already 
set to an id of an existing external service,
                     // then we need to take the Versioned Component ID and 
resolve this to the instance ID of the service
                     if (existingExternalServiceId == null) {
-                        final String serviceVersionedComponentId = 
proposedProperties.get(propertyName);
-                        String instanceId = 
getServiceInstanceId(serviceVersionedComponentId, group);
-                        value = (instanceId == null) ? 
serviceVersionedComponentId : instanceId;
+                        String instanceId = 
getServiceInstanceId(proposedValue, group);
+                        value = (instanceId == null) ? proposedValue : 
instanceId;
 
                         // Find the same property descriptor in the 
component's CreatedExtension and replace it with the
                         // instance ID of the service
@@ -1700,7 +1702,7 @@ public class StandardVersionedComponentSynchronizer 
implements VersionedComponen
                         value = existingExternalServiceId;
                     }
                 } else {
-                    value = proposedProperties.get(propertyName);
+                    value = proposedValue;
                 }
 
                 // skip any sensitive properties that are not populated so we 
can retain whatever is currently set. We do this because sensitive properties 
are not stored in the registry
@@ -1730,6 +1732,10 @@ public class StandardVersionedComponentSynchronizer 
implements VersionedComponen
         return fullPropertyMap;
     }
 
+    private static boolean isReferencingParameter(String proposedValue) {
+        return 
!agnosticParameterParser.parseTokens(proposedValue).toReferenceList().isEmpty();
+    }
+
     private Map<String, String> getDecryptedProperties(final Map<String, 
String> properties) {
         final Map<String, String> decryptedProperties = new LinkedHashMap<>();
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizerTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizerTest.java
index 0c2f28f7aed..b93ab48cb11 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizerTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizerTest.java
@@ -1022,7 +1022,6 @@ public class StandardVersionedComponentSynchronizerTest {
     }
 
     @Test
-    @SuppressWarnings("unchecked")
     public void testExternalControllerServiceReferenceRemoved() throws 
FlowSynchronizationException, InterruptedException, TimeoutException {
         final PropertyDescriptor descriptorB = new 
PropertyDescriptor.Builder().name("b").build();
         final PropertyDescriptor descriptorCS = new 
PropertyDescriptor.Builder().name("cs")
@@ -1064,7 +1063,7 @@ public class StandardVersionedComponentSynchronizerTest {
         versionedProcessor.setPropertyDescriptors(proposedDescriptors);
         versionedProcessor.setProperties(proposedProperties);
 
-        final ArgumentCaptor<Map<String, String>> captorProperties = 
ArgumentCaptor.forClass(Map.class);
+        final ArgumentCaptor<Map<String, String>> captorProperties = 
ArgumentCaptor.captor();
         synchronizer.synchronize(processorNode, versionedProcessor, group, 
synchronizationOptions);
         verify(processorNode).setProperties(captorProperties.capture(), 
anyBoolean(), any());
         final Map<String, String> properties = captorProperties.getValue();
@@ -1072,6 +1071,194 @@ public class StandardVersionedComponentSynchronizerTest 
{
         assertNull(properties.get("cs"));
     }
 
+    @Test
+    public void testExternalControllerServiceParameterReferencePreserved() 
throws FlowSynchronizationException, InterruptedException, TimeoutException {
+        // A controller-service-identifying property is configured with a 
Parameter reference (#{svc}) that resolves to a
+        // controller service living outside this process group. The proposed 
(versioned) flow references the property via
+        // the SAME parameter reference. Synchronizing must PRESERVE the 
parameter reference.
+        //
+        // Bug: populatePropertiesMap resolves the effective value (#{svc} -> 
concrete service id), finds that id as an
+        // existing external controller service, and pins the property to the 
concrete instance id -- silently dropping the
+        // parameterization. On the next flow comparison this surfaces as 
DifferenceType.PROPERTY_PARAMETERIZATION_REMOVED.
+        // This test asserts the correct behavior and therefore fails against 
the current implementation.
+        final String parameterReference = "#{svc}";
+        final String externalServiceId = "external-service-id";
+
+        final PropertyDescriptor descriptorCS = new 
PropertyDescriptor.Builder().name("cs")
+                .identifiesControllerService(ControllerService.class).build();
+
+        final Map<PropertyDescriptor, String> rawPropertyValues = new 
HashMap<>();
+        rawPropertyValues.put(descriptorCS, parameterReference);
+
+        final VersionedPropertyDescriptor versionedDescriptorCS = new 
VersionedPropertyDescriptor();
+        versionedDescriptorCS.setName(descriptorCS.getName());
+        final Map<String, VersionedPropertyDescriptor> proposedDescriptors = 
new HashMap<>();
+        proposedDescriptors.put(versionedDescriptorCS.getName(), 
versionedDescriptorCS);
+
+        final Map<PropertyDescriptor, PropertyConfiguration> propertiesBefore 
= new HashMap<>();
+        propertiesBefore.put(descriptorCS, new 
PropertyConfiguration(parameterReference, null, null, null));
+
+        final ProcessorNode processorNode = createMockProcessor();
+        
when(processorNode.getPropertyDescriptor(eq("cs"))).thenReturn(descriptorCS);
+        when(processorNode.getProperties()).thenReturn(propertiesBefore);
+        
when(processorNode.getRawPropertyValues()).thenReturn(rawPropertyValues);
+        // #{svc} resolves to the concrete id of a controller service outside 
this group.
+        
when(processorNode.getEffectivePropertyValue(eq(descriptorCS))).thenReturn(externalServiceId);
+        
when(processorNode.isReferencingParameter(eq(descriptorCS.getName()))).thenReturn(true);
+
+        final ProcessGroup processGroup = processorNode.getProcessGroup();
+        final ProcessGroup processGroupParent = mock(ProcessGroup.class);
+        final ControllerServiceNode externalService = 
createMockControllerService();
+        when(processGroup.getParent()).thenReturn(processGroupParent);
+        when(processGroupParent.findControllerService(eq(externalServiceId), 
eq(false), eq(true))).thenReturn(externalService);
+
+        // Proposed flow references the property via the SAME parameter 
reference.
+        final Map<String, String> proposedProperties = new HashMap<>();
+        proposedProperties.put("cs", parameterReference);
+        final VersionedProcessor versionedProcessor = 
createMinimalVersionedProcessor();
+        versionedProcessor.setPropertyDescriptors(proposedDescriptors);
+        versionedProcessor.setProperties(proposedProperties);
+
+        final ArgumentCaptor<Map<String, String>> captorProperties = 
ArgumentCaptor.captor();
+        synchronizer.synchronize(processorNode, versionedProcessor, group, 
synchronizationOptions);
+        verify(processorNode).setProperties(captorProperties.capture(), 
anyBoolean(), any());
+        final Map<String, String> properties = captorProperties.getValue();
+
+        assertEquals(parameterReference, properties.get("cs"),
+                "Controller-service property configured with a Parameter 
reference was flattened to the resolved service id");
+    }
+
+    @Test
+    public void testNonControllerServiceParameterReferencePreserved() throws 
FlowSynchronizationException, InterruptedException, TimeoutException {
+        // A regular (non-CS-identifying) property is configured with a 
Parameter reference (#{my_param}).
+        // The proposed (versioned) flow references the same parameter. 
Synchronizing must preserve the
+        // parameter reference rather than substituting any resolved effective 
value.
+        final String parameterReference = "#{my_param}";
+        final String effectiveValue = "resolved-value";
+
+        final PropertyDescriptor descriptorA = new 
PropertyDescriptor.Builder().name("a").build();
+
+        final Map<PropertyDescriptor, String> rawPropertyValues = new 
HashMap<>();
+        rawPropertyValues.put(descriptorA, parameterReference);
+
+        final VersionedPropertyDescriptor versionedDescriptorA = new 
VersionedPropertyDescriptor();
+        versionedDescriptorA.setName(descriptorA.getName());
+        final Map<String, VersionedPropertyDescriptor> proposedDescriptors = 
new HashMap<>();
+        proposedDescriptors.put(versionedDescriptorA.getName(), 
versionedDescriptorA);
+
+        final Map<PropertyDescriptor, PropertyConfiguration> propertiesBefore 
= new HashMap<>();
+        propertiesBefore.put(descriptorA, new 
PropertyConfiguration(parameterReference, null, null, null));
+
+        final ProcessorNode processorNode = createMockProcessor();
+        
when(processorNode.getPropertyDescriptor(eq("a"))).thenReturn(descriptorA);
+        when(processorNode.getProperties()).thenReturn(propertiesBefore);
+        
when(processorNode.getRawPropertyValues()).thenReturn(rawPropertyValues);
+        
when(processorNode.getEffectivePropertyValue(eq(descriptorA))).thenReturn(effectiveValue);
+        
when(processorNode.isReferencingParameter(eq(descriptorA.getName()))).thenReturn(true);
+
+        final Map<String, String> proposedProperties = new HashMap<>();
+        proposedProperties.put("a", parameterReference);
+        final VersionedProcessor versionedProcessor = 
createMinimalVersionedProcessor();
+        versionedProcessor.setPropertyDescriptors(proposedDescriptors);
+        versionedProcessor.setProperties(proposedProperties);
+
+        final ArgumentCaptor<Map<String, String>> captorProperties = 
ArgumentCaptor.captor();
+        synchronizer.synchronize(processorNode, versionedProcessor, group, 
synchronizationOptions);
+        verify(processorNode).setProperties(captorProperties.capture(), 
anyBoolean(), any());
+        final Map<String, String> properties = captorProperties.getValue();
+
+        assertEquals(parameterReference, properties.get("a"),
+                "Non-CS property configured with a Parameter reference should 
preserve the reference, not resolve it");
+    }
+
+    @Test
+    public void testNonControllerServiceParameterChangedToHardCoded() throws 
FlowSynchronizationException, InterruptedException, TimeoutException {
+        // When the proposed flow replaces a Parameter reference with a 
hard-coded value on a non-CS property,
+        // the synchronizer must use the hard-coded proposed value, not the 
old parameter reference.
+        final String parameterReference = "#{my_param}";
+        final String newExplicitValue = "new-explicit-value";
+
+        final PropertyDescriptor descriptorA = new 
PropertyDescriptor.Builder().name("a").build();
+
+        final Map<PropertyDescriptor, String> rawPropertyValues = new 
HashMap<>();
+        rawPropertyValues.put(descriptorA, parameterReference);
+
+        final VersionedPropertyDescriptor versionedDescriptorA = new 
VersionedPropertyDescriptor();
+        versionedDescriptorA.setName(descriptorA.getName());
+        final Map<String, VersionedPropertyDescriptor> proposedDescriptors = 
new HashMap<>();
+        proposedDescriptors.put(versionedDescriptorA.getName(), 
versionedDescriptorA);
+
+        final Map<PropertyDescriptor, PropertyConfiguration> propertiesBefore 
= new HashMap<>();
+        propertiesBefore.put(descriptorA, new 
PropertyConfiguration(parameterReference, null, null, null));
+
+        final ProcessorNode processorNode = createMockProcessor();
+        
when(processorNode.getPropertyDescriptor(eq("a"))).thenReturn(descriptorA);
+        when(processorNode.getProperties()).thenReturn(propertiesBefore);
+        
when(processorNode.getRawPropertyValues()).thenReturn(rawPropertyValues);
+        
when(processorNode.isReferencingParameter(eq(descriptorA.getName()))).thenReturn(true);
+
+        final Map<String, String> proposedProperties = new HashMap<>();
+        proposedProperties.put("a", newExplicitValue);
+        final VersionedProcessor versionedProcessor = 
createMinimalVersionedProcessor();
+        versionedProcessor.setPropertyDescriptors(proposedDescriptors);
+        versionedProcessor.setProperties(proposedProperties);
+
+        final ArgumentCaptor<Map<String, String>> captorProperties = 
ArgumentCaptor.captor();
+        synchronizer.synchronize(processorNode, versionedProcessor, group, 
synchronizationOptions);
+        verify(processorNode).setProperties(captorProperties.capture(), 
anyBoolean(), any());
+        final Map<String, String> properties = captorProperties.getValue();
+
+        assertEquals(newExplicitValue, properties.get("a"),
+                "Non-CS property previously set via Parameter reference should 
adopt the hard-coded value from the proposed flow");
+    }
+
+    @Test
+    public void testControllerServiceParameterChangedToHardCoded() throws 
FlowSynchronizationException, InterruptedException, TimeoutException {
+        // When the proposed flow replaces a Parameter reference with a 
hard-coded versioned component ID on
+        // a CS-identifying property, the synchronizer must resolve the 
versioned ID to the local instance ID.
+        final String parameterReference = "#{svc}";
+        final String versionedCsId = "versioned-cs-uuid";
+
+        final PropertyDescriptor descriptorCS = new 
PropertyDescriptor.Builder().name("cs")
+                .identifiesControllerService(ControllerService.class).build();
+
+        final Map<PropertyDescriptor, String> rawPropertyValues = new 
HashMap<>();
+        rawPropertyValues.put(descriptorCS, parameterReference);
+
+        final VersionedPropertyDescriptor versionedDescriptorCS = new 
VersionedPropertyDescriptor();
+        versionedDescriptorCS.setName(descriptorCS.getName());
+        final Map<String, VersionedPropertyDescriptor> proposedDescriptors = 
new HashMap<>();
+        proposedDescriptors.put(versionedDescriptorCS.getName(), 
versionedDescriptorCS);
+
+        final Map<PropertyDescriptor, PropertyConfiguration> propertiesBefore 
= new HashMap<>();
+        propertiesBefore.put(descriptorCS, new 
PropertyConfiguration(parameterReference, null, null, null));
+
+        final ProcessorNode processorNode = createMockProcessor();
+        
when(processorNode.getPropertyDescriptor(eq("cs"))).thenReturn(descriptorCS);
+        when(processorNode.getProperties()).thenReturn(propertiesBefore);
+        
when(processorNode.getRawPropertyValues()).thenReturn(rawPropertyValues);
+        
when(processorNode.isReferencingParameter(eq(descriptorCS.getName()))).thenReturn(true);
+
+        // Wire up a local controller service that the versioned ID resolves 
to.
+        final ControllerServiceNode localService = 
createMockControllerService();
+        
when(localService.getVersionedComponentId()).thenReturn(Optional.of(versionedCsId));
+        
when(group.getControllerServices(false)).thenReturn(Set.of(localService));
+
+        final Map<String, String> proposedProperties = new HashMap<>();
+        proposedProperties.put("cs", versionedCsId);
+        final VersionedProcessor versionedProcessor = 
createMinimalVersionedProcessor();
+        versionedProcessor.setPropertyDescriptors(proposedDescriptors);
+        versionedProcessor.setProperties(proposedProperties);
+
+        final ArgumentCaptor<Map<String, String>> captorProperties = 
ArgumentCaptor.captor();
+        synchronizer.synchronize(processorNode, versionedProcessor, group, 
synchronizationOptions);
+        verify(processorNode).setProperties(captorProperties.capture(), 
anyBoolean(), any());
+        final Map<String, String> properties = captorProperties.getValue();
+
+        assertEquals(localService.getIdentifier(), properties.get("cs"),
+                "CS-identifying property previously set via Parameter 
reference should be resolved to the local service instance ID");
+    }
+
     @Test
     public void testControllerServiceRemoved() throws 
FlowSynchronizationException, InterruptedException, TimeoutException {
         final ControllerServiceNode service = createMockControllerService();
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ExternalControllerServiceVersioningIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ExternalControllerServiceVersioningIT.java
index 84d5d799136..b96ae82bb27 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ExternalControllerServiceVersioningIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ExternalControllerServiceVersioningIT.java
@@ -30,6 +30,7 @@ import org.apache.nifi.web.api.entity.ConnectionEntity;
 import org.apache.nifi.web.api.entity.ControllerServiceEntity;
 import org.apache.nifi.web.api.entity.FlowComparisonEntity;
 import org.apache.nifi.web.api.entity.FlowRegistryClientEntity;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ProcessorEntity;
@@ -385,6 +386,58 @@ public class ExternalControllerServiceVersioningIT extends 
NiFiSystemIT {
                 "A migration-marked Controller Service must not be reported as 
a local modification, but it was");
     }
 
+    /**
+     * Reproduces Show Local Changes gap where a processor property that 
identifies a Controller Service is set via
+     * a Parameter reference (#{svc}) pointing at a service defined outside 
the versioned PG. Both
+     * v1 and v2 of the flow reference the service through the same Parameter.
+     * <p>
+     * When the PG is downgraded to v1 and then upgraded back to v2, the 
synchronizer used to resolve
+     * "#{svc}" to the external service's concrete instance ID and pin the 
property to that literal
+     * value, silently dropping the Parameter reference. This showed up as a 
false "local
+     * modification" (Property Value Changed) even though the flow and the 
versioned snapshot were
+     * otherwise identical.
+     */
+    @Test
+    public void 
testExternalControllerServiceParameterReferencePreservedOnUpgrade() throws 
NiFiClientException, IOException, InterruptedException {
+        final FlowRegistryClientEntity registryClient = registerClient();
+        final NiFiClientUtil util = getClientUtil();
+
+        final ControllerServiceEntity service = 
util.createControllerService(COUNT_SERVICE_TYPE, "root");
+        util.enableControllerService(service);
+
+        final ParameterContextEntity paramContext = 
util.createParameterContext(
+                "svc-context", Collections.singletonMap("svc", 
service.getComponent().getId()));
+
+        final ProcessGroupEntity child = util.createProcessGroup("Child", 
"root");
+        util.setParameterContext(child.getId(), paramContext);
+
+        ProcessorEntity counter = util.createProcessor("CountFlowFiles", 
child.getId());
+        util.updateProcessorProperties(counter, 
Collections.singletonMap("Count Service", "#{svc}"));
+        final ProcessorEntity terminate = 
util.createProcessor("TerminateFlowFile", child.getId());
+        util.createConnection(counter, terminate, "success");
+
+        final VersionControlInformationEntity vci = 
util.startVersionControl(child, registryClient, TEST_FLOWS_BUCKET, 
"param-ref-external-cs");
+        util.assertFlowUpToDate(child.getId());
+
+        // v2 differs only in scheduling period. "Count Service" remains 
#{svc} in both versions.
+        counter = util.updateProcessorSchedulingPeriod(counter, "10 sec");
+        util.saveFlowVersion(child, registryClient, vci);
+        util.assertFlowUpToDate(child.getId());
+
+        // Downgrade then upgrade to force the synchronizer to re-resolve the 
property, which is
+        // the code path affected by NIFI-16114.
+        util.changeFlowVersion(child.getId(), "1");
+        util.changeFlowVersion(child.getId(), "2");
+
+        final ProcessorEntity refreshed = 
getNifiClient().getProcessorClient().getProcessor(counter.getId());
+        assertEquals("#{svc}", 
refreshed.getComponent().getConfig().getProperties().get("Count Service"),
+                "Parameter reference to external Controller Service should 
survive version upgrade, not be flattened to the service's instance ID");
+
+        final FlowComparisonEntity localMods = 
getNifiClient().getProcessGroupClient().getLocalModifications(child.getId());
+        assertTrue(localMods.getComponentDifferences().isEmpty(),
+                "Show Local Changes should report no differences after 
upgrading between versions that both reference the service via the same 
Parameter");
+    }
+
     /**
      * Deletes only the connections and processors within a Process Group, 
without touching
      * Controller Services (which may be inherited from ancestor groups).

Reply via email to