You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2021/04/14 09:48:17 UTC

[syncope] branch master updated: [SYNCOPE-1628] Returning errored ConnInstanceTO instead of null in case of troubles with ConnId initialization

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

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 3f799ba  [SYNCOPE-1628] Returning errored ConnInstanceTO instead of null in case of troubles with ConnId initialization
3f799ba is described below

commit 3f799ba940399a1c381991dfc501a43afb6d6fb7
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Apr 14 11:01:00 2021 +0200

    [SYNCOPE-1628] Returning errored ConnInstanceTO instead of null in case of troubles with ConnId initialization
---
 .../syncope/client/console/topology/Topology.java  | 119 +++++++++------------
 .../client/console/topology/TopologyNodePanel.java |  61 ++++-------
 .../console/topology/TopologyTogglePanel.java      |  11 --
 .../topology/TopologyWebSocketBehavior.java        |   8 +-
 .../resources/ui-commons/css/topology.scss         |   6 +-
 .../syncope/common/lib/to/ConnInstanceTO.java      |  14 +++
 .../apache/syncope/core/logic/ConnectorLogic.java  |  15 +--
 .../src/test/resources/domains/MasterContent.xml   |   8 ++
 .../java/data/ConnInstanceDataBinderImpl.java      |  46 +++++---
 .../java/data/ConnInstanceDataBinderTest.java      |  61 +++++++++++
 .../apache/syncope/fit/console/TopologyITCase.java |   4 +-
 .../apache/syncope/fit/core/ConnectorITCase.java   |  40 ++++---
 12 files changed, 224 insertions(+), 169 deletions(-)

diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
index 97fe802..ca67ed0 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
@@ -50,7 +50,6 @@ import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.Bas
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
-import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.IdMEntitlement;
 import org.apache.wicket.Component;
