Merge "Enable retries for the /authn/validate endpoint if it fails to connect"
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java
index c48e35f..bd94d0a 100644
--- a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java
+++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java
@@ -22,6 +22,9 @@
 package org.onap.aaf.cadi.aaf.v2_0;
 
 import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.onap.aaf.cadi.AbsUserCache;
 import org.onap.aaf.cadi.CachedPrincipal;
@@ -29,6 +32,7 @@
 import org.onap.aaf.cadi.User;
 import org.onap.aaf.cadi.aaf.AAFPermission;
 import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
 import org.onap.aaf.cadi.lur.ConfigPrincipal;
 
 import aaf.v2_0.CredRequest;
@@ -137,32 +141,43 @@
         }
 
         public Resp revalidate(Object state) {
-            try {
-                Miss missed = missed(getName(),getCred());
-                if (missed==null || missed.mayContinue()) {
-                    CredRequest cr = new CredRequest();
-                    cr.setId(getName());
-                    cr.setPassword(new String(getCred()));
-                    Future<String> fp = con.client().readPost("/authn/validate", con.credReqDF, cr);
-                    //Rcli<CLIENT> client = con.client().forUser(con.basicAuth(getName(), new String(getCred())));
-                    //Future<String> fp = client.read(
-                    //        "/authn/basicAuth",
-                    //        "text/plain"
-                    //       );
-                     if (fp.get(con.timeout)) {
-                        expires = System.currentTimeMillis() + timeToLive;
-                        addUser(new User<AAFPermission>(this, expires));
-                        return Resp.REVALIDATED;
+            List<URI> attemptedUris = new ArrayList<>();
+            URI thisUri = null;
+            for (int retries = 0;; retries++) {
+                try {
+                    Miss missed = missed(getName(), getCred());
+                    if (missed == null || missed.mayContinue()) {
+                        CredRequest cr = new CredRequest();
+                        cr.setId(getName());
+                        cr.setPassword(new String(getCred()));
+                        Rcli<CLIENT> client = con.clientIgnoreAlreadyAttempted(attemptedUris);
+                        thisUri = client.getURI();
+                        Future<String> fp = client.readPost("/authn/validate", con.credReqDF, cr);
+                        //Rcli<CLIENT> client = con.client().forUser(con.basicAuth(getName(), new String(getCred())));
+                        //Future<String> fp = client.read(
+                        //        "/authn/basicAuth",
+                        //        "text/plain"
+                        //       );
+                        if (fp.get(con.timeout)) {
+                            expires = System.currentTimeMillis() + timeToLive;
+                            addUser(new User<AAFPermission>(this, expires));
+                            return Resp.REVALIDATED;
+                        } else {
+                            addMiss(getName(), getCred());
+                            return Resp.UNVALIDATED;
+                        }
                     } else {
-                        addMiss(getName(), getCred());
                         return Resp.UNVALIDATED;
                     }
-                } else {
-                    return Resp.UNVALIDATED;
+                } catch (Exception e) {
+                    if (thisUri != null)  {
+                        attemptedUris.add(thisUri);
+                    }
+                    con.access.log(e);
+                    if (retries > 2) {
+                        return Resp.INACCESSIBLE;
+                    }
                 }
-            } catch (Exception e) {
-                con.access.log(e);
-                return Resp.INACCESSIBLE;
             }
         }
 
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java
index 88333d8..9830309 100644
--- a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java
+++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java
@@ -23,6 +23,7 @@
 
 import java.net.URI;
 import java.net.UnknownHostException;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -265,6 +266,24 @@
     }
 
 
+    /**
+     * Use this call to get the appropriate client based on configuration (HTTP, future),
+     * ignoring those already attempted, using the default api version
+     *
+     * @param attemptedClients
+     * @return
+     * @throws CadiException
+     */
+    public Rcli<CLIENT> clientIgnoreAlreadyAttempted(List<URI> attemptedClients) throws CadiException {
+        Rcli<CLIENT> client = rclient(attemptedClients, si.defSS);
+        client.apiVersion(apiVersion)
+                .readTimeout(connTimeout);
+        clients.put(apiVersion, client);
+
+        return client;
+    }
+
+
     public RosettaEnv env() {
         return env;
     }
@@ -336,6 +355,8 @@
 
     protected abstract Rcli<CLIENT> rclient(URI uri, SecuritySetter<CLIENT> ss) throws CadiException;
 
+    protected abstract Rcli<CLIENT> rclient(List<URI> uris, SecuritySetter<CLIENT> ss) throws CadiException;
+
     public abstract Rcli<CLIENT> rclient(Locator<URI> loc, SecuritySetter<CLIENT> ss) throws CadiException;
 
     public Rcli<CLIENT> client(Locator<URI> locator) throws CadiException {
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java
index 7ccf3e6..84ef788 100644
--- a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java
+++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java
@@ -24,6 +24,7 @@
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URI;
+import java.util.List;
 
 import org.onap.aaf.cadi.Access;
 import org.onap.aaf.cadi.CadiException;
@@ -136,6 +137,34 @@
         }
     }
 
+    protected Rcli<HttpURLConnection> rclient(List<URI> ignoredURIs, SecuritySetter<HttpURLConnection> ss) throws CadiException {
+        if (hman.loc==null) {
+            throw new CadiException("No Locator set in AAFConHttp");
+        }
+        try {
+            if (ignoredURIs.isEmpty()) {
+                return new HRcli(hman, hman.loc.best(), ss);
+            } else {
+                Item item = hman.loc.first();
+                HRcli currentClient = new HRcli(hman, item, ss);
+
+                item = hman.loc.next(item);
+
+                while (item != null) {
+                    if (!ignoredURIs.contains(currentClient.getURI())) {
+                        break;
+                    } else {
+                        currentClient = new HRcli(hman, item, ss);
+                    }
+                    item = hman.loc.next(item);
+                }
+                return currentClient;
+            }
+        } catch (Exception e) {
+            throw new CadiException(e);
+        }
+    }
+
     @Override
     public Rcli<HttpURLConnection> rclient(Locator<URI> loc, SecuritySetter<HttpURLConnection> ss) throws CadiException {
         try {