// Path: src/main/java/biz/paluch/spinach/api/DisqueConnection.java
// public interface DisqueConnection<K, V> extends StatefulConnection<K, V> {
// 
//     /**
//      * Returns the {@link DisqueCommands} API for the current connection. Does not create a new connection.
//      * 
//      * @return the synchronous API for the underlying connection.
//      */
//     DisqueCommands<K, V> sync();
// 
//     /**
//      * Returns the {@link DisqueAsyncCommands} API for the current connection. Does not create a new connection.
//      * 
//      * @return the asynchronous API for the underlying connection.
//      */
//     DisqueAsyncCommands<K, V> async();
// 
//     /**
//      * Returns the {@link DisqueReactiveCommands} API for the current connection. Does not create a new connection.
//      *
//      * @return the reactive API for the underlying connection.
//      */
//     DisqueReactiveCommands<K, V> reactive();
// 
// }
// 
// Path: src/main/java/biz/paluch/spinach/cluster/DisqueNode.java
// @SuppressWarnings("serial")
// public class DisqueNode implements Serializable {
//     private String addr;
//     private int port;
//     private String nodeId;
// 
//     private boolean connected;
//     private long pingSentTimestamp;
//     private long pongReceivedTimestamp;
// 
//     private Set<NodeFlag> flags;
// 
//     public DisqueNode() {
// 
//     }
// 
//     public DisqueNode(String addr, int port, String nodeId, boolean connected, long pingSentTimestamp,
//             long pongReceivedTimestamp, Set<NodeFlag> flags) {
//         this.addr = addr;
//         this.port = port;
//         this.nodeId = nodeId;
//         this.connected = connected;
//         this.pingSentTimestamp = pingSentTimestamp;
//         this.pongReceivedTimestamp = pongReceivedTimestamp;
//         this.flags = flags;
//     }
// 
//     public String getAddr() {
//         return addr;
//     }
// 
//     public void setAddr(String addr) {
//         this.addr = addr;
//     }
// 
//     public int getPort() {
//         return port;
//     }
// 
//     public void setPort(int port) {
//         this.port = port;
//     }
// 
//     public String getNodeId() {
//         return nodeId;
//     }
// 
//     public void setNodeId(String nodeId) {
//         LettuceAssert.notNull(nodeId, "nodeId must not be null");
//         this.nodeId = nodeId;
//     }
// 
//     public boolean isConnected() {
//         return connected;
//     }
// 
//     public void setConnected(boolean connected) {
//         this.connected = connected;
//     }
// 
//     public long getPingSentTimestamp() {
//         return pingSentTimestamp;
//     }
// 
//     public void setPingSentTimestamp(long pingSentTimestamp) {
//         this.pingSentTimestamp = pingSentTimestamp;
//     }
// 
//     public long getPongReceivedTimestamp() {
//         return pongReceivedTimestamp;
//     }
// 
//     public void setPongReceivedTimestamp(long pongReceivedTimestamp) {
//         this.pongReceivedTimestamp = pongReceivedTimestamp;
//     }
// 
//     public Set<NodeFlag> getFlags() {
//         return flags;
//     }
// 
//     public void setFlags(Set<NodeFlag> flags) {
//         this.flags = flags;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) {
//             return true;
//         }
//         if (!(o instanceof DisqueNode)) {
//             return false;
//         }
// 
//         DisqueNode that = (DisqueNode) o;
// 
//         if (nodeId != null ? !nodeId.equals(that.nodeId) : that.nodeId != null) {
//             return false;
//         }
// 
//         return true;
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = 31 * (nodeId != null ? nodeId.hashCode() : 0);
//         return result;
//     }
// 
//     @Override
//     public String toString() {
//         final StringBuilder sb = new StringBuilder();
//         sb.append(getClass().getSimpleName());
//         sb.append(" [addr=").append(addr);
//         sb.append(", port='").append(port).append('\'');
//         sb.append(", nodeId='").append(nodeId).append('\'');
//         sb.append(", connected=").append(connected);
//         sb.append(", pingSentTimestamp=").append(pingSentTimestamp);
//         sb.append(", pongReceivedTimestamp=").append(pongReceivedTimestamp);
//         sb.append(", flags=").append(flags);
//         sb.append(']');
//         return sb.toString();
//     }
// 
//     public enum NodeFlag {
//         NOFLAGS, MYSELF, EVENTUAL_FAIL, FAIL, HANDSHAKE, NOADDR;
//     }
// }


import java.net.InetSocketAddress;
import java.net.SocketAddress;
import biz.paluch.spinach.api.DisqueConnection;
import biz.paluch.spinach.cluster.DisqueNode;
/*
 * Copyright 2016 the original author or authors.
 *
 * 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 biz.paluch.spinach.impl;



/**
 * Supplier for {@link SocketAddress adresses} that is aware of the cluster nodes.
 * <p>
 * This class performs a {@code HELLO} command handshake upon connection and retrieves the nodes from the command result. The
 * node set is not refreshed once it is retrieved. The nodes are used in the order of their priority in a round-robin fashion.
 * Until the handshake is completed a fallback {@link SocketAddressSupplier} is used.
 * </p>
 *
 * @author Mark Paluch
 */