@@ -105,7 +104,7 @@ public class Topology extends BasePage {
             final Map<String, List<ConnInstanceTO>> res = new HashMap<>();
 
             ConnectorRestClient.getAllConnectors().forEach(conn -> {
-                final List<ConnInstanceTO> conns;
+                List<ConnInstanceTO> conns;
                 if (res.containsKey(conn.getLocation())) {
                     conns = res.get(conn.getLocation());
                 } else {
@@ -162,7 +161,7 @@ public class Topology extends BasePage {
             }
         });
 
-        final TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior();
+        TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior();
         body.add(websocket);
 
         togglePanel = new TopologyTogglePanel("toggle", getPageReference());
@@ -171,7 +170,7 @@ public class Topology extends BasePage {
         // -----------------------------------------
         // Add Zoom panel
         // -----------------------------------------
-        final ActionsPanel<Serializable> zoomActionPanel = new ActionsPanel<>("zoom", null);
+        ActionsPanel<Serializable> zoomActionPanel = new ActionsPanel<>("zoom", null);
 
         zoomActionPanel.add(new ActionLink<Serializable>() {
 
@@ -203,25 +202,25 @@ public class Topology extends BasePage {
         syncopeTopologyNode.setX(origX);
         syncopeTopologyNode.setY(origY);
 
-        final URI uri = WebClient.client(BaseRestClient.getSyncopeService()).getBaseURI();
+        URI uri = WebClient.client(BaseRestClient.getSyncopeService()).getBaseURI();
         syncopeTopologyNode.setHost(uri.getHost());
         syncopeTopologyNode.setPort(uri.getPort());
 
-        body.add(topologyNodePanel(Constants.SYNCOPE, syncopeTopologyNode));
+        body.add(topologyNodePanel(Constants.SYNCOPE, syncopeTopologyNode, false));
 
-        final Map<Serializable, Map<Serializable, TopologyNode>> connections = new HashMap<>();
-        final Map<Serializable, TopologyNode> syncopeConnections = new HashMap<>();
+        Map<Serializable, Map<Serializable, TopologyNode>> connections = new HashMap<>();
+        Map<Serializable, TopologyNode> syncopeConnections = new HashMap<>();
         connections.put(syncopeTopologyNode.getKey(), syncopeConnections);
 
         // required to retrieve parent positions
-        final Map<String, TopologyNode> servers = new HashMap<>();
-        final Map<String, TopologyNode> connectors = new HashMap<>();
+        Map<String, TopologyNode> servers = new HashMap<>();
+        Map<String, TopologyNode> connectors = new HashMap<>();
         // -----------------------------------------
 
         // -----------------------------------------
         // Add Connector Servers
         // -----------------------------------------
-        final ListView<URI> connectorServers = new ListView<URI>("connectorServers", csModel.getObject().getLeft()) {
+        ListView<URI> connectorServers = new ListView<URI>("connectorServers", csModel.getObject().getLeft()) {
 
             private static final long serialVersionUID = 6978621871488360380L;
 
@@ -234,10 +233,10 @@ public class Topology extends BasePage {
                 int x = (int) Math.round(origX + kx * Math.cos(Math.PI + Math.PI * (item.getIndex() + 1) / size));
                 int y = (int) Math.round(origY + 100 * Math.sin(Math.PI + Math.PI * (item.getIndex() + 1) / size));
 
-                final URI location = item.getModelObject();
-                final String url = location.toASCIIString();
+                URI location = item.getModelObject();
+                String url = location.toASCIIString();
 
-                final TopologyNode topologynode = new TopologyNode(url, url, TopologyNode.Kind.CONNECTOR_SERVER);
+                TopologyNode topologynode = new TopologyNode(url, url, TopologyNode.Kind.CONNECTOR_SERVER);
 
                 topologynode.setHost(location.getHost());
                 topologynode.setPort(location.getPort());
@@ -246,7 +245,7 @@ public class Topology extends BasePage {
 
                 servers.put(String.class.cast(topologynode.getKey()), topologynode);
 
-                item.add(topologyNodePanel("cs", topologynode));
+                item.add(topologyNodePanel("cs", topologynode, false));
 
                 syncopeConnections.put(url, topologynode);
                 connections.put(url, new HashMap<>());
@@ -260,7 +259,7 @@ public class Topology extends BasePage {
         // -----------------------------------------
         // Add File Paths
         // -----------------------------------------
-        final ListView<URI> filePaths = new ListView<URI>("filePaths", csModel.getObject().getRight()) {
+        ListView<URI> filePaths = new ListView<URI>("filePaths", csModel.getObject().getRight()) {
 
             private static final long serialVersionUID = 6978621871488360380L;
 
@@ -273,10 +272,10 @@ public class Topology extends BasePage {
                 int x = (int) Math.round(origX + kx * Math.cos(Math.PI * (item.getIndex() + 1) / size));
                 int y = (int) Math.round(origY + 100 * Math.sin(Math.PI * (item.getIndex() + 1) / size));
 
-                final URI location = item.getModelObject();
-                final String url = location.toASCIIString();
+                URI location = item.getModelObject();
+                String url = location.toASCIIString();
 
-                final TopologyNode topologynode = new TopologyNode(url, url, TopologyNode.Kind.FS_PATH);
+                TopologyNode topologynode = new TopologyNode(url, url, TopologyNode.Kind.FS_PATH);
 
                 topologynode.setHost(location.getHost());
                 topologynode.setPort(location.getPort());
@@ -285,7 +284,7 @@ public class Topology extends BasePage {
 
                 servers.put(String.class.cast(topologynode.getKey()), topologynode);
 
-                item.add(topologyNodePanel("fp", topologynode));
+                item.add(topologyNodePanel("fp", topologynode, false));
 
                 syncopeConnections.put(url, topologynode);
                 connections.put(url, new HashMap<>());
@@ -299,43 +298,36 @@ public class Topology extends BasePage {
         // -----------------------------------------
         // Add Connector Intances
         // -----------------------------------------
-        final List<List<ConnInstanceTO>> allConns = new ArrayList<>(connModel.getObject().values());
-
-        final ListView<List<ConnInstanceTO>> conns = new ListView<List<ConnInstanceTO>>("conns", allConns) {
+        ListView<List<ConnInstanceTO>> conns =
+                new ListView<List<ConnInstanceTO>>("conns", new ArrayList<>(connModel.getObject().values())) {
 
             private static final long serialVersionUID = 697862187148836036L;
 
             @Override
             protected void populateItem(final ListItem<List<ConnInstanceTO>> item) {
+                int size = item.getModelObject().size() + 1;
 
-                final int size = item.getModelObject().size() + 1;
-
-                final ListView<ConnInstanceTO> conns = new ListView<ConnInstanceTO>("conns", item.getModelObject()) {
+                ListView<ConnInstanceTO> conns = new ListView<ConnInstanceTO>("conns", item.getModelObject()) {
 
                     private static final long serialVersionUID = 6978621871488360381L;
 
                     @Override
                     protected void populateItem(final ListItem<ConnInstanceTO> item) {
-                        final ConnInstanceTO conn = item.getModelObject();
+                        ConnInstanceTO conn = item.getModelObject();
 
-                        final TopologyNode topologynode = new TopologyNode(
+                        TopologyNode topologynode = new TopologyNode(
                                 conn.getKey(),
                                 StringUtils.isBlank(conn.getDisplayName()) // [SYNCOPE-1233]
                                 ? conn.getBundleName() : conn.getDisplayName(),
                                 TopologyNode.Kind.CONNECTOR);
 
                         // Define the parent note
-                        final TopologyNode parent = servers.get(conn.getLocation());
+                        TopologyNode parent = servers.get(conn.getLocation());
 
                         // Set the position
                         int kx = size >= 6 ? 800 : (130 * size);
 
-                        final double hpos;
-                        if (conn.getLocation().startsWith(CONNECTOR_SERVER_LOCATION_PREFIX)) {
-                            hpos = Math.PI;
-                        } else {
-                            hpos = 0.0;
-                        }
+                        double hpos = conn.getLocation().startsWith(CONNECTOR_SERVER_LOCATION_PREFIX) ? Math.PI : 0.0;
 
                         int x = (int) Math.round((Optional.ofNullable(parent).map(TopologyNode::getX).orElse(origX))
                                 + kx * Math.cos(hpos + Math.PI * (item.getIndex() + 1) / size));
@@ -347,11 +339,10 @@ public class Topology extends BasePage {
                         topologynode.setY(y);
 
                         connectors.put(String.class.cast(topologynode.getKey()), topologynode);
-                        item.add(topologyNodePanel("conn", topologynode));
+                        item.add(topologyNodePanel("conn", topologynode, conn.isErrored()));
 
                         // Update connections
-                        final Map<Serializable, TopologyNode> remoteConnections;
-
+                        Map<Serializable, TopologyNode> remoteConnections;
                         if (connections.containsKey(conn.getLocation())) {
                             remoteConnections = connections.get(conn.getLocation());
                         } else {
@@ -374,18 +365,19 @@ public class Topology extends BasePage {
         // -----------------------------------------
         // Add Resources
         // -----------------------------------------
-        final Collection<String> adminConns = new HashSet<>();
+        Collection<String> adminConns = new HashSet<>();
         connModel.getObject().values().forEach(connInstances -> adminConns.addAll(
-                connInstances.stream().map(EntityTO::getKey).collect(Collectors.toList())));
+                connInstances.stream().map(ConnInstanceTO::getKey).collect(Collectors.toList())));
 
-        final Set<String> adminRes = new HashSet<>();
-        final List<String> connToBeProcessed = new ArrayList<>();
+        Set<String> adminRes = new HashSet<>();
+        List<String> connToBeProcessed = new ArrayList<>();
         resModel.getObject().stream().
-                filter((resourceTO) -> (adminConns.contains(resourceTO.getConnector()))).
-                forEachOrdered(resourceTO -> {
-                    final TopologyNode topologynode = new TopologyNode(
+                filter(resourceTO -> adminConns.contains(resourceTO.getConnector())).
+                forEach(resourceTO -> {
+                    TopologyNode topologynode = new TopologyNode(
                             resourceTO.getKey(), resourceTO.getKey(), TopologyNode.Kind.RESOURCE);
-                    final Map<Serializable, TopologyNode> remoteConnections;
+
+                    Map<Serializable, TopologyNode> remoteConnections;
                     if (connections.containsKey(resourceTO.getConnector())) {
                         remoteConnections = connections.get(resourceTO.getConnector());
                     } else {
@@ -401,15 +393,15 @@ public class Topology extends BasePage {
                     }
                 });
 
-        final ListView<String> resources = new ListView<String>("resources", connToBeProcessed) {
+        ListView<String> resources = new ListView<String>("resources", connToBeProcessed) {
 
             private static final long serialVersionUID = 697862187148836038L;
 
             @Override
             protected void populateItem(final ListItem<String> item) {
-                final String connectorKey = item.getModelObject();
+                String connectorKey = item.getModelObject();
 
-                final ListView<TopologyNode> innerListView = new ListView<TopologyNode>("resources",
+                ListView<TopologyNode> innerListView = new ListView<TopologyNode>("resources",
                         new ArrayList<>(connections.get(connectorKey).values())) {
 
                     private static final long serialVersionUID = -3447760771863754342L;
@@ -418,19 +410,14 @@ public class Topology extends BasePage {
 
                     @Override
                     protected void populateItem(final ListItem<TopologyNode> item) {
-                        final TopologyNode topologynode = item.getModelObject();
-                        final TopologyNode parent = connectors.get(connectorKey);
+                        TopologyNode topologynode = item.getModelObject();
+                        TopologyNode parent = connectors.get(connectorKey);
 
                         // Set position
                         int kx = size >= 16 ? 800 : (48 * size);
                         int ky = size < 4 ? 100 : size < 6 ? 350 : 750;
 
-                        final double hpos;
-                        if (parent == null || parent.getY() < syncopeTopologyNode.getY()) {
-                            hpos = Math.PI;
-                        } else {
-                            hpos = 0.0;
-                        }
+                        double hpos = (parent == null || parent.getY() < syncopeTopologyNode.getY()) ? Math.PI : 0.0;
 
                         int x = (int) Math.round((Optional.ofNullable(parent).map(TopologyNode::getX).orElse(origX))
                                 + kx * Math.cos(hpos + Math.PI * (item.getIndex() + 1) / size));
@@ -440,7 +427,7 @@ public class Topology extends BasePage {
                         topologynode.setX(x);
                         topologynode.setY(y);
 
-                        item.add(topologyNodePanel("res", topologynode));
+                        item.add(topologyNodePanel("res", topologynode, false));
                     }
                 };
 
@@ -456,7 +443,7 @@ public class Topology extends BasePage {
         // -----------------------------------------
         // Create connections
         // -----------------------------------------
-        final WebMarkupContainer jsPlace = new WebMarkupContainerNoVeil("jsPlace");
+        WebMarkupContainer jsPlace = new WebMarkupContainerNoVeil("jsPlace");
         jsPlace.setOutputMarkupId(true);
         body.add(jsPlace);
 
@@ -514,7 +501,7 @@ public class Topology extends BasePage {
 
             @Override
             protected void populateItem(final ListItem<TopologyNode> item) {
-                item.add(topologyNodePanel("el", item.getModelObject()));
+                item.add(topologyNodePanel("el", item.getModelObject(), false));
             }
         };
         newlyCreated.setOutputMarkupId(true);
@@ -528,16 +515,16 @@ public class Topology extends BasePage {
 
         targets.forEach((key, value) -> value.forEach((label, node) -> list.add(
                 String.format("connect('%s','%s','%s');", key, label, node.getKind()))));
+
         return list;
     }
 
-    private TopologyNodePanel topologyNodePanel(final String id, final TopologyNode node) {
-
-        final TopologyNodePanel panel = new TopologyNodePanel(id, node);
+    private TopologyNodePanel topologyNodePanel(final String id, final TopologyNode node, final boolean errored) {
+        TopologyNodePanel panel = new TopologyNodePanel(id, node, errored);
         panel.setMarkupId(String.valueOf(node.getKey()));
         panel.setOutputMarkupId(true);
 
-        final List<Behavior> behaviors = new ArrayList<>();
+        List<Behavior> behaviors = new ArrayList<>();
 
         behaviors.add(new Behavior() {
 
@@ -580,9 +567,9 @@ public class Topology extends BasePage {
         super.onEvent(event);
 
         if (event.getPayload() instanceof CreateEvent) {
-            final CreateEvent resourceCreateEvent = CreateEvent.class.cast(event.getPayload());
+            CreateEvent resourceCreateEvent = CreateEvent.class.cast(event.getPayload());
 
-            final TopologyNode node = new TopologyNode(
+            TopologyNode node = new TopologyNode(
                     resourceCreateEvent.getKey(),
                     resourceCreateEvent.getDisplayName(),
                     resourceCreateEvent.getKind());
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNodePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNodePanel.java
index b5907de..44f2e25 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNodePanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNodePanel.java
@@ -25,12 +25,8 @@ import org.apache.syncope.client.console.topology.TopologyNode.Kind;
 import org.apache.syncope.client.console.topology.TopologyTogglePanel.UpdateEvent;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.wicket.AttributeModifier;
-import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.IAjaxIndicatorAware;
 import org.apache.wicket.behavior.AttributeAppender;
-import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.event.IEvent;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Panel;
@@ -49,38 +45,42 @@ public class TopologyNodePanel extends Panel implements IAjaxIndicatorAware {
 
     }
 
-    public TopologyNodePanel(final String id, final TopologyNode node) {
+    public TopologyNodePanel(final String id, final TopologyNode node, final boolean errored) {
         super(id);
         this.node = node;
 
-        final String resourceName = node.getDisplayName().length() > 14
-                ? node.getDisplayName().subSequence(0, 10) + "..."
-                : node.getDisplayName();
-
-        label = new Label("label", resourceName);
+        label = new Label("label", StringUtils.abbreviate(node.getDisplayName(), 10));
         label.setOutputMarkupId(true);
         add(label);
 
-        final String title;
-
+        String title;
         switch (node.getKind()) {
             case SYNCOPE:
                 title = "";
                 add(new AttributeAppender("class", "topology_root", " "));
                 break;
+
             case CONNECTOR_SERVER:
                 title = node.getDisplayName();
                 add(new AttributeAppender("class", "topology_cs", " "));
                 break;
+
             case FS_PATH:
                 title = node.getDisplayName();
                 add(new AttributeAppender("class", "topology_cs", " "));
                 break;
+
             case CONNECTOR:
                 title = (StringUtils.isBlank(node.getConnectionDisplayName())
                         ? "" : node.getConnectionDisplayName() + ':') + node.getDisplayName();
-                add(new AttributeAppender("class", "topology_conn", " "));
+                if (errored) {
+                    add(new AttributeAppender("class", "topology_conn_errored", " "));
+                } else {
+                    add(new AttributeAppender("class", "topology_conn", " "));
+                }
                 break;
+
+            case RESOURCE:
             default:
                 title = node.getDisplayName().length() > 14 ? node.getDisplayName() : "";
                 add(new AttributeAppender("class", "topology_res", " "));
@@ -94,21 +94,6 @@ public class TopologyNodePanel extends Panel implements IAjaxIndicatorAware {
     }
 
     @Override
-    public final MarkupContainer add(final Component... childs) {
-        return super.add(childs);
-    }
-
-    @Override
-    public final Component add(final Behavior... behaviors) {
-        return super.add(behaviors);
-    }
-
-    @Override
-    public final Component setMarkupId(final String markupId) {
-        return super.setMarkupId(markupId);
-    }
-
-    @Override
     public String getAjaxIndicatorMarkupId() {
         return Constants.VEIL_INDICATOR_MARKUP_ID;
     }
@@ -116,26 +101,20 @@ public class TopologyNodePanel extends Panel implements IAjaxIndicatorAware {
     @Override
     public void onEvent(final IEvent<?> event) {
         if (event.getPayload() instanceof UpdateEvent) {
-            final UpdateEvent updateEvent = UpdateEvent.class.cast(event.getPayload());
-            final String key = updateEvent.getKey();
-            final AjaxRequestTarget target = updateEvent.getTarget();
+            UpdateEvent updateEvent = UpdateEvent.class.cast(event.getPayload());
+            String key = updateEvent.getKey();
 
             if (node.getKind() == Kind.CONNECTOR && key.equalsIgnoreCase(node.getKey())) {
                 ConnInstanceTO conn = ConnectorRestClient.read(key);
 
-                String displayName =
-                        // [SYNCOPE-1233]
-                        StringUtils.isBlank(conn.getDisplayName()) ? conn.getBundleName() : conn.getDisplayName();
+                // [SYNCOPE-1233]
+                String displayName = StringUtils.isBlank(conn.getDisplayName())
+                        ? conn.getBundleName() : conn.getDisplayName();
 
-                final String resourceName = displayName.length() > 14
-                        ? displayName.subSequence(0, 10) + "..."
-                        : displayName;
-
-                label.setDefaultModelObject(resourceName);
-                target.add(label);
+                label.setDefaultModelObject(StringUtils.abbreviate(displayName, 10));
+                updateEvent.getTarget().add(label);
                 node.setDisplayName(displayName);
             }
         }
     }
-
 }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
index e941250..9e7c0a3 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
@@ -248,7 +248,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         fragment.add(create);
         MetaDataRoleAuthorizationStrategy.authorize(create, RENDER, IdMEntitlement.CONNECTOR_CREATE);
@@ -336,7 +335,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, IdMEntitlement.CONNECTOR_READ);
         fragment.add(edit);
@@ -385,7 +383,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(history, RENDER,
                 String.format("%s,%s", IdMEntitlement.CONNECTOR_READ, IdRepoEntitlement.AUDIT_LIST));
@@ -468,7 +465,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(status, RENDER, IdRepoEntitlement.USER_UPDATE);
         fragment.add(status);
@@ -504,7 +500,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, IdMEntitlement.RESOURCE_READ);
         fragment.add(provision);
@@ -526,7 +521,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(explore, RENDER, IdMEntitlement.RESOURCE_LIST_CONNOBJECT);
         fragment.add(explore);
@@ -549,7 +543,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(propagation, RENDER, IdRepoEntitlement.TASK_LIST);
         fragment.add(propagation);
@@ -569,7 +562,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(pull, RENDER, IdRepoEntitlement.TASK_LIST);
         fragment.add(pull);
@@ -589,7 +581,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(push, RENDER, IdRepoEntitlement.TASK_LIST);
         fragment.add(push);
@@ -638,7 +629,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(history, RENDER,
                 String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST));
@@ -731,6 +721,5 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         public String getKey() {
             return key;
         }
-
     }
 }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java
index dd88cba..f70d6dd 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyWebSocketBehavior.java
@@ -47,14 +47,14 @@ public class TopologyWebSocketBehavior extends WebSocketBehavior {
 
     private static final long serialVersionUID = -1653665542635275551L;
 
-    private static final String CONNECTOR_TEST_TIMEOUT_PARAMETER = "connector.test.timeout";
-
-    private static final String RESOURCE_TEST_TIMEOUT_PARAMETER = "resource.test.timeout";
-
     private static final Logger LOG = LoggerFactory.getLogger(TopologyWebSocketBehavior.class);
 
     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
 
+    private static final String CONNECTOR_TEST_TIMEOUT_PARAMETER = "connector.test.timeout";
+
+    private static final String RESOURCE_TEST_TIMEOUT_PARAMETER = "resource.test.timeout";
+
     private static void timeoutHandlingConnectionChecker(
             final Checker checker,
             final Integer timeout,
diff --git a/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/topology.scss b/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/topology.scss
index 61c33b5..f435ed1 100644
--- a/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/topology.scss
+++ b/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/topology.scss
@@ -73,6 +73,10 @@
   background-color: rgba(100, 200, 200, 0.08);
 }
 
+.topology_conn_errored {
+  background-color:rgba(217,83,79,0.56);
+}
+
 .topology_res {
   background-color: rgba(250, 230, 0, 0.09);
 }
@@ -232,4 +236,4 @@ div.alert-widget > a > .badge {
   top: 0;
   right: 25px;
   z-index: 1;
-}
\ No newline at end of file
+}
diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
index 08f93ad..6dcdb7b 100644
--- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
+++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
@@ -21,6 +21,7 @@ package org.apache.syncope.common.lib.to;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
@@ -38,6 +39,8 @@ public class ConnInstanceTO implements EntityTO {
 
     private String key;
 
+    private boolean errored;
+
     private String adminRealm;
 
     private String location;
@@ -69,6 +72,15 @@ public class ConnInstanceTO implements EntityTO {
         this.key = key;
     }
 
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public boolean isErrored() {
+        return errored;
+    }
+
+    public void setErrored(final boolean errored) {
+        this.errored = errored;
+    }
+
     public String getAdminRealm() {
         return adminRealm;
     }
@@ -176,6 +188,7 @@ public class ConnInstanceTO implements EntityTO {
         ConnInstanceTO other = (ConnInstanceTO) obj;
         return new EqualsBuilder().
                 append(key, other.key).
+                append(errored, other.errored).
                 append(adminRealm, other.adminRealm).
                 append(location, other.location).
                 append(connectorName, other.connectorName).
@@ -193,6 +206,7 @@ public class ConnInstanceTO implements EntityTO {
     public int hashCode() {
         return new HashCodeBuilder().
                 append(key).
+                append(errored).
                 append(adminRealm).
                 append(location).
                 append(connectorName).
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
index e08df21..a3c5d53 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
@@ -22,7 +22,6 @@ import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -142,19 +141,7 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
     public List<ConnInstanceTO> list(final String lang) {
         CurrentLocale.set(StringUtils.isBlank(lang) ? Locale.ENGLISH : new Locale(lang));
 
-        return connInstanceDAO.findAll().stream().
-                filter(Objects::nonNull).
-                map(connInstance -> {
-                    ConnInstanceTO result = null;
-                    try {
-                        result = binder.getConnInstanceTO(connInstance);
-                    } catch (NotFoundException e) {
-                        LOG.error("Connector '{}#{}' not found",
-                                connInstance.getBundleName(), connInstance.getVersion());
-                    }
-
-                    return result;
-                }).collect(Collectors.toList());
+        return connInstanceDAO.findAll().stream().map(binder::getConnInstanceTO).collect(Collectors.toList());
     }
 
     @PreAuthorize("hasRole('" + IdMEntitlement.CONNECTOR_READ + "')")
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index b3fc14e..c424fea 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -578,6 +578,14 @@ under the License.
   <PushCorrelationRuleEntity id="24463935-32a0-4272-bc78-04d6d0adc69e" pushPolicy_id="fb6530e5-892d-4f47-a46b-180c5b6c5c83" 
                              anyType_id="USER" implementation_id="TestPushCorrelationRule"/>
   
+  <ConnInstance id="413bf072-678a-41d3-9d20-8c453b3a39d1" displayName="Errored"
+                adminRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
+                location="${connid.location}"
+                bundleName="net.tirasa.connid.bundles.missing"
+                connectorName="net.tirasa.connid.bundles.missing.MissingConnector"
+                version="none"
+                jsonConf='[]'/>
+
   <ConnInstance id="88a7a819-dab5-46b4-9b90-0b9769eabdb8" displayName="ConnInstance100"
                 adminRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
                 location="${connid.location}"
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
index 9502a5d..1d5f396 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
@@ -43,12 +43,16 @@ import org.identityconnectors.framework.api.ConfigurationProperties;
 import org.identityconnectors.framework.api.ConfigurationProperty;
 import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl;
 import org.identityconnectors.framework.api.ConnectorInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 @Component
 public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
 
+    private static final Logger LOG = LoggerFactory.getLogger(ConnInstanceDataBinder.class);
+
     @Autowired
     private ConnIdBundleManager connIdBundleManager;
 
@@ -210,8 +214,6 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
 
     @Override
     public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) {
-        Pair<URI, ConnectorInfo> info = connIdBundleManager.getConnectorInfo(connInstance);
-
         ConnInstanceTO connInstanceTO = new ConnInstanceTO();
         connInstanceTO.setKey(connInstance.getKey());
         connInstanceTO.setBundleName(connInstance.getBundleName());
@@ -220,21 +222,35 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
         connInstanceTO.setDisplayName(connInstance.getDisplayName());
         connInstanceTO.setConnRequestTimeout(connInstance.getConnRequestTimeout());
         connInstanceTO.setAdminRealm(connInstance.getAdminRealm().getFullPath());
-        connInstanceTO.setLocation(info.getLeft().toASCIIString());
         connInstanceTO.getCapabilities().addAll(connInstance.getCapabilities());
         connInstanceTO.getConf().addAll(connInstance.getConf());
-        // refresh stored properties in the given connInstance with direct information from underlying connector
-        ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(info.getRight());
-        properties.getPropertyNames().forEach(propName -> {
-            ConnConfPropSchema schema = build(properties.getProperty(propName));
-
-            Optional<ConnConfProperty> property = connInstanceTO.getConf(propName);
-            if (property.isEmpty()) {
-                property = Optional.of(new ConnConfProperty());
-                connInstanceTO.getConf().add(property.get());
-            }
-            property.get().setSchema(schema);
-        });
+
+        try {
+            Pair<URI, ConnectorInfo> info = connIdBundleManager.getConnectorInfo(connInstance);
+
+            connInstanceTO.setLocation(info.getLeft().toASCIIString());
+
+            // refresh stored properties in the given connInstance with direct information from underlying connector
+            ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(info.getRight());
+            properties.getPropertyNames().forEach(propName -> {
+                ConnConfPropSchema schema = build(properties.getProperty(propName));
+
+                Optional<ConnConfProperty> property = connInstanceTO.getConf(propName);
+                if (property.isEmpty()) {
+                    property = Optional.of(new ConnConfProperty());
+                    connInstanceTO.getConf().add(property.get());
+                }
+                property.get().setSchema(schema);
+            });
+        } catch (Exception e) {
+            LOG.error("Could not get ConnId information for {} / {}#{}#{}",
+                    connInstance.getLocation(), connInstance.getBundleName(), connInstance.getConnectorName(),
+                    connInstance.getVersion(), e);
+
+            connInstanceTO.setErrored(true);
+            connInstanceTO.setLocation(connInstance.getLocation());
+        }
+
         Collections.sort(connInstanceTO.getConf());
 
         // pool configuration
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java
new file mode 100644
index 0000000..1162209
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.syncope.core.provisioning.java.data;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ConnInstanceDataBinderTest extends AbstractTest {
+
+    @Autowired
+    private ConnInstanceDataBinder binder;
+
+    @Autowired
+    private ConnInstanceDAO connInstanceDAO;
+
+    @Test
+    public void working() {
+        ConnInstanceTO connInstance = binder.getConnInstanceTO(
+                connInstanceDAO.find("88a7a819-dab5-46b4-9b90-0b9769eabdb8"));
+        assertNotNull(connInstance);
+        assertFalse(connInstance.isErrored());
+        assertNotNull(connInstance.getLocation());
+        assertFalse(connInstance.getConf().isEmpty());
+    }
+
+    @Test
+    public void errored() {
+        ConnInstanceTO connInstance = binder.getConnInstanceTO(
+                connInstanceDAO.find("413bf072-678a-41d3-9d20-8c453b3a39d1"));
+        assertNotNull(connInstance);
+        assertTrue(connInstance.isErrored());
+        assertNotNull(connInstance.getLocation());
+        assertTrue(connInstance.getConf().isEmpty());
+    }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java
index 6e1e142..88556b9 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java
@@ -160,10 +160,10 @@ public class TopologyITCase extends AbstractConsoleITCase {
 
     @Test
     public void createNewResurceAndProvisionRules() {
-        final String res = UUID.randomUUID().toString();
+        String res = UUID.randomUUID().toString();
 
         TESTER.executeAjaxEvent(
-                "body:conns:0:conns:0:conn", Constants.ON_CLICK);
+                "body:conns:0:conns:1:conn", Constants.ON_CLICK);
         TESTER.executeAjaxEvent(
                 "body:toggle:container:content:togglePanelContainer:container:actions:create", Constants.ON_CLICK);
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
index 1084df7..d0ad892 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
@@ -285,17 +285,27 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void list() {
-        List<ConnInstanceTO> connectorInstanceTOs = connectorService.list(null);
-        assertNotNull(connectorInstanceTOs);
-        assertFalse(connectorInstanceTOs.isEmpty());
-        connectorInstanceTOs.forEach(Assertions::assertNotNull);
+        List<ConnInstanceTO> connInstances = connectorService.list(null);
+        assertNotNull(connInstances);
+        assertFalse(connInstances.isEmpty());
+        connInstances.forEach(Assertions::assertNotNull);
     }
 
     @Test
     public void read() {
-        ConnInstanceTO connectorInstanceTO = connectorService.read(
+        ConnInstanceTO connInstance = connectorService.read(
                 "88a7a819-dab5-46b4-9b90-0b9769eabdb8", Locale.ENGLISH.getLanguage());
-        assertNotNull(connectorInstanceTO);
+        assertNotNull(connInstance);
+        assertFalse(connInstance.isErrored());
+        assertNotNull(connInstance.getLocation());
+        assertFalse(connInstance.getConf().isEmpty());
+
+        connInstance = connectorService.read(
+                "413bf072-678a-41d3-9d20-8c453b3a39d1", Locale.ENGLISH.getLanguage());
+        assertNotNull(connInstance);
+        assertTrue(connInstance.isErrored());
+        assertNotNull(connInstance.getLocation());
+        assertTrue(connInstance.getConf().isEmpty());
     }
 
     @Test
@@ -332,18 +342,18 @@ public class ConnectorITCase extends AbstractITCase {
     @Test
     public void checkSelectedLanguage() {
         // 1. Check Italian
-        List<ConnInstanceTO> connectorInstanceTOs = connectorService.list("it");
+        List<ConnInstanceTO> connInstances = connectorService.list("it");
 
-        for (ConnInstanceTO instance : connectorInstanceTOs) {
+        for (ConnInstanceTO instance : connInstances) {
             if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
                 assertEquals("Utente", instance.getConf("user").get().getSchema().getDisplayName());
             }
         }
 
         // 2. Check English (default)
-        connectorInstanceTOs = connectorService.list(null);
+        connInstances = connectorService.list(null);
 
-        for (ConnInstanceTO instance : connectorInstanceTOs) {
+        for (ConnInstanceTO instance : connInstances) {
             if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
                 assertEquals("User", instance.getConf("user").get().getSchema().getDisplayName());
             }
@@ -712,15 +722,15 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void issueSYNCOPE605() {
-        ConnInstanceTO connectorInstanceTO = connectorService.read(
+        ConnInstanceTO connInstance = connectorService.read(
                 "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());
-        assertTrue(connectorInstanceTO.getCapabilities().isEmpty());
+        assertTrue(connInstance.getCapabilities().isEmpty());
 
-        connectorInstanceTO.getCapabilities().add(ConnectorCapability.SEARCH);
-        connectorService.update(connectorInstanceTO);
+        connInstance.getCapabilities().add(ConnectorCapability.SEARCH);
+        connectorService.update(connInstance);
 
         ConnInstanceTO updatedCapabilities = connectorService.read(
-                connectorInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
+                connInstance.getKey(), Locale.ENGLISH.getLanguage());
         assertNotNull(updatedCapabilities.getCapabilities());
         assertTrue(updatedCapabilities.getCapabilities().size() == 1);
     }