From 1c59d6c7d4e33c5b88fbe0692c1d50c0eab74c4a Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Thu, 20 Feb 2014 14:08:01 +0000
Subject: [PATCH] OPENDJ-1271 (CR-3008) dsreplication pre-external-initialization task fails with STOPPED_BY_ERROR

---
 opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java |  532 ++++++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 379 insertions(+), 153 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java b/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java
index c5f305f..d9e6fca 100644
--- a/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java
+++ b/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java
@@ -173,11 +173,7 @@
   // @NotNull // for the reference
   private final AtomicReference<ConnectedRS> connectedRS =
       new AtomicReference<ConnectedRS>(ConnectedRS.noConnectedRS());
-  /**
-   * Our replication domain.
-   * <p>
-   * Can be null for unit test purpose.
-   */
+  /** Our replication domain. */
   private ReplicationDomain domain;
   /**
    * This object is used as a conditional event to be notified about
@@ -217,25 +213,14 @@
   /*
    * Properties for the last topology info received from the network.
    */
-  /**
-   * Info for other DSs.
-   * <p>
-   * Warning: does not contain info for us (for our server id)
-   */
-  private volatile List<DSInfo> dsList = new ArrayList<DSInfo>();
-  private volatile long generationID;
+  /** Contains the last known state of the replication topology. */
+  private final AtomicReference<Topology> topology =
+      new AtomicReference<Topology>(new Topology());
+  /** <pre>@GuardedBy("this")</pre>. */
   private volatile int updateDoneCount = 0;
   private volatile boolean connectRequiresRecovery = false;
 
   /**
-   * The map of replication server info initialized at connection time and
-   * regularly updated. This is used to decide to which best suitable
-   * replication server one wants to connect. Key: replication server id Value:
-   * replication server info for the matching replication server id
-   */
-  private volatile Map<Integer, ReplicationServerInfo> replicationServerInfos;
-
-  /**
    * This integer defines when the best replication server checking algorithm
    * should be engaged.
    * Every time a monitoring message (each monitoring publisher period) is
@@ -266,19 +251,16 @@
    * @param state The ServerState that should be used by this broker
    *        when negotiating the session with the replicationServer.
    * @param config The configuration to use.
-   * @param generationId The generationId for the server associated to the
-   * provided serverId and for the domain associated to the provided baseDN.
    * @param replSessionSecurity The session security configuration.
    */
   public ReplicationBroker(ReplicationDomain replicationDomain,
-      ServerState state, ReplicationDomainCfg config, long generationId,
+      ServerState state, ReplicationDomainCfg config,
       ReplSessionSecurity replSessionSecurity)
   {
     this.domain = replicationDomain;
     this.state = state;
     this.config = config;
     this.replSessionSecurity = replSessionSecurity;
-    this.generationID = generationId;
     this.rcvWindow = getMaxRcvWindow();
     this.halfRcvWindow = rcvWindow / 2;
 
@@ -352,8 +334,7 @@
    */
   private long getGenerationID()
   {
-    generationID = domain.getGenerationID();
-    return generationID;
+    return domain.getGenerationID();
   }
 
   /**
@@ -362,38 +343,7 @@
    */
   public void setGenerationID(long generationID)
   {
-    this.generationID = generationID;
-  }
-
-  /**
-   * Sets the locally configured flag for the passed ReplicationServerInfo
-   * object, analyzing the local configuration.
-   * @param rsInfo the Replication server to check and update
-   */
-  private void setLocallyConfiguredFlag(ReplicationServerInfo rsInfo)
-  {
-    // Determine if the passed ReplicationServerInfo has a URL that is present
-    // in the locally configured replication servers
-    String rsUrl = rsInfo.getServerURL();
-    if (rsUrl == null)
-    {
-      // The ReplicationServerInfo has been generated from a server with
-      // no URL in TopologyMsg (i.e: with replication protocol version < 4):
-      // ignore this server as we do not know how to connect to it
-      rsInfo.setLocallyConfigured(false);
-      return;
-    }
-    for (String serverUrl : getReplicationServerUrls())
-    {
-      if (isSameReplicationServerUrl(serverUrl, rsUrl))
-      {
-        // This RS is locally configured, mark this
-        rsInfo.setLocallyConfigured(true);
-        rsInfo.setServerURL(serverUrl);
-        return;
-      }
-    }
-    rsInfo.setLocallyConfigured(false);
+    domain.setGenerationID(generationID);
   }
 
   /**
@@ -485,7 +435,7 @@
 
       // Unsupported message type: should not happen
       throw new IllegalArgumentException("Unexpected PDU type: "
-          + msg.getClass().getName() + " :\n" + msg);
+          + msg.getClass().getName() + ":\n" + msg);
     }
 
     /**
@@ -733,8 +683,10 @@
     @Override
     public String toString()
     {
-      return "Url:" + getServerURL() + " ServerId:" + getServerId()
-          + " GroupId:" + getGroupId();
+      return "ReplServerInfo Url:" + getServerURL()
+          + " ServerId:" + getServerId()
+          + " GroupId:" + getGroupId()
+          + " connectedDSs:" + connectedDSs;
     }
   }
 
@@ -860,9 +812,11 @@
             + "elect the preferred one");
 
       // Get info from every available replication servers
-      replicationServerInfos = collectReplicationServersInfo();
+      Map<Integer, ReplicationServerInfo> rsInfos =
+          collectReplicationServersInfo();
+      computeNewTopology(toRSInfos(rsInfos));
 
-      if (replicationServerInfos.isEmpty())
+      if (rsInfos.isEmpty())
       {
         setConnectedRS(ConnectedRS.noConnectedRS());
       }
@@ -870,7 +824,7 @@
       {
         // At least one server answered, find the best one.
         RSEvaluations evals = computeBestReplicationServer(true, -1, state,
-            replicationServerInfos, serverId, getGroupId(), getGenerationID());
+            rsInfos, serverId, getGroupId(), getGenerationID());
 
         // Best found, now initialize connection to this one (handshake phase 1)
         if (logger.isTraceEnabled())
@@ -886,8 +840,7 @@
           Update replication server info with potentially more up to date
           data (server state for instance may have changed)
           */
-          replicationServerInfos
-              .put(electedRsInfo.getServerId(), electedRsInfo);
+          rsInfos.put(electedRsInfo.getServerId(), electedRsInfo);
 
           // Handshake phase 1 exchange went well
 
@@ -935,10 +888,10 @@
           connectionError = true;
           connectPhaseLock.notify();
 
-          if (replicationServerInfos.size() > 0)
+          if (rsInfos.size() > 0)
           {
             logger.warn(WARN_COULD_NOT_FIND_CHANGELOG, serverId, baseDN.toNormalizedString(),
-                Utils.joinAsString(", ", replicationServerInfos.keySet()));
+                Utils.joinAsString(", ", rsInfos.keySet()));
           }
           else
           {
@@ -949,6 +902,43 @@
     }
   }
 
