/** * 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 io.hawtjms.provider.failover; import io.hawtjms.util.URISupport; import java.io.IOException; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.LinkedList; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manages the list of available failover URIs that are used to connect * and recover a connection. */ public class FailoverUriPool { private static final Logger LOG = LoggerFactory.getLogger(FailoverUriPool.class); private final LinkedList<URI> uris; private final Map<String, String> nestedOptions; private boolean randomize; public FailoverUriPool() { this.uris = new LinkedList<URI>(); this.nestedOptions = Collections.emptyMap(); } public FailoverUriPool(URI[] uris, Map<String, String> nestedOptions) { this.uris = new LinkedList<URI>(); if (nestedOptions != null) { this.nestedOptions = nestedOptions; } else { this.nestedOptions = Collections.emptyMap(); } if (uris != null) { for (URI uri : uris) { this.add(uri); } } } /** * Returns the next URI in the pool of URIs. The URI will be shifted to the * end of the list and not be attempted again until the full list has been * returned once. * * @return the next URI that should be used for a connection attempt. */ public URI getNext() { URI next = null; if (!uris.isEmpty()) { next = uris.removeFirst(); uris.addLast(next); } return next; } /** * Reports that the Failover Provider connected to the last URI returned from * this pool. If the Pool is set to randomize this will result in the Pool of * URIs being shuffled in preparation for the next connect cycle. */ public void connected() { if (isRandomize()) { Collections.shuffle(uris); } } /** * @return true if this pool returns the URI values in random order. */ public boolean isRandomize() { return randomize; } /** * Sets whether the URIs that are returned by this pool are returned in random * order or not. If false the URIs are returned in FIFO order. * * @param randomize * true to have the URIs returned in a random order. */ public void setRandomize(boolean randomize) { this.randomize = randomize; if (randomize) { Collections.shuffle(uris); } } /** * Adds a new URI to the pool if not already contained within. The URI will have * any nest options that have been configured added to its existing set of options. * * @param uri * The new URI to add to the pool. */ public void add(URI uri) { if (!contains(uri)) { if (!nestedOptions.isEmpty()) { try { URISupport.applyParameters(uri, nestedOptions); } catch (URISyntaxException e) { LOG.debug("Failed to add nested options to uri: {}", uri); } } this.uris.add(uri); } } /** * Remove a URI from the pool if present, otherwise has no effect. * * @param uri * The URI to attempt to remove from the pool. */ public void remove(URI uri) { this.uris.remove(uri); } /** * Returns the currently set value for nested options which will be added to each * URI that is returned from the pool. * * @return the Map instance containing the nest options which can be empty if none set. */ public Map<String, String> getNestedOptions() { return nestedOptions; } private boolean contains(URI newURI) { boolean result = false; for (URI uri : uris) { if (compareURIs(newURI, uri)) { result = true; break; } } return result; } private boolean compareURIs(final URI first, final URI second) { boolean result = false; if (first == null || second == null) { return result; } if (first.getPort() == second.getPort()) { InetAddress firstAddr = null; InetAddress secondAddr = null; try { firstAddr = InetAddress.getByName(first.getHost()); secondAddr = InetAddress.getByName(second.getHost()); if (firstAddr.equals(secondAddr)) { result = true; } } catch(IOException e) { if (firstAddr == null) { LOG.error("Failed to Lookup INetAddress for URI[ " + first + " ] : " + e); } else { LOG.error("Failed to Lookup INetAddress for URI[ " + second + " ] : " + e); } if (first.getHost().equalsIgnoreCase(second.getHost())) { result = true; } } } return result; } }