/*
 * Copyright DataStax, Inc.
 *
 * Licensed 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 com.datastax.oss.driver.api.core.metadata;

import com.datastax.oss.driver.api.core.loadbalancing.NodeDistance;
import com.datastax.oss.driver.api.core.session.Session;
import com.datastax.oss.driver.api.core.session.SessionBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;

A listener that gets notified when nodes states change.

An implementation of this interface can be registered in the configuration, or with SessionBuilder.withNodeStateListener(NodeStateListener).

Note that the methods defined by this interface will be executed by internal driver threads, and are therefore expected to have short execution times. If you need to perform long computations or blocking calls in response to schema change events, it is strongly recommended to schedule them asynchronously on a separate thread provided by your application code.

If you implement this interface but don't need to implement all the methods, extend NodeStateListenerBase.

/** * A listener that gets notified when nodes states change. * * <p>An implementation of this interface can be registered in the configuration, or with {@link * SessionBuilder#withNodeStateListener(NodeStateListener)}. * * <p>Note that the methods defined by this interface will be executed by internal driver threads, * and are therefore expected to have short execution times. If you need to perform long * computations or blocking calls in response to schema change events, it is strongly recommended to * schedule them asynchronously on a separate thread provided by your application code. * * <p>If you implement this interface but don't need to implement all the methods, extend {@link * NodeStateListenerBase}. */
public interface NodeStateListener extends AutoCloseable {
Invoked when a node is first added to the cluster.

The node is not up yet at this point. onUp(Node) will be notified later if the driver successfully connects to the node (provided that a session is opened and the node is not ignored), or receives a topology event for it.

This method is not invoked for the contact points provided at initialization. It is however for new nodes discovered during the full node list refresh after the first connection.

/** * Invoked when a node is first added to the cluster. * * <p>The node is not up yet at this point. {@link #onUp(Node)} will be notified later if the * driver successfully connects to the node (provided that a session is opened and the node is not * {@link NodeDistance#IGNORED ignored}), or receives a topology event for it. * * <p>This method is <b>not</b> invoked for the contact points provided at initialization. It is * however for new nodes discovered during the full node list refresh after the first connection. */
void onAdd(@NonNull Node node);
Invoked when a node's state switches to NodeState.UP.
/** Invoked when a node's state switches to {@link NodeState#UP}. */
void onUp(@NonNull Node node);
Invoked when a node's state switches to NodeState.DOWN or NodeState.FORCED_DOWN.
/** * Invoked when a node's state switches to {@link NodeState#DOWN} or {@link * NodeState#FORCED_DOWN}. */
void onDown(@NonNull Node node);
Invoked when a node leaves the cluster.

This can be triggered by a topology event, or during a full node list refresh if the node is absent from the new list.

/** * Invoked when a node leaves the cluster. * * <p>This can be triggered by a topology event, or during a full node list refresh if the node is * absent from the new list. */
void onRemove(@NonNull Node node);
Invoked when the session is ready to process user requests.

This corresponds to the moment when SessionBuilder.build() returns, or the future returned by SessionBuilder.buildAsync() completes. If the session initialization fails, this method will not get called.

Listener methods are invoked from different threads; if you store the session in a field, make it at least volatile to guarantee proper publication.

Note that this method will not be the first one invoked on the listener; the driver emits node events before that, during the initialization of the session:

  • First the driver shuffles the contact points, and tries each one sequentially. For any contact point that can't be reached, onDown(Node) is invoked; for the one that eventually succeeds, onUp(Node) is invoked and that node becomes the control node (if none succeeds, the session initialization fails and the process stops here).
  • The control node's system.peers table is inspected to discover the remaining nodes in the cluster. For any node that wasn't already a contact point, onAdd(Node) is invoked; for any contact point that doesn't have a corresponding entry in the table, onRemove(Node) is invoked;
  • The load balancing policy computes the nodes' distances, and, for each LOCAL or REMOTE node, the driver creates a connection pool. If at least one pooled connection can be established, onUp(Node) is invoked; otherwise, onDown(Node) is invoked (no additional event is emitted for the control node, it is considered up since we already have a connection to it).
  • Once all the pools are created, the session is fully initialized and this method is invoked.
If you're not interested in those init events, or want to delay them until after the session is ready, take a look at SafeInitNodeStateListener.

This method's default implementation is empty.

/** * Invoked when the session is ready to process user requests. * * <p>This corresponds to the moment when {@link SessionBuilder#build()} returns, or the future * returned by {@link SessionBuilder#buildAsync()} completes. If the session initialization fails, * this method will not get called. * * <p>Listener methods are invoked from different threads; if you store the session in a field, * make it at least volatile to guarantee proper publication. * * <p>Note that this method will not be the first one invoked on the listener; the driver emits * node events before that, during the initialization of the session: * * <ul> * <li>First the driver shuffles the contact points, and tries each one sequentially. For any * contact point that can't be reached, {@link #onDown(Node)} is invoked; for the one that * eventually succeeds, {@link #onUp(Node)} is invoked and that node becomes the control * node (if none succeeds, the session initialization fails and the process stops here). * <li>The control node's {@code system.peers} table is inspected to discover the remaining * nodes in the cluster. For any node that wasn't already a contact point, {@link * #onAdd(Node)} is invoked; for any contact point that doesn't have a corresponding entry * in the table, {@link #onRemove(Node)} is invoked; * <li>The load balancing policy computes the nodes' {@linkplain NodeDistance distances}, and, * for each LOCAL or REMOTE node, the driver creates a connection pool. If at least one * pooled connection can be established, {@link #onUp(Node)} is invoked; otherwise, {@link * #onDown(Node)} is invoked (no additional event is emitted for the control node, it is * considered up since we already have a connection to it). * <li>Once all the pools are created, the session is fully initialized and this method is * invoked. * </ul> * * If you're not interested in those init events, or want to delay them until after the session is * ready, take a look at {@link SafeInitNodeStateListener}. * * <p>This method's default implementation is empty. */
default void onSessionReady(@NonNull Session session) {} }