/* Hook for creating session factory */
  protected ClientSessionFactoryInternal createSessionFactory() throws Exception {
    if (targetNodeID != null
        && (this.reconnectAttemptsSameNode < 0 || retryCount <= this.reconnectAttemptsSameNode)) {
      csf = reconnectOnOriginalNode();
    } else {
      serverLocator.resetToInitialConnectors();
      csf = (ClientSessionFactoryInternal) serverLocator.createSessionFactory();
    }

    // null here means the targetNodeIS is not available yet
    if (csf != null) {
      csf.setReconnectAttempts(0);
    }

    return csf;
  }
  @Test
  public void testNoAutoFailback() throws Exception {
    locator
        .setBlockOnNonDurableSend(true)
        .setBlockOnDurableSend(true)
        .setFailoverOnInitialConnection(true)
        .setReconnectAttempts(-1);

    ClientSessionFactoryInternal sf = createSessionFactoryAndWaitForTopology(locator, 2);

    ClientSession session = sendAndConsume(sf, true);

    CountDownSessionFailureListener listener = new CountDownSessionFailureListener(1, session);

    session.addFailureListener(listener);

    backupServer.stop();

    liveServer.crash();

    backupServer.start();

    assertTrue(listener.getLatch().await(5, TimeUnit.SECONDS));

    ClientProducer producer = session.createProducer(ADDRESS);

    ClientMessage message = session.createMessage(true);

    setBody(0, message);

    producer.send(message);

    session.removeFailureListener(listener);

    Thread t = new Thread(new ServerStarter(liveServer));

    t.start();

    waitForRemoteBackup(sf, 10, false, backupServer.getServer());

    assertTrue(backupServer.isStarted());

    backupServer.crash();

    waitForServerToStart(liveServer.getServer());

    assertTrue(liveServer.isStarted());

    sf.close();

    assertEquals(0, sf.numSessions());

    assertEquals(0, sf.numConnections());
  }
  protected void scheduleRetryConnect() {
    if (serverLocator.isClosed()) {
      ActiveMQServerLogger.LOGGER.bridgeLocatorShutdown();
      return;
    }

    if (stopping) {
      ActiveMQServerLogger.LOGGER.bridgeStopping();
      return;
    }

    if (reconnectAttemptsInUse >= 0 && retryCount > reconnectAttemptsInUse) {
      ActiveMQServerLogger.LOGGER.bridgeAbortStart(name, retryCount, reconnectAttempts);
      fail(true);
      return;
    }

    long timeout = (long) (this.retryInterval * Math.pow(this.retryMultiplier, retryCount));
    if (timeout == 0) {
      timeout = this.retryInterval;
    }
    if (timeout > maxRetryInterval) {
      timeout = maxRetryInterval;
    }

    logger.debug(
        "Bridge "
            + this
            + " retrying connection #"
            + retryCount
            + ", maxRetry="
            + reconnectAttemptsInUse
            + ", timeout="
            + timeout);

    scheduleRetryConnectFixedTimeout(timeout);
  }
  private ClientSessionFactoryInternal reconnectOnOriginalNode() throws Exception {
    String targetNodeIdUse = targetNodeID;
    TopologyMember nodeUse = targetNode;
    if (targetNodeIdUse != null && nodeUse != null) {
      TransportConfiguration[] configs = new TransportConfiguration[2]; // live and backup
      int numberOfConfigs = 0;

      if (nodeUse.getLive() != null) {
        configs[numberOfConfigs++] = nodeUse.getLive();
      }
      if (nodeUse.getBackup() != null) {
        configs[numberOfConfigs++] = nodeUse.getBackup();
      }

      if (numberOfConfigs > 0) {
        // It will bounce between all the available configs
        int nodeTry = (retryCount - 1) % numberOfConfigs;

        return (ClientSessionFactoryInternal) serverLocator.createSessionFactory(configs[nodeTry]);
      }
    }

    return null;
  }
  /* This is called only when the bridge is activated */
  protected void connect() {
    if (stopping) return;

    synchronized (connectionGuard) {
      if (!keepConnecting) return;

      logger.debug(
          "Connecting  "
              + this
              + " to its destination ["
              + nodeUUID.toString()
              + "], csf="
              + this.csf);

      retryCount++;

      try {
        if (csf == null || csf.isClosed()) {
          if (stopping) return;
          csf = createSessionFactory();
          if (csf == null) {
            // Retrying. This probably means the node is not available (for the cluster connection
            // case)
            scheduleRetryConnect();
            return;
          }
          // Session is pre-acknowledge
          session =
              (ClientSessionInternal) csf.createSession(user, password, false, true, true, true, 1);
          sessionConsumer =
              (ClientSessionInternal) csf.createSession(user, password, false, true, true, true, 1);
        }

        if (forwardingAddress != null) {
          ClientSession.AddressQuery query = null;

          try {
            query = session.addressQuery(forwardingAddress);
          } catch (Throwable e) {
            ActiveMQServerLogger.LOGGER.errorQueryingBridge(e, name);
            // This was an issue during startup, we will not count this retry
            retryCount--;

            scheduleRetryConnectFixedTimeout(100);
            return;
          }

          if (forwardingAddress.startsWith(BridgeImpl.JMS_QUEUE_ADDRESS_PREFIX)
              || forwardingAddress.startsWith(BridgeImpl.JMS_TOPIC_ADDRESS_PREFIX)) {
            if (!query.isExists()) {
              ActiveMQServerLogger.LOGGER.errorQueryingBridge(forwardingAddress, retryCount);
              scheduleRetryConnect();
              return;
            }
          } else {
            if (!query.isExists()) {
              ActiveMQServerLogger.LOGGER.bridgeNoBindings(
                  getName(), getForwardingAddress(), getForwardingAddress());
            }
          }
        }

        producer = session.createProducer();
        session.addFailureListener(BridgeImpl.this);

        session.setSendAcknowledgementHandler(BridgeImpl.this);

        afterConnect();

        active = true;

        queue.addConsumer(BridgeImpl.this);
        queue.deliverAsync();

        ActiveMQServerLogger.LOGGER.bridgeConnected(this);

        // We only do this on plain core bridges
        if (isPlainCoreBridge()) {
          serverLocator.addClusterTopologyListener(new TopologyListener());
        }

        keepConnecting = false;
        return;
      } catch (ActiveMQException e) {
        // the session was created while its server was starting, retry it:
        if (e.getType() == ActiveMQExceptionType.SESSION_CREATION_REJECTED) {
          ActiveMQServerLogger.LOGGER.errorStartingBridge(name);

          // We are not going to count this one as a retry
          retryCount--;

          scheduleRetryConnectFixedTimeout(this.retryInterval);
          return;
        } else {
          if (logger.isDebugEnabled()) {
            logger.debug("Bridge " + this + " is unable to connect to destination. Retrying", e);
          }

          scheduleRetryConnect();
        }
      } catch (ActiveMQInterruptedException | InterruptedException e) {
        ActiveMQServerLogger.LOGGER.errorConnectingBridge(e, this);
      } catch (Exception e) {
        ActiveMQServerLogger.LOGGER.errorConnectingBridge(e, this);
        if (csf != null) {
          try {
            csf.close();
            csf = null;
          } catch (Throwable ignored) {
          }
        }
        fail(false);
        scheduleRetryConnect();
      }
    }
  }