/*
 * 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 org.apache.tinkerpop.gremlin.driver;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

/**
 * Identifies a server within the {@link Cluster} at a specific address.
 *
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public final class Host {
    private static final Logger logger = LoggerFactory.getLogger(Host.class);
    private final InetSocketAddress address;
    private final URI hostUri;
    private volatile boolean isAvailable;
    private final Cluster cluster;
    private final String hostLabel;

    final AtomicReference<ScheduledFuture<?>> reconnectionAttempt = new AtomicReference<>(null);

    Host(final InetSocketAddress address, final Cluster cluster) {
        this.cluster = cluster;
        this.address = address;
        this.hostUri = makeUriFromAddress(address, cluster.connectionPoolSettings().enableSsl);
        hostLabel = String.format("Host{address=%s, hostUri=%s}", address, hostUri);
    }

    public InetSocketAddress getAddress() {
        return address;
    }

    public URI getHostUri() {
        return hostUri;
    }

    public boolean isAvailable() {
        return isAvailable;
    }

    void makeAvailable() {
        isAvailable = true;
    }

    void makeUnavailable(final Function<Host, Boolean> reconnect) {
        isAvailable = false;

        // only do a connection re-attempt if one is not already in progress
        reconnectionAttempt.compareAndSet(null,
                this.cluster.executor().scheduleAtFixedRate(() -> {
                            logger.debug("Trying to reconnect to dead host at {}", this);
                            if (reconnect.apply(this)) reconnected();
                        }, cluster.connectionPoolSettings().reconnectInitialDelay,
                        cluster.connectionPoolSettings().reconnectInterval, TimeUnit.MILLISECONDS));
    }

    private void reconnected() {
        reconnectionAttempt.get().cancel(false);
        reconnectionAttempt.set(null);
        makeAvailable();
    }

    private static URI makeUriFromAddress(final InetSocketAddress addy, final boolean ssl) {
        try {
            final String scheme = ssl ? "wss" : "ws";
            return new URI(scheme, null, addy.getHostName(), addy.getPort(), "/gremlin", null, null);
        } catch (URISyntaxException use) {
            throw new RuntimeException(String.format("URI for host could not be constructed from: %s", addy), use);
        }
    }

    @Override
    public String toString() {
        return hostLabel;
    }

    public static interface Listener {
        public void onAvailable(final Host host);

        public void onUnavailable(final Host host);

        public void onNew(final Host host);

        public void onRemove(final Host host);
    }
}

@@ -2464,32 +2464,46 @@
 rogress%0A        
+synchronized (
 reconnectionAtte
@@ -2509,28 +2509,64 @@
 empt
-.compareAndSet(
+) %7B%0A            if (reconnectionAttempt.get() == 
 null
-,
+) %7B
 %0A   
@@ -2570,32 +2570,56 @@
                 
+reconnectionAttempt.set(
 this.cluster.exe
@@ -2674,24 +2674,16 @@
         
-  
-      
 logger.d
@@ -2752,32 +2752,24 @@
             
-        
 if (reconnec
@@ -2782,16 +2782,40 @@
 y(this))
+%0A                       
  reconne
@@ -2822,24 +2822,16 @@
 cted();%0A
-        
         
@@ -2900,32 +2900,8 @@
 lay,
-%0A                       
  clu
@@ -2974,16 +2974,40 @@
 ONDS));%0A
+            %7D%0A        %7D%0A
     %7D%0A%0A 

Check the value of reconnectionAttempt and create scheduled task as a 2 step process.