You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2018/01/08 18:14:17 UTC

[29/50] nifi git commit: NIFI-4436: - Addressing miscellaneous minor UX issues. - Updating comments UX for all components. - Updating the styling of PG and RPG to be more consistent. - Adding the icons for nested versioned process groups. - Calculating t

NIFI-4436:
- Addressing miscellaneous minor UX issues.
- Updating comments UX for all components.
- Updating the styling of PG and RPG to be more consistent.
- Adding the icons for nested versioned process groups.
- Calculating the number/states of nested versioned process groups.


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/d34fb5e2
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/d34fb5e2
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/d34fb5e2

Branch: refs/heads/master
Commit: d34fb5e2ef967b0704621ffa0115283c3ccdc070
Parents: e160670
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Nov 28 16:22:11 2017 -0500
Committer: Bryan Bende <bb...@apache.org>
Committed: Mon Jan 8 12:44:54 2018 -0500

----------------------------------------------------------------------
 .../nifi/web/api/dto/ProcessGroupDTO.java       |  59 ++++-
 .../apache/nifi/groups/ProcessGroupCounts.java  |  32 ++-
 .../apache/nifi/groups/RemoteProcessGroup.java  |   2 +-
 .../nifi/groups/RemoteProcessGroupCounts.java   |  36 +++
 .../apache/nifi/controller/TemplateUtils.java   |   5 +
 .../nifi/groups/StandardProcessGroup.java       |  81 ++++--
 .../nifi/remote/StandardRemoteProcessGroup.java | 103 +++-----
 .../nifi/web/StandardNiFiServiceFacade.java     |  10 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java |  14 +-
 .../canvas/revert-local-changes-dialog.jsp      |   6 +-
 .../canvas/show-local-changes-dialog.jsp        |   6 +-
 .../nifi-web-ui/src/main/webapp/css/dialog.css  |   8 +-
 .../nifi-web-ui/src/main/webapp/css/graph.css   |   6 +-
 .../nifi-web-ui/src/main/webapp/css/main.css    |  32 +++
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |   4 +
 .../main/webapp/js/nf/canvas/nf-flow-version.js |  65 +++--
 .../src/main/webapp/js/nf/canvas/nf-port.js     |  46 ++++
 .../webapp/js/nf/canvas/nf-process-group.js     | 252 ++++++++++++++++---
 .../main/webapp/js/nf/canvas/nf-processor.js    |  46 ++++
 .../js/nf/canvas/nf-remote-process-group.js     |  89 ++++---
 20 files changed, 687 insertions(+), 215 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java