+  private void computeNewTopology(List<RSInfo> newRSInfos)
+  {
+    final int rsServerId = getRsServerId();
+
+    Topology oldTopo;
+    Topology newTopo;
+    do
+    {
+      oldTopo = topology.get();
+      newTopo = new Topology(oldTopo.replicaInfos, newRSInfos, getServerId(),
+          rsServerId, getReplicationServerUrls(), oldTopo.rsInfos);
+    }
+    while (!topology.compareAndSet(oldTopo, newTopo));
+
+    if (logger.isTraceEnabled())
+    {
+      debugInfo(topologyChange(rsServerId, oldTopo, newTopo));
+    }
+  }
+
+  private StringBuilder topologyChange(int rsServerId, Topology oldTopo,
+      Topology newTopo)
+  {
+    final StringBuilder sb = new StringBuilder();
+    sb.append("rsServerId=").append(rsServerId);
+    if (newTopo.equals(oldTopo))
+    {
+      sb.append(", unchangedTopology=").append(newTopo);
+    }
+    else
+    {
+      sb.append(", oldTopology=").append(oldTopo);
+      sb.append(", newTopology=").append(newTopo);
+    }
+    return sb;
+  }
+
   /**
    * Connects to a replication server.
    *
@@ -2303,7 +2293,7 @@
     if (logger.isTraceEnabled())
     {
       debugInfo("end restart : connected=" + rs.isConnected() + " with RS("
-          + rs.getServerId() + ") genId=" + generationID);
+          + rs.getServerId() + ") genId=" + getGenerationID());
     }
   }
 
@@ -2408,7 +2398,8 @@
           */
           credit =
             currentWindowSemaphore.tryAcquire(500, TimeUnit.MILLISECONDS);