public class HelloClusterSocketAddressSupplier extends ClusterAwareNodeSupport implements SocketAddressSupplier,
        ConnectionAware {

    protected final SocketAddressSupplier bootstrap;
// Path: src/main/java/biz/paluch/spinach/api/DisqueConnection.java
// public interface DisqueConnection<K, V> extends StatefulConnection<K, V> {
// 
//     /**
//      * Returns the {@link DisqueCommands} API for the current connection. Does not create a new connection.
//      * 
//      * @return the synchronous API for the underlying connection.
//      */
//     DisqueCommands<K, V> sync();
// 
//     /**
//      * Returns the {@link DisqueAsyncCommands} API for the current connection. Does not create a new connection.
//      * 
//      * @return the asynchronous API for the underlying connection.
//      */
//     DisqueAsyncCommands<K, V> async();
// 
//     /**
//      * Returns the {@link DisqueReactiveCommands} API for the current connection. Does not create a new connection.
//      *
//      * @return the reactive API for the underlying connection.
//      */
//     DisqueReactiveCommands<K, V> reactive();
// 
// }
// 
// Path: src/main/java/biz/paluch/spinach/cluster/DisqueNode.java
// @SuppressWarnings("serial")
// public class DisqueNode implements Serializable {
//     private String addr;
//     private int port;
//     private String nodeId;
// 
//     private boolean connected;
//     private long pingSentTimestamp;
//     private long pongReceivedTimestamp;
// 
//     private Set<NodeFlag> flags;
// 
//     public DisqueNode() {
// 
//     }
// 
//     public DisqueNode(String addr, int port, String nodeId, boolean connected, long pingSentTimestamp,
//             long pongReceivedTimestamp, Set<NodeFlag> flags) {
//         this.addr = addr;
//         this.port = port;
//         this.nodeId = nodeId;
//         this.connected = connected;
//         this.pingSentTimestamp = pingSentTimestamp;
//         this.pongReceivedTimestamp = pongReceivedTimestamp;
//         this.flags = flags;
//     }
// 
//     public String getAddr() {
//         return addr;
//     }
// 
//     public void setAddr(String addr) {
//         this.addr = addr;
//     }
// 
//     public int getPort() {
//         return port;
//     }
// 
//     public void setPort(int port) {
//         this.port = port;
//     }
// 
//     public String getNodeId() {
//         return nodeId;
//     }
// 
//     public void setNodeId(String nodeId) {
//         LettuceAssert.notNull(nodeId, "nodeId must not be null");
//         this.nodeId = nodeId;
//     }
// 
//     public boolean isConnected() {
//         return connected;
//     }
// 
//     public void setConnected(boolean connected) {
//         this.connected = connected;
//     }
// 
//     public long getPingSentTimestamp() {
//         return pingSentTimestamp;
//     }
// 
//     public void setPingSentTimestamp(long pingSentTimestamp) {
//         this.pingSentTimestamp = pingSentTimestamp;
//     }
// 
//     public long getPongReceivedTimestamp() {
//         return pongReceivedTimestamp;
//     }
// 
//     public void setPongReceivedTimestamp(long pongReceivedTimestamp) {
//         this.pongReceivedTimestamp = pongReceivedTimestamp;
//     }
// 
//     public Set<NodeFlag> getFlags() {
//         return flags;
//     }
// 
//     public void setFlags(Set<NodeFlag> flags) {
//         this.flags = flags;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) {
//             return true;
//         }
//         if (!(o instanceof DisqueNode)) {
//             return false;
//         }
// 
//         DisqueNode that = (DisqueNode) o;
// 
//         if (nodeId != null ? !nodeId.equals(that.nodeId) : that.nodeId != null) {
//             return false;
//         }
// 
//         return true;
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = 31 * (nodeId != null ? nodeId.hashCode() : 0);
//         return result;
//     }
// 
//     @Override
//     public String toString() {
//         final StringBuilder sb = new StringBuilder();
//         sb.append(getClass().getSimpleName());
//         sb.append(" [addr=").append(addr);
//         sb.append(", port='").append(port).append('\'');
//         sb.append(", nodeId='").append(nodeId).append('\'');
//         sb.append(", connected=").append(connected);
//         sb.append(", pingSentTimestamp=").append(pingSentTimestamp);
//         sb.append(", pongReceivedTimestamp=").append(pongReceivedTimestamp);
//         sb.append(", flags=").append(flags);
//         sb.append(']');
//         return sb.toString();
//     }
// 
//     public enum NodeFlag {
//         NOFLAGS, MYSELF, EVENTUAL_FAIL, FAIL, HANDSHAKE, NOADDR;
//     }
// }


// Path: src/main/java/biz/paluch/spinach/impl/HelloClusterSocketAddressSupplier.java
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import biz.paluch.spinach.api.DisqueConnection;
import biz.paluch.spinach.cluster.DisqueNode;

/*
 * Copyright 2016 the original author or authors.
 *
 * 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 biz.paluch.spinach.impl;



/**
 * Supplier for {@link SocketAddress adresses} that is aware of the cluster nodes.
 * <p>
 * This class performs a {@code HELLO} command handshake upon connection and retrieves the nodes from the command result. The
 * node set is not refreshed once it is retrieved. The nodes are used in the order of their priority in a round-robin fashion.
 * Until the handshake is completed a fallback {@link SocketAddressSupplier} is used.
 * </p>
 *
 * @author Mark Paluch
 */
public class HelloClusterSocketAddressSupplier extends ClusterAwareNodeSupport implements SocketAddressSupplier,
        ConnectionAware {

    protected final SocketAddressSupplier bootstrap;
    protected RoundRobin<DisqueNode> roundRobin;