index 7faf10b..d283510 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessGroupDTO.java
@@ -30,7 +30,7 @@ public class ProcessGroupDTO extends ComponentDTO {
     private String name;
     private String comments;
     private Map<String, String> variables;
-    private VersionControlInformationDTO versionControlInfo;
+    private VersionControlInformationDTO versionControlInformation;
 
     private Integer runningCount;
     private Integer stoppedCount;
@@ -39,6 +39,12 @@ public class ProcessGroupDTO extends ComponentDTO {
     private Integer activeRemotePortCount;
     private Integer inactiveRemotePortCount;
 
+    private Integer upToDateCount;
+    private Integer locallyModifiedCount;
+    private Integer staleCount;
+    private Integer locallyModifiedAndStaleCount;
+    private Integer syncFailureCount;
+
     private Integer inputPortCount;
     private Integer outputPortCount;
 
@@ -204,6 +210,51 @@ public class ProcessGroupDTO extends ComponentDTO {
         this.inactiveRemotePortCount = inactiveRemotePortCount;
     }
 
+    @ApiModelProperty("The number of up to date versioned process groups in the process group.")
+    public Integer getUpToDateCount() {
+        return upToDateCount;
+    }
+
+    public void setUpToDateCount(Integer upToDateCount) {
+        this.upToDateCount = upToDateCount;
+    }
+
+    @ApiModelProperty("The number of locally modified versioned process groups in the process group.")
+    public Integer getLocallyModifiedCount() {
+        return locallyModifiedCount;
+    }
+
+    public void setLocallyModifiedCount(Integer locallyModifiedCount) {
+        this.locallyModifiedCount = locallyModifiedCount;
+    }
+
+    @ApiModelProperty("The number of stale versioned process groups in the process group.")
+    public Integer getStaleCount() {
+        return staleCount;
+    }
+
+    public void setStaleCount(Integer staleCount) {
+        this.staleCount = staleCount;
+    }
+
+    @ApiModelProperty("The number of locally modified and stale versioned process groups in the process group.")
+    public Integer getLocallyModifiedAndStaleCount() {
+        return locallyModifiedAndStaleCount;
+    }
+
+    public void setLocallyModifiedAndStaleCount(Integer locallyModifiedAndStaleCount) {
+        this.locallyModifiedAndStaleCount = locallyModifiedAndStaleCount;
+    }
+
+    @ApiModelProperty("The number of versioned process groups in the process group that are unable to sync to a registry.")
+    public Integer getSyncFailureCount() {
+        return syncFailureCount;
+    }
+
+    public void setSyncFailureCount(Integer syncFailureCount) {
+        this.syncFailureCount = syncFailureCount;
+    }
+
     @ApiModelProperty(value = "The variables that are configured for the Process Group. Note that this map contains only "
         + "those variables that are defined on this Process Group and not any variables that are defined in the parent "
         + "Process Group, etc. I.e., this Map will not contain all variables that are accessible by components in this "
@@ -219,10 +270,10 @@ public class ProcessGroupDTO extends ComponentDTO {
     @ApiModelProperty("The Version Control information that indicates which Flow Registry, and where in the Flow Registry, "
         + "this Process Group is tracking to; or null if this Process Group is not under version control")
     public VersionControlInformationDTO getVersionControlInformation() {
-        return versionControlInfo;
+        return versionControlInformation;
     }
 
-    public void setVersionControlInformation(final VersionControlInformationDTO versionControlInfo) {
-        this.versionControlInfo = versionControlInfo;
+    public void setVersionControlInformation(final VersionControlInformationDTO versionControlInformation) {
+        this.versionControlInformation = versionControlInformation;
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroupCounts.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroupCounts.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroupCounts.java
index 3eb594b..394e5d3 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroupCounts.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroupCounts.java
@@ -18,10 +18,11 @@ package org.apache.nifi.groups;
 
 public class ProcessGroupCounts {
 
-    private final int inputPortCount, outputPortCount, runningCount, stoppedCount, invalidCount, disabledCount, activeRemotePortCount, inactiveRemotePortCount;
+    private final int inputPortCount, outputPortCount, runningCount, stoppedCount, invalidCount, disabledCount, activeRemotePortCount, inactiveRemotePortCount,
+            upToDateCount, locallyModifiedCount, staleCount, locallyModifiedAndStaleCount, syncFailureCount;
 
-    public ProcessGroupCounts(final int inputPortCount, final int outputPortCount, final int runningCount,
-            final int stoppedCount, final int invalidCount, final int disabledCount, final int activeRemotePortCount, final int inactiveRemotePortCount) {
+    public ProcessGroupCounts(int inputPortCount, int outputPortCount, int runningCount, int stoppedCount, int invalidCount, int disabledCount, int activeRemotePortCount,
+                              int inactiveRemotePortCount, int upToDateCount, int locallyModifiedCount, int staleCount, int locallyModifiedAndStaleCount, int syncFailureCount) {
         this.inputPortCount = inputPortCount;
         this.outputPortCount = outputPortCount;
         this.runningCount = runningCount;
@@ -30,6 +31,11 @@ public class ProcessGroupCounts {
         this.disabledCount = disabledCount;
         this.activeRemotePortCount = activeRemotePortCount;
         this.inactiveRemotePortCount = inactiveRemotePortCount;
+        this.upToDateCount = upToDateCount;
+        this.locallyModifiedCount = locallyModifiedCount;
+        this.staleCount = staleCount;
+        this.locallyModifiedAndStaleCount = locallyModifiedAndStaleCount;
+        this.syncFailureCount = syncFailureCount;
     }
 
     public int getInputPortCount() {
@@ -63,4 +69,24 @@ public class ProcessGroupCounts {
     public int getInactiveRemotePortCount() {
         return inactiveRemotePortCount;
     }
+
+    public int getUpToDateCount() {
+        return upToDateCount;
+    }
+
+    public int getLocallyModifiedCount() {
+        return locallyModifiedCount;
+    }
+
+    public int getStaleCount() {
+        return staleCount;
+    }
+
+    public int getLocallyModifiedAndStaleCount() {
+        return locallyModifiedAndStaleCount;
+    }
+
+    public int getSyncFailureCount() {
+        return syncFailureCount;
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroup.java
index e4da31b..0dd6070 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroup.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroup.java
@@ -73,7 +73,7 @@ public interface RemoteProcessGroup extends ComponentAuthorizable, Positionable,
 
     RemoteGroupPort getOutputPort(String id);
 
-    ProcessGroupCounts getCounts();
+    RemoteProcessGroupCounts getCounts();
 
     void refreshFlowContents() throws CommunicationsException;
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroupCounts.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroupCounts.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroupCounts.java
new file mode 100644
index 0000000..e3163f6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/RemoteProcessGroupCounts.java
@@ -0,0 +1,36 @@
+/*
+ * 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.groups;
+
+public class RemoteProcessGroupCounts {
+
+    private final int inputPortCount, outputPortCount;
+
+    public RemoteProcessGroupCounts(int inputPortCount, int outputPortCount) {
+        this.inputPortCount = inputPortCount;
+        this.outputPortCount = outputPortCount;
+    }
+
+    public int getInputPortCount() {
+        return inputPortCount;
+    }
+
+    public int getOutputPortCount() {
+        return outputPortCount;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
index 9583bbb..6bfa49d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
@@ -127,6 +127,11 @@ public class TemplateUtils {
             processGroupDTO.setOutputPortCount(null);
             processGroupDTO.setRunningCount(null);
             processGroupDTO.setStoppedCount(null);
+            processGroupDTO.setUpToDateCount(null);
+            processGroupDTO.setLocallyModifiedCount(null);
+            processGroupDTO.setStaleCount(null);
+            processGroupDTO.setLocallyModifiedAndStaleCount(null);
+            processGroupDTO.setSyncFailureCount(null);
 
             scrubSnippet(processGroupDTO.getContents());
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 77be3fe..9a14464 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -16,31 +16,6 @@
  */
 package org.apache.nifi.groups;
 
-import static java.util.Objects.requireNonNull;
-
-import java.io.IOException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -139,6 +114,31 @@ import org.apache.nifi.web.api.dto.TemplateDTO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
+
 public final class StandardProcessGroup implements ProcessGroup {
 
     private final String id;
@@ -278,6 +278,12 @@ public final class StandardProcessGroup implements ProcessGroup {
         int activeRemotePorts = 0;
         int inactiveRemotePorts = 0;
 
+        int upToDate = 0;
+        int locallyModified = 0;
+        int stale = 0;
+        int locallyModifiedAndStale = 0;
+        int syncFailure = 0;
+
         readLock.lock();
         try {
             for (final ProcessorNode procNode : processors.values()) {
@@ -324,6 +330,27 @@ public final class StandardProcessGroup implements ProcessGroup {
                 stopped += childCounts.getStoppedCount();
                 invalid += childCounts.getInvalidCount();
                 disabled += childCounts.getDisabledCount();
+
+                // update the vci counts for this child group
+                final VersionControlInformation vci = childGroup.getVersionControlInformation();
+                if (vci != null) {
+                    if (vci.isModified() && !vci.isCurrent()) {
+                        locallyModifiedAndStale += 1;
+                    } else if (!vci.isCurrent()) {
+                        stale += 1;
+                    } else if (vci.isModified()) {
+                        locallyModified += 1;
+                    } else {
+                        upToDate += 1;
+                    }
+                }
+
+                // update the vci counts for all nested groups within the child
+                upToDate += childCounts.getUpToDateCount();
+                locallyModified += childCounts.getLocallyModifiedCount();
+                stale += childCounts.getStaleCount();
+                locallyModifiedAndStale += childCounts.getLocallyModifiedAndStaleCount();
+                syncFailure += childCounts.getSyncFailureCount();
             }
 
             for (final RemoteProcessGroup remoteGroup : findAllRemoteProcessGroups()) {
@@ -358,8 +385,8 @@ public final class StandardProcessGroup implements ProcessGroup {
             readLock.unlock();
         }
 
-        return new ProcessGroupCounts(inputPortCount, outputPortCount, running, stopped,
-                invalid, disabled, activeRemotePorts, inactiveRemotePorts);
+        return new ProcessGroupCounts(inputPortCount, outputPortCount, running, stopped, invalid, disabled, activeRemotePorts,
+                inactiveRemotePorts, upToDate, locallyModified, stale, locallyModifiedAndStale, syncFailure);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java
index 039ac66..67710fe 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java
@@ -16,40 +16,6 @@
  */
 package org.apache.nifi.remote;
 
-import static java.util.Objects.requireNonNull;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import javax.net.ssl.SSLContext;
-import javax.ws.rs.core.Response;
-
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.authorization.Resource;
 import org.apache.nifi.authorization.resource.Authorizable;
@@ -70,6 +36,7 @@ import org.apache.nifi.events.EventReporter;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.ProcessGroupCounts;
 import org.apache.nifi.groups.RemoteProcessGroup;
+import org.apache.nifi.groups.RemoteProcessGroupCounts;
 import org.apache.nifi.groups.RemoteProcessGroupPortDescriptor;
 import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol;
 import org.apache.nifi.remote.protocol.http.HttpProxy;
@@ -84,6 +51,39 @@ import org.apache.nifi.web.api.dto.PortDTO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.net.ssl.SSLContext;
+import javax.ws.rs.core.Response;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
+
 /**
  * Represents the Root Process Group of a remote NiFi Instance. Holds
  * information about that remote instance, as well as {@link IncomingPort}s and
@@ -137,7 +137,7 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup {
     // Maps a Port Name to a PullingPort that can be used to receive files from that port
     private final Map<String, StandardRemoteGroupPort> outputPorts = new HashMap<>();
 
-    private ProcessGroupCounts counts = new ProcessGroupCounts(0, 0, 0, 0, 0, 0, 0, 0);
+    private RemoteProcessGroupCounts counts = new RemoteProcessGroupCounts(0, 0);
     private Long refreshContentsTimestamp = null;
     private Boolean destinationSecure;
     private Integer listeningPort;
@@ -829,7 +829,7 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup {
     }
 
     @Override
-    public ProcessGroupCounts getCounts() {
+    public RemoteProcessGroupCounts getCounts() {
         readLock.lock();
         try {
             return counts;
@@ -838,7 +838,7 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup {
         }
     }
 
-    private void setCounts(final ProcessGroupCounts counts) {
+    private void setCounts(final RemoteProcessGroupCounts counts) {
         writeLock.lock();
         try {
             this.counts = counts;
@@ -910,39 +910,12 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup {
                 if (dto.getOutputPortCount() != null) {
                     outputPortCount = dto.getOutputPortCount();
                 }
-                int runningCount = 0;
-                if (dto.getRunningCount() != null) {
-                    runningCount = dto.getRunningCount();
-                }
-                int stoppedCount = 0;
-                if (dto.getStoppedCount() != null) {
-                    stoppedCount = dto.getStoppedCount();
-                }
-                int invalidCount = 0;
-                if (dto.getInvalidCount() != null) {
-                    invalidCount = dto.getInvalidCount();
-                }
-                int disabledCount = 0;
-                if (dto.getDisabledCount() != null) {
-                    disabledCount = dto.getDisabledCount();
-                }
-
-                int activeRemotePortCount = 0;
-                if (dto.getActiveRemotePortCount() != null) {
-                    activeRemotePortCount = dto.getActiveRemotePortCount();
-                }
-
-                int inactiveRemotePortCount = 0;
-                if (dto.getInactiveRemotePortCount() != null) {
-                    inactiveRemotePortCount = dto.getInactiveRemotePortCount();
-                }
 
                 this.listeningPort = dto.getRemoteSiteListeningPort();
                 this.listeningHttpPort = dto.getRemoteSiteHttpListeningPort();
                 this.destinationSecure = dto.isSiteToSiteSecure();
 
-                final ProcessGroupCounts newCounts = new ProcessGroupCounts(inputPortCount, outputPortCount,
-                        runningCount, stoppedCount, invalidCount, disabledCount, activeRemotePortCount, inactiveRemotePortCount);
+                final RemoteProcessGroupCounts newCounts = new RemoteProcessGroupCounts(inputPortCount, outputPortCount);
                 setCounts(newCounts);
                 this.refreshContentsTimestamp = System.currentTimeMillis();
             } finally {

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index e0594fa..98c7bc8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -16,9 +16,7 @@
  */
 package org.apache.nifi.web;
 
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Response;
-
+import com.google.common.collect.Sets;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
@@ -269,8 +267,8 @@ import org.apache.nifi.web.util.SnippetUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Sets;
-
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -3018,6 +3016,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         final ControllerDTO controllerDTO = new ControllerDTO();
         controllerDTO.setId(controllerFacade.getRootGroupId());
         controllerDTO.setInstanceId(controllerFacade.getInstanceId());
+        controllerDTO.setName(controllerFacade.getName());
+        controllerDTO.setComments(controllerFacade.getComments());
         controllerDTO.setInputPorts(inputPortDtos);
         controllerDTO.setOutputPorts(outputPortDtos);
         controllerDTO.setInputPortCount(inputPortDtos.size());

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index fb60604..5b33d90 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -101,6 +101,7 @@ import org.apache.nifi.flowfile.attributes.CoreAttributes;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.ProcessGroupCounts;
 import org.apache.nifi.groups.RemoteProcessGroup;
+import org.apache.nifi.groups.RemoteProcessGroupCounts;
 import org.apache.nifi.history.History;
 import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.nar.NarClassLoaders;
@@ -1648,7 +1649,7 @@ public final class DtoFactory {
         dto.setInactiveRemoteOutputPortCount(inactiveRemoteOutputPortCount);
         dto.setVersionedComponentId(group.getVersionedComponentId().orElse(null));
 
-        final ProcessGroupCounts counts = group.getCounts();
+        final RemoteProcessGroupCounts counts = group.getCounts();
         if (counts != null) {
             dto.setInputPortCount(counts.getInputPortCount());
             dto.setOutputPortCount(counts.getOutputPortCount());
@@ -2175,6 +2176,11 @@ public final class DtoFactory {
         dto.setOutputPortCount(counts.getOutputPortCount());
         dto.setActiveRemotePortCount(counts.getActiveRemotePortCount());
         dto.setInactiveRemotePortCount(counts.getInactiveRemotePortCount());
+        dto.setUpToDateCount(counts.getUpToDateCount());
+        dto.setLocallyModifiedCount(counts.getLocallyModifiedCount());
+        dto.setStaleCount(counts.getStaleCount());
+        dto.setLocallyModifiedAndStaleCount(counts.getLocallyModifiedAndStaleCount());
+        dto.setSyncFailureCount(counts.getSyncFailureCount());
 
         return dto;
     }
@@ -3453,6 +3459,12 @@ public final class DtoFactory {
         copy.setActiveRemotePortCount(original.getActiveRemotePortCount());
         copy.setInactiveRemotePortCount(original.getInactiveRemotePortCount());
 
+        copy.setUpToDateCount(original.getUpToDateCount());
+        copy.setLocallyModifiedCount(original.getLocallyModifiedCount());
+        copy.setStaleCount(original.getStaleCount());
+        copy.setLocallyModifiedAndStaleCount(original.getLocallyModifiedAndStaleCount());
+        copy.setSyncFailureCount(original.getSyncFailureCount());
+
         if (original.getVariables() != null) {
             copy.setVariables(new HashMap<>(original.getVariables()));
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp
index 45d4c4c..ce18052 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp
@@ -17,10 +17,8 @@
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="revert-local-changes-dialog" layout="column" class="hidden large-dialog">
     <div class="dialog-content">
-        <div class="setting">
-            <div class="setting-field">
-                Are you sure you want to revert changes? All flow configuration changes detailed below will be reverted to the last version.
-            </div>
+        <div class="setting local-changes-message">
+            <span id="revert-local-changes-message"></span>&nbsp;<span style="font-weight: bold;">Revert will remove all changes.</span>
         </div>
         <span id="revert-local-changes-process-group-id" class="hidden"></span>
         <div id="revert-local-changes-filter-controls">

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp
index 9b122ae..bdd4d41 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp
@@ -17,10 +17,8 @@
 <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
 <div id="show-local-changes-dialog" layout="column" class="hidden large-dialog">
     <div class="dialog-content">
-        <div class="setting">
-            <div class="setting-field">
-                The following changes have been made to the flow since the last version.
-            </div>
+        <div class="setting local-changes-message">
+            <span id="show-local-changes-message"></span>
         </div>
         <div id="show-local-changes-filter-controls">
             <div id="show-local-changes-filter-status" class="filter-status">

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
index 69d4bea..9acbdde 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
@@ -269,17 +269,21 @@ div.progress-label {
 
 #revert-local-changes-table, #show-local-changes-table {
     position: absolute;
-    top: 80px;
+    top: 78px;
     left: 0px;
     right: 0px;
     bottom: 0px;
-    height: 225px;
+    height: 350px;
 }
 
 #revert-local-changes-filter, #show-local-changes-filter {
     width: 173px;
 }
 
+div.local-changes-message {
+    font-size: 13px;
+}
+
 /*
     Variable Registry
  */

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
index 204b1d1..4c77066 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
@@ -136,6 +136,11 @@ text.active-thread-count {
     font-weight: bold;
 }
 
+path.component-comments {
+    fill: #000;
+    stroke: #000;
+}
+
 /*
     Selection
 */
@@ -471,5 +476,4 @@ text.remote-process-group-transmission-secure {
 
 text.remote-process-group-last-refresh {
     fill: #728e9b;
-    text-anchor: end;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
index 1554356..d10761a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
@@ -153,6 +153,38 @@ div.context-menu-provenance {
     text-shadow: 0 0 4px rgba(255,255,255,1);
 }
 
+.up-to-date {
+    float: left;
+    color: #3da67a !important;
+    fill: #3da67a !important;
+    margin-top: 0px !important;
+    text-shadow: 0 0 4px rgba(255,255,255,1);
+}
+
+.locally-modified {
+    float: left;
+    color: #747474 !important;
+    fill: #747474 !important;
+    margin-top: 0px !important;
+    text-shadow: 0 0 4px rgba(255,255,255,1);
+}
+
+.stale {
+    float: left;
+    color: #c7685d !important;
+    fill: #c7685d !important;
+    margin-top: 0px !important;
+    text-shadow: 0 0 4px rgba(255,255,255,1);
+}
+
+.locally-modified-and-stale {
+    float: left;
+    color: #c7685d !important;
+    fill: #c7685d !important;
+    margin-top: 0px !important;
+    text-shadow: 0 0 4px rgba(255,255,255,1);
+}
+
 div.valid {
     float: left;
     background-color: transparent;

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index 1aefa44..162da36 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -378,6 +378,10 @@
                 });
 
                 return refreshGraph;
+            } else {
+                return $.Deferred(function (deferred) {
+                    deferred.reject();
+                }).promise();
             }
         },
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
index cb42c30..03aed70 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
@@ -126,13 +126,16 @@
 
             var localChangesData = localChangesGrid.getData();
             localChangesData.setItems([]);
+            localChangesData.setFilterArgs({
+                searchString: ''
+            });
         }
 
         filterInput.val('');
 
         displayedLabel.text('0');
         totalLabel.text('0');
-    }
+    };
 
     /**
      * Clears the version grid
@@ -386,9 +389,18 @@
     var sort = function (sortDetails, data) {
         // defines a function for sorting
         var comparer = function (a, b) {
-            var aString = nfCommon.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
-            var bString = nfCommon.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
-            return aString === bString ? 0 : aString > bString ? 1 : -1;
+            var aIsBlank = nfCommon.isBlank(a[sortDetails.columnId]);
+            var bIsBlank = nfCommon.isBlank(b[sortDetails.columnId]);
+
+            if (aIsBlank && bIsBlank) {
+                return 0;
+            } else if (aIsBlank) {
+                return 1;
+            } else if (bIsBlank) {
+                return -1;
+            }
+
+            return a[sortDetails.columnId] === b[sortDetails.columnId] ? 0 : a[sortDetails.columnId] > b[sortDetails.columnId] ? 1 : -1;
         };
 
         // perform the sort
@@ -584,7 +596,7 @@
             },
             {
                 id: 'differenceType',
-                name: 'Type',
+                name: 'Change Type',
                 field: 'differenceType',
                 formatter: valueFormatter,
                 sortable: true,
@@ -596,8 +608,7 @@
                 field: 'difference',
                 formatter: valueFormatter,
                 sortable: true,
-                resizable: true,
-                minWidth: 300
+                resizable: true
             },
             {
                 id: 'actions',
@@ -614,22 +625,21 @@
             inlineFilters: false
         });
         localChangesData.setFilterArgs({
-            searchString: '',
-            property: 'componentName'
+            searchString: getFilterText()
         });
         localChangesData.setFilter(filter);
 
         // initialize the sort
         sort({
-            columnId: 'version',
-            sortAsc: false
+            columnId: 'componentName',
+            sortAsc: true
         }, localChangesData);
 
         // initialize the grid
         var localChangesGrid = new Slick.Grid(localChangesTable, localChangesData, localChangesColumns, gridOptions);
         localChangesGrid.setSelectionModel(new Slick.RowSelectionModel());
         localChangesGrid.registerPlugin(new Slick.AutoTooltips());
-        localChangesGrid.setSortColumn('version', false);
+        localChangesGrid.setSortColumn('componentName', true);
         localChangesGrid.onSort.subscribe(function (e, args) {
             sort({
                 columnId: args.sortCol.id,
@@ -650,9 +660,13 @@
                     if (componentDifference.componentType === 'Controller Service') {
                         nfProcessGroupConfiguration.showConfiguration(componentDifference.processGroupId).done(function () {
                             nfProcessGroupConfiguration.selectControllerService(componentDifference.componentId);
+
+                            localChangesTable.closest('.large-dialog').modal('hide');
                         });
                     } else {
-                        nfCanvasUtils.showComponent(componentDifference.processGroupId, componentDifference.componentId);
+                        nfCanvasUtils.showComponent(componentDifference.processGroupId, componentDifference.componentId).done(function () {
+                            localChangesTable.closest('.large-dialog').modal('hide');
+                        });
                     }
                 }
             }
@@ -1240,10 +1254,11 @@
      * Shows local changes for the specified process group.
      *
      * @param processGroupId
+     * @param localChangesMessage
      * @param localChangesTable
      * @param totalLabel
      */
-    var loadLocalChanges = function (processGroupId, localChangesTable, totalLabel) {
+    var loadLocalChanges = function (processGroupId, localChangesMessage, localChangesTable, totalLabel) {
         var localChangesGrid = localChangesTable.data('gridInstance');
         var localChangesData = localChangesGrid.getData();
 
@@ -1255,7 +1270,19 @@
         localChangesGrid.resetActiveCell();
         localChangesData.setItems([]);
 
-        return $.ajax({
+        // load the necessary details
+        var loadMessage = getVersionControlInformation(processGroupId).done(function (response) {
+            if (nfCommon.isDefinedAndNotNull(response.versionControlInformation)) {
+                var vci = response.versionControlInformation;
+                localChangesMessage.text('The following changes have been made to ' + vci.flowName + ' (Version ' + vci.version + ').');
+            } else {
+                nfDialog.showOkDialog({
+                    headerText: 'Change Version',
+                    dialogContent: 'This Process Group is not currently under version control.'
+                });
+            }
+        });
+        var loadChanges = $.ajax({
             type: 'GET',
             url: '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/local-modifications',
             dataType: 'json'
@@ -1292,6 +1319,8 @@
                 });
             }
         }).fail(nfErrorHandler.handleAjaxError);
+
+        return $.when(loadMessage, loadChanges);
     };
 
     /**
@@ -1593,7 +1622,7 @@
             // init the show local changes dialog
             $('#show-local-changes-dialog').modal({
                 scrollableContentStyle: 'scrollable',
-                headerText: 'Local Changes',
+                headerText: 'Show Local Changes',
                 buttons: [{
                     buttonText: 'Close',
                     color: {
@@ -1711,7 +1740,7 @@
          * @param processGroupId
          */
         revertLocalChanges: function (processGroupId) {
-            loadLocalChanges(processGroupId, $('#revert-local-changes-table'), $('#total-revert-local-changes-entries')).done(function () {
+            loadLocalChanges(processGroupId, $('#revert-local-changes-message'), $('#revert-local-changes-table'), $('#total-revert-local-changes-entries')).done(function () {
                 $('#revert-local-changes-process-group-id').text(processGroupId);
                 $('#revert-local-changes-dialog').modal('show');
             });
@@ -1723,7 +1752,7 @@
          * @param processGroupId
          */
         showLocalChanges: function (processGroupId) {
-            loadLocalChanges(processGroupId, $('#show-local-changes-table'), $('#total-show-local-changes-entries')).done(function () {
+            loadLocalChanges(processGroupId, $('#show-local-changes-message'), $('#show-local-changes-table'), $('#total-show-local-changes-entries')).done(function () {
                 $('#show-local-changes-dialog').modal('show');
             });
         },

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
index 0bc6b80..66c5542 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
@@ -280,6 +280,17 @@
                             }
                         });
 
+                    // --------
+                    // comments
+                    // --------
+
+                    details.append('path')
+                        .attr({
+                            'class': 'component-comments',
+                            'transform': 'translate(' + (portData.dimensions.width - 2) + ', ' + (portData.dimensions.height - 10) + ')',
+                            'd': 'm0,0 l0,8 l-8,0 z'
+                        });
+
                     // -------------------
                     // active thread count
                     // -------------------
@@ -321,9 +332,43 @@
                         }).append('title').text(function (d) {
                         return d.component.name;
                     });
+
+                    // update the port comments
+                    port.select('path.component-comments')
+                        .style('visibility', nfCommon.isBlank(portData.component.comments) ? 'hidden' : 'visible')
+                        .each(function () {
+                            // get the tip
+                            var tip = d3.select('#comments-tip-' + portData.id);
+
+                            // if there are validation errors generate a tooltip
+                            if (nfCommon.isBlank(portData.component.comments)) {
+                                // remove the tip if necessary
+                                if (!tip.empty()) {
+                                    tip.remove();
+                                }
+                            } else {
+                                // create the tip if necessary
+                                if (tip.empty()) {
+                                    tip = d3.select('#port-tooltips').append('div')
+                                        .attr('id', function () {
+                                            return 'comments-tip-' + portData.id;
+                                        })
+                                        .attr('class', 'tooltip nifi-tooltip');
+                                }
+
+                                // update the tip
+                                tip.text(portData.component.comments);
+
+                                // add the tooltip
+                                nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                            }
+                        });
                 } else {
                     // clear the port name
                     port.select('text.port-name').text(null);
+
+                    // clear the port comments
+                    port.select('path.component-comments').style('visibility', false);
                 }
 
                 // populate the stats
@@ -519,6 +564,7 @@
             // remove any associated tooltips
             $('#run-status-tip-' + d.id).remove();
             $('#bulletin-tip-' + d.id).remove();
+            $('#comments-tip-' + d.id).remove();
         });
     };
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
index 7a61363..69db138 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
@@ -86,19 +86,6 @@
     // --------------------------
 
     /**
-     * Gets the process group comments.
-     *
-     * @param {object} d
-     */
-    var getProcessGroupComments = function (d) {
-        if (nfCommon.isBlank(d.component.comments)) {
-            return 'No comments specified';
-        } else {
-            return d.component.comments;
-        }
-    };
-
-    /**
      * Determines whether the specified process group is under version control.
      *
      * @param d
@@ -296,6 +283,19 @@
                             'fill': '#e3e8eb'
                         });
 
+                    details.append('rect')
+                        .attr({
+                            'x': 0,
+                            'y': function () {
+                                return processGroupData.dimensions.height - 24;
+                            },
+                            'width': function () {
+                                return processGroupData.dimensions.width;
+                            },
+                            'height': 24,
+                            'fill': '#e3e8eb'
+                        });
+
                     // --------
                     // contents
                     // --------
@@ -397,6 +397,87 @@
                             'class': 'process-group-disabled-count process-group-contents-count'
                         });
 
+                    // current icon
+                    details.append('text')
+                        .attr({
+                            'x': 10,
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-up-to-date process-group-contents-icon',
+                            'font-family': 'FontAwesome'
+                        })
+                        .text('\uf00c');
+
+                    // current count
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-up-to-date-count process-group-contents-count'
+                        });
+
+                    // modified icon
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-locally-modified process-group-contents-icon',
+                            'font-family': 'FontAwesome'
+                        })
+                        .text('\uf069');
+
+                    // modified count
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-locally-modified-count process-group-contents-count'
+                        });
+
+                    // not current icon
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-stale process-group-contents-icon',
+                            'font-family': 'FontAwesome'
+                        })
+                        .text('\uf0aa');
+
+                    // not current count
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-stale-count process-group-contents-count'
+                        });
+
+                    // modified and not current icon
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-locally-modified-and-stale process-group-contents-icon',
+                            'font-family': 'FontAwesome'
+                        })
+                        .text('\uf06a');
+
+                    // modified and not current count
+                    details.append('text')
+                        .attr({
+                            'y': function () {
+                                return processGroupData.dimensions.height - 7;
+                            },
+                            'class': 'process-group-locally-modified-and-stale-count process-group-contents-count'
+                        });
+
                     // ----------------
                     // stats background
                     // ----------------
@@ -676,14 +757,11 @@
                     // comments
                     // --------
 
-                    // process group comments
-                    details.append('text')
+                    details.append('path')
                         .attr({
-                            'x': 10,
-                            'y': 160,
-                            'width': 342,
-                            'height': 22,
-                            'class': 'process-group-comments'
+                            'class': 'component-comments',
+                            'transform': 'translate(' + (processGroupData.dimensions.width - 2) + ', ' + (processGroupData.dimensions.height - 10) + ')',
+                            'd': 'm0,0 l0,8 l-8,0 z'
                         });
 
                     // -------------------
@@ -928,20 +1006,35 @@
                         });
 
                     // update the process group comments
-                    details.select('text.process-group-comments')
-                        .each(function (d) {
-                            var processGroupComments = d3.select(this);
+                    processGroup.select('path.component-comments')
+                        .style('visibility', nfCommon.isBlank(processGroupData.component.comments) ? 'hidden' : 'visible')
+                        .each(function () {
+                            // get the tip
+                            var tip = d3.select('#comments-tip-' + processGroupData.id);
 
-                            // reset the process group name to handle any previous state
-                            processGroupComments.text(null).selectAll('tspan, title').remove();
-
-                            // apply ellipsis to the port name as necessary
-                            nfCanvasUtils.ellipsis(processGroupComments, getProcessGroupComments(d));
-                        }).classed('unset', function (d) {
-                        return nfCommon.isBlank(d.component.comments);
-                    }).append('title').text(function (d) {
-                        return getProcessGroupComments(d);
-                    });
+                            // if there are validation errors generate a tooltip
+                            if (nfCommon.isBlank(processGroupData.component.comments)) {
+                                // remove the tip if necessary
+                                if (!tip.empty()) {
+                                    tip.remove();
+                                }
+                            } else {
+                                // create the tip if necessary
+                                if (tip.empty()) {
+                                    tip = d3.select('#process-group-tooltips').append('div')
+                                        .attr('id', function () {
+                                            return 'comments-tip-' + processGroupData.id;
+                                        })
+                                        .attr('class', 'tooltip nifi-tooltip');
+                                }
+
+                                // update the tip
+                                tip.text(processGroupData.component.comments);
+
+                                // add the tooltip
+                                nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                            }
+                        });
 
                     // update the process group name
                     processGroup.select('text.process-group-name')
@@ -977,12 +1070,102 @@
                         .text(function (d) {
                             return d.component.name;
                         });
+
+                    // up to date current
+                    var upToDate = details.select('text.process-group-up-to-date')
+                        .classed('up-to-date', function (d) {
+                            return d.component.upToDateCount > 0;
+                        })
+                        .classed('zero', function (d) {
+                            return d.component.upToDateCount === 0;
+                        });
+                    var upToDateCount = details.select('text.process-group-up-to-date-count')
+                        .attr('x', function () {
+                            var currentCountX = parseInt(upToDate.attr('x'), 10);
+                            return currentCountX + Math.round(upToDate.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+                        })
+                        .text(function (d) {
+                            return d.component.upToDateCount;
+                        });
+
+                    // update locally modified
+                    var locallyModified = details.select('text.process-group-locally-modified')
+                        .classed('locally-modified', function (d) {
+                            return d.component.locallyModifiedCount > 0;
+                        })
+                        .classed('zero', function (d) {
+                            return d.component.locallyModifiedCount === 0;
+                        })
+                        .attr('x', function () {
+                            var currentX = parseInt(upToDateCount.attr('x'), 10);
+                            return currentX + Math.round(upToDateCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+                        });
+                    var locallyModifiedCount = details.select('text.process-group-locally-modified-count')
+                        .attr('x', function () {
+                            var modifiedCountX = parseInt(locallyModified.attr('x'), 10);
+                            return modifiedCountX + Math.round(locallyModified.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+                        })
+                        .text(function (d) {
+                            return d.component.locallyModifiedCount;
+                        });
+
+                    // update stale
+                    var stale = details.select('text.process-group-stale')
+                        .classed('stale', function (d) {
+                            return d.component.staleCount > 0;
+                        })
+                        .classed('zero', function (d) {
+                            return d.component.staleCount === 0;
+                        })
+                        .attr('x', function () {
+                            var modifiedX = parseInt(locallyModifiedCount.attr('x'), 10);
+                            return modifiedX + Math.round(locallyModifiedCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+                        });
+                    var staleCount = details.select('text.process-group-stale-count')
+                        .attr('x', function () {
+                            var notCurrentCountX = parseInt(stale.attr('x'), 10);
+                            return notCurrentCountX + Math.round(stale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+                        })
+                        .text(function (d) {
+                            return d.component.staleCount;
+                        });
+
+                    // update locally modified and stale
+                    var locallyModifiedAndStale = details.select('text.process-group-locally-modified-and-stale')
+                        .classed('locally-modified-and-stale', function (d) {
+                            return d.component.locallyModifiedAndStaleCount > 0;
+                        })
+                        .classed('zero', function (d) {
+                            return d.component.locallyModifiedAndStaleCount === 0;
+                        })
+                        .attr('x', function () {
+                            var runningX = parseInt(staleCount.attr('x'), 10);
+                            return runningX + Math.round(staleCount.node().getComputedTextLength()) + CONTENTS_SPACER;
+                        });
+                    details.select('text.process-group-locally-modified-and-stale-count')
+                        .attr('x', function () {
+                            var modifiedAndNotCurrentCountX = parseInt(locallyModifiedAndStale.attr('x'), 10);
+                            return modifiedAndNotCurrentCountX + Math.round(locallyModifiedAndStale.node().getComputedTextLength()) + CONTENTS_VALUE_SPACER;
+                        })
+                        .text(function (d) {
+                            return d.component.locallyModifiedAndStaleCount;
+                        });
                 } else {
                     // update version control information
                     processGroup.select('text.version-control').style('visibility', false).text('');
 
                     // clear the process group comments
-                    details.select('text.process-group-comments').text(null);
+                    processGroup.select('path.component-comments').style('visibility', false);
+
+                    // clear the encapsulate versioned pg counts
+                    details.select('text.process-group-up-to-date').style('visibility', false);
+                    details.select('text.process-group-up-to-date-count').style('visibility', false);
+                    details.select('text.process-group-locally-modified').style('visibility', false);
+                    details.select('text.process-group-locally-modified-count').style('visibility', false);
+                    details.select('text.process-group-stale').style('visibility', false);
+                    details.select('text.process-group-stale-count').style('visibility', false);
+                    details.select('text.process-group-locally-modified-and-stale').style('visibility', false);
+                    details.select('text.process-group-locally-modified-and-stale-count').style('visibility', false);
 
                     // clear the process group name
                     processGroup.select('text.process-group-name')
@@ -1136,6 +1319,7 @@
             // remove any associated tooltips
             $('#bulletin-tip-' + d.id).remove();
             $('#version-control-tip-' + d.id).remove();
+            $('#comments-tip-' + d.id).remove();
         });
     };
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
index 1d9b4b2..77b9af8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
@@ -520,6 +520,17 @@
                         })
                         .text('5 min');
 
+                    // --------
+                    // comments
+                    // --------
+
+                    details.append('path')
+                        .attr({
+                            'class': 'component-comments',
+                            'transform': 'translate(' + (processorData.dimensions.width - 2) + ', ' + (processorData.dimensions.height - 10) + ')',
+                            'd': 'm0,0 l0,8 l-8,0 z'
+                        });
+
                     // -------------------
                     // active thread count
                     // -------------------
@@ -608,6 +619,37 @@
                         }).append('title').text(function (d) {
                             return nfCommon.formatBundle(d.component.bundle);
                         });
+
+                    // update the processor comments
+                    processor.select('path.component-comments')
+                        .style('visibility', nfCommon.isBlank(processorData.component.config.comments) ? 'hidden' : 'visible')
+                        .each(function () {
+                            // get the tip
+                            var tip = d3.select('#comments-tip-' + processorData.id);
+
+                            // if there are validation errors generate a tooltip
+                            if (nfCommon.isBlank(processorData.component.config.comments)) {
+                                // remove the tip if necessary
+                                if (!tip.empty()) {
+                                    tip.remove();
+                                }
+                            } else {
+                                // create the tip if necessary
+                                if (tip.empty()) {
+                                    tip = d3.select('#processor-tooltips').append('div')
+                                        .attr('id', function () {
+                                            return 'comments-tip-' + processorData.id;
+                                        })
+                                        .attr('class', 'tooltip nifi-tooltip');
+                                }
+
+                                // update the tip
+                                tip.text(processorData.component.config.comments);
+
+                                // add the tooltip
+                                nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                            }
+                        });
                 } else {
                     // clear the processor name
                     processor.select('text.processor-name').text(null);
@@ -617,6 +659,9 @@
 
                     // clear the processor bundle
                     processor.select('text.processor-bundle').text(null);
+
+                    // clear the processor comments
+                    processor.select('path.component-comments').style('visibility', false);
                 }
 
                 // populate the stats
@@ -903,6 +948,7 @@
             // remove any associated tooltips
             $('#run-status-tip-' + d.id).remove();
             $('#bulletin-tip-' + d.id).remove();
+            $('#comments-tip-' + d.id).remove();
         });
     };
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d34fb5e2/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
index a71b1b8..5de0164 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
@@ -84,19 +84,6 @@
     // --------------------------
 
     /**
-     * Gets the process group comments.
-     *
-     * @param {object} d
-     */
-    var getProcessGroupComments = function (d) {
-        if (nfCommon.isBlank(d.component.comments)) {
-            return 'No comments specified';
-        } else {
-            return d.component.comments;
-        }
-    };
-
-    /**
      * Selects the remote process group elements against the current remote process group map.
      */
     var select = function () {
@@ -182,9 +169,6 @@
         remoteProcessGroup.call(nfSelectable.activate).call(nfContextMenu.activate).call(nfQuickSelect.activate);
     };
 
-    // attempt of space between component count and icon for process group contents
-    var CONTENTS_SPACER = 5;
-
     /**
      * Updates the process groups in the specified selection.
      *
@@ -438,20 +422,6 @@
                         })
                         .text('5 min');
 
-                    // --------
-                    // comments
-                    // --------
-
-                    // process group comments
-                    details.append('text')
-                        .attr({
-                            'x': 10,
-                            'y': 121,
-                            'width': 342,
-                            'height': 22,
-                            'class': 'remote-process-group-comments'
-                        });
-
                     // -------------------
                     // last refreshed time
                     // -------------------
@@ -471,11 +441,22 @@
 
                     details.append('text')
                         .attr({
-                            'x': 370,
+                            'x': 10,
                             'y': 150,
                             'class': 'remote-process-group-last-refresh'
                         });
 
+                    // --------
+                    // comments
+                    // --------
+
+                    details.append('path')
+                        .attr({
+                            'class': 'component-comments',
+                            'transform': 'translate(' + (remoteProcessGroupData.dimensions.width - 2) + ', ' + (remoteProcessGroupData.dimensions.height - 10) + ')',
+                            'd': 'm0,0 l0,8 l-8,0 z'
+                        });
+
                     // -------------------
                     // active thread count
                     // -------------------
@@ -579,21 +560,36 @@
                     // update comments
                     // ---------------
 
-                    // update the process group comments
-                    details.select('text.remote-process-group-comments')
-                        .each(function (d) {
-                            var remoteProcessGroupComments = d3.select(this);
+                    // update the remote process group comments
+                    details.select('path.component-comments')
+                        .style('visibility', nfCommon.isBlank(remoteProcessGroupData.component.comments) ? 'hidden' : 'visible')
+                        .each(function () {
+                            // get the tip
+                            var tip = d3.select('#comments-tip-' + remoteProcessGroupData.id);
+
+                            // if there are validation errors generate a tooltip
+                            if (nfCommon.isBlank(remoteProcessGroupData.component.comments)) {
+                                // remove the tip if necessary
+                                if (!tip.empty()) {
+                                    tip.remove();
+                                }
+                            } else {
+                                // create the tip if necessary
+                                if (tip.empty()) {
+                                    tip = d3.select('#remote-process-group-tooltips').append('div')
+                                        .attr('id', function () {
+                                            return 'comments-tip-' + remoteProcessGroupData.id;
+                                        })
+                                        .attr('class', 'tooltip nifi-tooltip');
+                                }
 
-                            // reset the processor name to handle any previous state
-                            remoteProcessGroupComments.text(null).selectAll('tspan, title').remove();
+                                // update the tip
+                                tip.text(remoteProcessGroupData.component.comments);
 
-                            // apply ellipsis to the port name as necessary
-                            nfCanvasUtils.ellipsis(remoteProcessGroupComments, getProcessGroupComments(d));
-                        }).classed('unset', function (d) {
-                        return nfCommon.isBlank(d.component.comments);
-                    }).append('title').text(function (d) {
-                        return getProcessGroupComments(d);
-                    });
+                                // add the tooltip
+                                nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                            }
+                        });
 
                     // --------------
                     // last refreshed
@@ -628,8 +624,8 @@
                     // clear the transmission secure icon
                     details.select('text.remote-process-group-transmission-secure').text(null);
 
-                    // clear the process group comments
-                    details.select('text.remote-process-group-comments').text(null);
+                    // clear the comments
+                    details.select('path.component-comments').style('visibility', false);
 
                     // clear the last refresh
                     details.select('text.remote-process-group-last-refresh').text(null);
@@ -853,6 +849,7 @@
             $('#bulletin-tip-' + d.id).remove();
             $('#authorization-issues-' + d.id).remove();
             $('#transmission-secure-' + d.id).remove();
+            $('#comments-tip-' + d.id).remove();
         });
     };