-        } else
+        }
+        else
         {
           credit = true;
         }
@@ -2451,6 +2442,11 @@
       }
       catch (IOException e)
       {
+        if (logger.isTraceEnabled())
+        {
+          debugInfo("publish(): IOException caught: "
+              + stackTraceToSingleLineString(e));
+        }
         if (!retryOnFailure)
         {
           return false;
@@ -2463,23 +2459,24 @@
           try
           {
             connectPhaseLock.wait(100);
-          } catch (InterruptedException e1)
+          }
+          catch (InterruptedException ignored)
           {
-            // ignore
             if (logger.isTraceEnabled())
             {
-              debugInfo("publish(): Interrupted exception raised : "
-                  + e.getLocalizedMessage());
+              debugInfo("publish(): InterruptedException caught 1: "
+                  + stackTraceToSingleLineString(ignored));
             }
           }
         }
-      } catch (InterruptedException e)
+      }
+      catch (InterruptedException ignored)
       {
         // just loop.
         if (logger.isTraceEnabled())
         {
-          debugInfo("publish(): Interrupted exception raised."
-              + e.getLocalizedMessage());
+          debugInfo("publish(): InterruptedException caught 2: "
+              + stackTraceToSingleLineString(ignored));
         }
       }
     }
@@ -2607,9 +2604,10 @@
           }
 
           // Update the replication servers ServerStates with new received info
