@Override
  public void notify(ChangeSet changeSet) {
    if (changeSet == null) {
      return; // do nothing
    }
    if (!isOpen.get()) {
      // The channel is not open ...
      return;
    }
    if (!multipleAddressesInCluster.get()) {
      // We are in clustered mode, but there is only one participant in the cluster (us).
      // So short-circuit the cluster and just notify the local observers ...
      if (hasObservers()) {
        delegate.notify(changeSet);
        logReceivedOperation(changeSet);
      }
      return;
    }

    // There are multiple participants in the cluster, so send all changes out to JGroups,
    // letting JGroups do the ordering of messages...
    try {
      logSendOperation(changeSet);
      byte[] data = serialize(changeSet);
      Message message = new Message(null, null, data);
      channel.send(message);
    } catch (IllegalStateException e) {
      LOGGER.warn(
          BusI18n.unableToNotifyChanges,
          clusteringConfiguration.getClusterName(),
          changeSet.size(),
          changeSet.getWorkspaceName(),
          changeSet.getUserId(),
          changeSet.getProcessKey(),
          changeSet.getTimestamp());
    } catch (Exception e) {
      // Something went wrong here (this should not happen) ...
      String msg =
          BusI18n.errorSerializingChanges.text(
              clusteringConfiguration.getClusterName(),
              changeSet.size(),
              changeSet.getWorkspaceName(),
              changeSet.getUserId(),
              changeSet.getProcessKey(),
              changeSet.getTimestamp(),
              changeSet);
      throw new SystemFailureException(msg, e);
    }
  }
 protected final void logReceivedOperation(ChangeSet changeSet) {
   if (LOGGER.isTraceEnabled()) {
     LOGGER.trace(
         "Received on cluster '{0}' {1} changes on workspace {2} made by {3} from process '{4}' at {5}",
         clusteringConfiguration.getClusterName(),
         changeSet.size(),
         changeSet.getWorkspaceName(),
         changeSet.getUserId(),
         changeSet.getProcessKey(),
         changeSet.getTimestamp());
   }
 }