content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.internal.core.metadata;
  
  import com.datastax.oss.driver.api.core.Version;
  import com.datastax.oss.driver.api.core.loadbalancing.NodeDistance;
  import com.datastax.oss.driver.api.core.metadata.EndPoint;
  import com.datastax.oss.driver.api.core.metadata.Node;
  import com.datastax.oss.driver.api.core.metadata.NodeState;
  import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
  import com.datastax.oss.driver.internal.core.metrics.NodeMetricUpdater;
  import com.datastax.oss.driver.internal.core.metrics.NoopNodeMetricUpdater;
  import edu.umd.cs.findbugs.annotations.NonNull;
  import edu.umd.cs.findbugs.annotations.Nullable;
  import java.io.Serializable;
  import java.net.InetSocketAddress;
  import java.util.Collections;
  import java.util.Map;
  import java.util.Optional;
  import java.util.Set;
  import java.util.UUID;
  import net.jcip.annotations.ThreadSafe;
  
  /**
   * Implementation note: all the mutable state in this class is read concurrently, but only mutated
   * from {@link MetadataManager}'s admin thread.
   */
  @ThreadSafe
  public class DefaultNode implements Node, Serializable {
  
    private static final long serialVersionUID = 1;
  
    private volatile EndPoint endPoint;
    // A deserialized node is not attached to a session anymore, so we don't need to retain this
    private transient volatile NodeMetricUpdater metricUpdater;
  
    volatile InetSocketAddress broadcastRpcAddress;
    volatile InetSocketAddress broadcastAddress;
    volatile InetSocketAddress listenAddress;
    volatile String datacenter;
    volatile String rack;
    volatile Version cassandraVersion;
    // Keep a copy of the raw tokens, to detect if they have changed when we refresh the node
    volatile Set<String> rawTokens;
    volatile Map<String, Object> extras;
    volatile UUID hostId;
    volatile UUID schemaVersion;
  
    // These 4 fields are read concurrently, but only mutated on NodeStateManager's admin thread
    volatile NodeState state;
    volatile int openConnections;
    volatile int reconnections;
    volatile long upSinceMillis;
  
    volatile NodeDistance distance;
  
    public DefaultNode(EndPoint endPoint, InternalDriverContext context) {
      this.endPoint = endPoint;
      this.state = NodeState.UNKNOWN;
      this.distance = NodeDistance.IGNORED;
      this.rawTokens = Collections.emptySet();
      this.extras = Collections.emptyMap();
      // We leak a reference to a partially constructed object (this), but in practice this won't be a
      // problem because the node updater only needs the connect address to initialize.
      this.metricUpdater = context.getMetricsFactory().newNodeUpdater(this);
      this.upSinceMillis = -1;
    }
  
    @NonNull
    @Override
    public EndPoint getEndPoint() {
      return endPoint;
    }
  
    public void setEndPoint(@NonNull EndPoint newEndPoint, @NonNull InternalDriverContext context) {
      if (!newEndPoint.equals(endPoint)) {
        endPoint = newEndPoint;
  
        // The endpoint is also used to build metric names, so make sure they get updated
        NodeMetricUpdater previousMetricUpdater = metricUpdater;
        if (!(previousMetricUpdater instanceof NoopNodeMetricUpdater)) {
          metricUpdater = context.getMetricsFactory().newNodeUpdater(this);
        }
      }
    }
  
    @NonNull
    @Override
    public Optional<InetSocketAddress> getBroadcastRpcAddress() {
      return Optional.ofNullable(broadcastRpcAddress);
    }
  
    @NonNull
    @Override
    public Optional<InetSocketAddress> getBroadcastAddress() {
      return Optional.ofNullable(broadcastAddress);
    }
  
    @NonNull
    @Override
    public Optional<InetSocketAddress> getListenAddress() {
      return Optional.ofNullable(listenAddress);
    }
  
    @Nullable
    @Override
    public String getDatacenter() {
      return datacenter;
    }
  
    @Nullable
    @Override
    public String getRack() {
      return rack;
    }
  
    @Nullable
    @Override
    public Version getCassandraVersion() {
      return cassandraVersion;
    }
  
    @Nullable
    @Override
    public UUID getHostId() {
      return hostId;
    }
  
    @Nullable
    @Override
    public UUID getSchemaVersion() {
      return schemaVersion;
    }
  
    @NonNull
    @Override
    public Map<String, Object> getExtras() {
      return extras;
    }
  
    @NonNull
    @Override
    public NodeState getState() {
      return state;
    }
  
    @Override
    public long getUpSinceMillis() {
      return upSinceMillis;
    }
  
    @Override
    public int getOpenConnections() {
      return openConnections;
    }
  
    @Override
    public boolean isReconnecting() {
      return reconnections > 0;
    }
  
    @NonNull
    @Override
    public NodeDistance getDistance() {
      return distance;
    }
  
    public NodeMetricUpdater getMetricUpdater() {
      return metricUpdater;
    }
  
    @Override
    public String toString() {
      // Include the hash code because this class uses reference equality
      return String.format(
          "Node(endPoint=%s, hostId=%s, hashCode=%x)", getEndPoint(), getHostId(), hashCode());
    }
  
    /** Note: deliberately not exposed by the public interface. */
    public Set<String> getRawTokens() {
      return rawTokens;
    }
  }
  