+          Map<Integer, ReplicationServerInfo> rsInfos = topology.get().rsInfos;
           for (int srvId : toIterable(monitorMsg.rsIterator()))
           {
-            ReplicationServerInfo rsInfo = replicationServerInfos.get(srvId);
+            final ReplicationServerInfo rsInfo = rsInfos.get(srvId);
             if (rsInfo != null)
             {
               rsInfo.update(monitorMsg.getRSServerState(srvId));
@@ -2629,9 +2627,9 @@
             {
               // Stable topology (no topo msg since few seconds): proceed with
               // best server checking.
-              final RSEvaluations evals =
-                  computeBestReplicationServer(false, previousRsServerID, state,
-                  replicationServerInfos, serverId, getGroupId(), generationID);
+              final RSEvaluations evals = computeBestReplicationServer(
+                  false, previousRsServerID, state,
+                  rsInfos, serverId, getGroupId(), getGenerationID());
               final ReplicationServerInfo bestServerInfo = evals.getBestRS();
               if (previousRsServerID != -1
                   && (bestServerInfo == null
@@ -2951,9 +2949,9 @@
    * Gets the info for DSs in the topology (except us).
    * @return The info for DSs in the topology (except us)
    */
-  public List<DSInfo> getDsList()
+  public Map<Integer, DSInfo> getReplicaInfos()
   {
-    return dsList;
+    return topology.get().replicaInfos;
   }
 
   /**
@@ -2962,10 +2960,15 @@
    * @return The info for RSs in the topology (except the one we are connected
    * to)
    */
-  public List<RSInfo> getRsList()
+  public List<RSInfo> getRsInfos()
+  {
+    return toRSInfos(topology.get().rsInfos);
+  }
+
+  private List<RSInfo> toRSInfos(Map<Integer, ReplicationServerInfo> rsInfos)
   {
     final List<RSInfo> result = new ArrayList<RSInfo>();
-    for (ReplicationServerInfo rsInfo : replicationServerInfos.values())
+    for (ReplicationServerInfo rsInfo : rsInfos.values())
     {
       result.add(rsInfo.toRSInfo());
     }
@@ -2973,39 +2976,6 @@
   }
 
   /**
-   * Computes the list of DSs connected to a particular RS.
-   * @param rsId The RS id of the server one wants to know the connected DSs
-   * @param dsList The list of DSinfo from which to compute things
-   * @param rsServerId the serverId to use for the connectedDS
-   * @return The list of connected DSs to the server rsId
-   */
-  private Set<Integer> computeConnectedDSs(int rsId, List<DSInfo> dsList,
-      int rsServerId)
-  {
-    final Set<Integer> connectedDSs = new HashSet<Integer>();
-    if (rsServerId == rsId)
-    {
-      /*
-      If we are computing connected DSs for the RS we are connected
-      to, we should count the local DS as the DSInfo of the local DS is not
-      sent by the replication server in the topology message. We must count
-      ourselves as a connected server.
-      */
-      connectedDSs.add(getServerId());
-    }
-
-    for (DSInfo dsInfo : dsList)
-    {
-      if (dsInfo.getRsId() == rsId)
-      {
-        connectedDSs.add(dsInfo.getDsId());
-      }
-    }
-
-    return connectedDSs;
-  }
-
-  /**
    * Processes an incoming TopologyMsg.
    * Updates the structures for the local view of the topology.
    *
@@ -3016,42 +2986,298 @@
    */
   private void receiveTopo(TopologyMsg topoMsg, int rsServerId)
   {
-    if (logger.isTraceEnabled())
-      debugInfo("receive TopologyMsg=" + topoMsg);
-
-    // Store new DS list
-    dsList = topoMsg.getDsList();
-
-    // Update replication server info list with the received topology
-    // information
-    final Set<Integer> rssToKeep = new HashSet<Integer>();
-    for (RSInfo rsInfo : topoMsg.getRsList())
+    final Topology newTopo = computeNewTopology(topoMsg, rsServerId);
+    for (DSInfo dsInfo : newTopo.replicaInfos.values())
     {
-      final int rsId = rsInfo.getId();
-      rssToKeep.add(rsId); // Mark this server as still existing
-      Set<Integer> connectedDSs = computeConnectedDSs(rsId, dsList, rsServerId);
-      ReplicationServerInfo rsInfo2 = replicationServerInfos.get(rsId);
-      if (rsInfo2 == null)
-      {
-        // New replication server, create info for it add it to the list
-        rsInfo2 = new ReplicationServerInfo(rsInfo, connectedDSs);
-        setLocallyConfiguredFlag(rsInfo2);
-        replicationServerInfos.put(rsId, rsInfo2);
-      }
-      else
-      {
-        // Update the existing info for the replication server
-        rsInfo2.update(rsInfo, connectedDSs);
-      }
+      domain.setEclIncludes(dsInfo.getDsId(), dsInfo.getEclIncludes(), dsInfo
+          .getEclIncludesForDeletes());
+    }
+  }
+
+  private Topology computeNewTopology(TopologyMsg topoMsg, int rsServerId)
+  {
+    Topology oldTopo;
+    Topology newTopo;
+    do
+    {
+      oldTopo = topology.get();
+      newTopo = new Topology(topoMsg, getServerId(), rsServerId,
+              getReplicationServerUrls(), oldTopo.rsInfos);
+    }
+    while (!topology.compareAndSet(oldTopo, newTopo));
+
+    if (logger.isTraceEnabled())
+    {
+      final StringBuilder sb = topologyChange(rsServerId, oldTopo, newTopo);
+      sb.append(" received TopologyMsg=").append(topoMsg);
+      debugInfo(sb);
+    }
+    return newTopo;
+  }
+
+  /**
+   * Contains the last known state of the replication topology.
+   */
+  static final class Topology
+  {
+
+    /**
+     * The RS's serverId that this DS was connected to when this topology state
+     * was computed.
+     */
+    private final int rsServerId;
+    /**
+     * Info for other DSs.
+     * <p>
+     * Warning: does not contain info for us (for our server id)
+     */
+    final Map<Integer, DSInfo> replicaInfos;
+    /**
+     * The map of replication server info initialized at connection time and
+     * regularly updated. This is used to decide to which best suitable
+     * replication server one wants to connect. Key: replication server id
+     * Value: replication server info for the matching replication server id
+     */
+    final Map<Integer, ReplicationServerInfo> rsInfos;
+
+    private Topology()
+    {
+      this.rsServerId = -1;
+      this.replicaInfos = Collections.emptyMap();
+      this.rsInfos = Collections.emptyMap();
     }
 
-    // Remove any replication server that may have disappeared from the topology
-    replicationServerInfos.keySet().retainAll(rssToKeep);
-
-    for (DSInfo info : dsList)
+    /**
+     * Constructor to use when only the RSInfos need to be recomputed.
+     *
+     * @param dsInfosToKeep
+     *          the DSInfos that will be stored as is
+     * @param newRSInfos
+     *          the new RSInfos from which to compute the new topology
+     * @param dsServerId
+     *          the DS serverId
+     * @param rsServerId
+     *          the current connected RS serverId
+     * @param configuredReplicationServerUrls
+     *          the configured replication server URLs
+     * @param previousRsInfos
+     *          the RSInfos computed in the previous Topology object
+     */
+    Topology(Map<Integer, DSInfo> dsInfosToKeep, List<RSInfo> newRSInfos,
+        int dsServerId, int rsServerId,
+        Set<String> configuredReplicationServerUrls,
+        Map<Integer, ReplicationServerInfo> previousRsInfos)
     {
-      domain.setEclIncludes(info.getDsId(), info.getEclIncludes(),
-          info.getEclIncludesForDeletes());
+      this.rsServerId = rsServerId;
+      this.replicaInfos = dsInfosToKeep;
+      this.rsInfos = computeRSInfos(dsServerId, newRSInfos,
+          previousRsInfos, configuredReplicationServerUrls);
+    }
+
+    /**
+     * Constructor to use when a new TopologyMsg has been received.
+     *
+     * @param topoMsg
+     *          the topology message containing the new DSInfos and RSInfos from
+     *          which to compute the new topology
+     * @param dsServerId
+     *          the DS serverId
+     * @param rsServerId
+     *          the current connected RS serverId
+     * @param configuredReplicationServerUrls
+     *          the configured replication server URLs
+     * @param previousRsInfos
+     *          the RSInfos computed in the previous Topology object
+     */
+    Topology(TopologyMsg topoMsg, int dsServerId,
+        int rsServerId, Set<String> configuredReplicationServerUrls,
+        Map<Integer, ReplicationServerInfo> previousRsInfos)
+    {
+      this.rsServerId = rsServerId;
+      this.replicaInfos = removeThisDs(topoMsg.getReplicaInfos(), dsServerId);
+      this.rsInfos = computeRSInfos(dsServerId, topoMsg.getRsInfos(),
+          previousRsInfos, configuredReplicationServerUrls);
+    }
+
+    private Map<Integer, DSInfo> removeThisDs(Map<Integer, DSInfo> dsInfos,
+        int dsServerId)
+    {
+      final Map<Integer, DSInfo> copy = new HashMap<Integer, DSInfo>(dsInfos);
+      copy.remove(dsServerId);
+      return Collections.unmodifiableMap(copy);
+    }
+
+    private Map<Integer, ReplicationServerInfo> computeRSInfos(
+        int dsServerId, List<RSInfo> newRsInfos,
+        Map<Integer, ReplicationServerInfo> previousRsInfos,
+        Set<String> configuredReplicationServerUrls)
+    {
+      final Map<Integer, ReplicationServerInfo> results =
+          new HashMap<Integer, ReplicationServerInfo>(previousRsInfos);
+
+      // Update replication server info list with the received topology info
+      final Set<Integer> rssToKeep = new HashSet<Integer>();
+      for (RSInfo newRSInfo : newRsInfos)
+      {
+        final int rsId = newRSInfo.getId();
+        rssToKeep.add(rsId); // Mark this server as still existing
+        Set<Integer> connectedDSs =
+            computeDSsConnectedTo(rsId, dsServerId);
+        ReplicationServerInfo rsInfo = results.get(rsId);
+        if (rsInfo == null)
+        {
+          // New replication server, create info for it add it to the list
+          rsInfo = new ReplicationServerInfo(newRSInfo, connectedDSs);
+          setLocallyConfiguredFlag(rsInfo, configuredReplicationServerUrls);
+          results.put(rsId, rsInfo);
+        }
+        else
+        {
+          // Update the existing info for the replication server
+          rsInfo.update(newRSInfo, connectedDSs);
+        }
+      }
+
+      // Remove any replication server that may have disappeared from the
+      // topology
+      results.keySet().retainAll(rssToKeep);
+
+      return Collections.unmodifiableMap(results);
+    }
+
+    /** Computes the list of DSs connected to a particular RS. */
+    private Set<Integer> computeDSsConnectedTo(int rsId, int dsServerId)
+    {
+      final Set<Integer> connectedDSs = new HashSet<Integer>();
+      if (rsServerId == rsId)
+      {
+        /*
+         * If we are computing connected DSs for the RS we are connected to, we
+         * should count the local DS as the DSInfo of the local DS is not sent
+         * by the replication server in the topology message. We must count
+         * ourselves as a connected server.
+         */
+        connectedDSs.add(dsServerId);
+      }
+
+      for (DSInfo dsInfo : replicaInfos.values())
+      {
+        if (dsInfo.getRsId() == rsId)
+        {
+          connectedDSs.add(dsInfo.getDsId());
+        }
+      }
+
+      return connectedDSs;
+    }
+
+    /**
+     * Sets the locally configured flag for the passed ReplicationServerInfo
+     * object, analyzing the local configuration.
+     *
+     * @param rsInfo
+     *          the Replication server to check and update
+     * @param configuredReplicationServerUrls
+     */
+    private void setLocallyConfiguredFlag(ReplicationServerInfo rsInfo,
+        Set<String> configuredReplicationServerUrls)
+    {
+      // Determine if the passed ReplicationServerInfo has a URL that is present
+      // in the locally configured replication servers
+      String rsUrl = rsInfo.getServerURL();
+      if (rsUrl == null)
+      {
+        // The ReplicationServerInfo has been generated from a server with
+        // no URL in TopologyMsg (i.e: with replication protocol version < 4):
+        // ignore this server as we do not know how to connect to it
+        rsInfo.setLocallyConfigured(false);
+        return;
+      }
+      for (String serverUrl : configuredReplicationServerUrls)
+      {
+        if (isSameReplicationServerUrl(serverUrl, rsUrl))
+        {
+          // This RS is locally configured, mark this
+          rsInfo.setLocallyConfigured(true);
+          rsInfo.setServerURL(serverUrl);
+          return;
+        }
+      }
+      rsInfo.setLocallyConfigured(false);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj)
+    {
+      if (this == obj)
+      {
+        return true;
+      }
+      if (obj == null || getClass() != obj.getClass())
+      {
+        return false;
+      }
+      final Topology other = (Topology) obj;
+      return rsServerId == other.rsServerId
+          && equals(replicaInfos, other.replicaInfos)
+          && equals(rsInfos, other.rsInfos)
+          && urlsEqual1(replicaInfos, other.replicaInfos)
+          && urlsEqual2(rsInfos, other.rsInfos);
+    }
+
+    private boolean equals(Object o1, Object o2)
+    {
+      return o1 == null ? o2 == null : o1.equals(o2);
+    }
+
+    private boolean urlsEqual1(Map<Integer, DSInfo> replicaInfos1,
+        Map<Integer, DSInfo> replicaInfos2)
+    {
+      for (Entry<Integer, DSInfo> entry : replicaInfos1.entrySet())
+      {
+        DSInfo dsInfo = replicaInfos2.get(entry.getKey());
+        if (!equals(entry.getValue().getDsUrl(), dsInfo.getDsUrl()))
+        {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    private boolean urlsEqual2(Map<Integer, ReplicationServerInfo> rsInfos1,
+        Map<Integer, ReplicationServerInfo> rsInfos2)
+    {
+      for (Entry<Integer, ReplicationServerInfo> entry : rsInfos1.entrySet())
+      {
+        ReplicationServerInfo rsInfo = rsInfos2.get(entry.getKey());
+        if (!equals(entry.getValue().getServerURL(), rsInfo.getServerURL()))
+        {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode()
+    {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + rsServerId;
+      result = prime * result
+          + (replicaInfos == null ? 0 : replicaInfos.hashCode());
+      result = prime * result + (rsInfos == null ? 0 : rsInfos.hashCode());
+      return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString()
+    {
+      return "rsServerId=" + rsServerId + ", replicaInfos=" + replicaInfos
+          + ", rsInfos=" + rsInfos.values();
     }
   }
 
@@ -3197,15 +3423,15 @@
       .append(" \"").append(getBaseDN()).append(" ")
       .append(getServerId()).append("\",")
       .append(" groupId=").append(getGroupId())
-      .append(", genId=").append(generationID)
+      .append(", genId=").append(getGenerationID())
       .append(", ");
     connectedRS.get().toString(sb);
     return sb.toString();
   }
 
-  private void debugInfo(String message)
+  private void debugInfo(CharSequence message)
   {
     logger.trace(getClass().getSimpleName() + " for baseDN=" + getBaseDN()
-        + " and serverId=" + getServerId() + " " + message);
+        + " and serverId=" + getServerId() + ": " + message);
   }
 }

--
Gitblit v1.10.0