blob: d4d5d3d0062c3510bb6c1a81a4f079c8a3d07253 [file] [log] [blame]
/*
* 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.qpid.protonj2.client.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.qpid.protonj2.client.ReconnectLocation;
/**
* Manages the list of available reconnect entries that are used to connect
* and recover a connection.
*/
public class ReconnectLocationPool {
private final LinkedList<ReconnectLocation> entries;
/**
* Creates an empty {@link ReconnectLocationPool}.
*/
public ReconnectLocationPool() {
this.entries = new LinkedList<ReconnectLocation>();
}
/**
* Creates a new {@link ReconnectLocationPool} with the provided {@link ReconnectLocation} values.
*
* @param backups
* a list of location where a reconnection attempt should be made.
*/
public ReconnectLocationPool(List<ReconnectLocation> backups) {
this.entries = new LinkedList<ReconnectLocation>();
if (backups != null) {
for (ReconnectLocation entry : backups) {
this.add(entry);
}
}
}
/**
* @return the current size of the entry pool.
*/
public int size() {
synchronized (entries) {
return entries.size();
}
}
/**
* @return true if the entry pool is empty.
*/
public boolean isEmpty() {
synchronized (entries) {
return entries.isEmpty();
}
}
/**
* Returns the next entry in the pool of entries. The entry 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 entry that should be used for a connection attempt.
*/
public ReconnectLocation getNext() {
ReconnectLocation next = null;
synchronized (entries) {
if (!entries.isEmpty()) {
next = entries.removeFirst();
entries.addLast(next);
}
}
return next;
}
/**
* Randomizes the order of the list of entries contained within the pool.
*/
public void shuffle() {
synchronized (entries) {
Collections.shuffle(entries);
}
}
/**
* Adds a new entry to the pool if not already contained within.
*
* @param entry
* The new {@link ReconnectLocation} to add to the pool.
*/
public void add(ReconnectLocation entry) {
if (entry != null) {
synchronized (entries) {
if (!contains(entry)) {
entries.add(entry);
}
}
}
}
/**
* Adds a list of new {@link ReconnectLocation} values to the pool if not already contained within.
*
* @param additions
* The new list of {@link ReconnectLocation} to add to the pool.
*/
public void addAll(List<ReconnectLocation> additions) {
if (additions != null && !additions.isEmpty()) {
synchronized (entries) {
for (ReconnectLocation entry : additions) {
add(entry);
}
}
}
}
/**
* Adds a new {@link ReconnectLocation} to the pool if not already contained within.
*
* The {@link ReconnectLocation} is added to the head of the pooled {@link ReconnectLocation} list and will be the
* next value that is returned from the pool.
*
* @param entry
* The new {@link ReconnectLocation} to add to the pool.
*/
public void addFirst(ReconnectLocation entry) {
if (entry != null) {
synchronized (entries) {
if (!contains(entry)) {
entries.addFirst(entry);
}
}
}
}
/**
* Remove a {@link ReconnectLocation} from the pool if present, otherwise has no effect.
*
* @param entry
* The {@link ReconnectLocation} to attempt to remove from the pool.
*
* @return true if the given {@link ReconnectLocation} was removed from the pool.
*/
public boolean remove(ReconnectLocation entry) {
if (entry != null) {
synchronized (entries) {
for (ReconnectLocation candidate : entries) {
if (compareEntries(entry, candidate)) {
return entries.remove(candidate);
}
}
}
}
return false;
}
/**
* Removes all currently configured {@link ReconnectLocation} from the pool, no new {@link ReconnectLocation} values
* will be served from this pool until new ones are added.
*/
public void removeAll() {
synchronized (entries) {
entries.clear();
}
}
/**
* Removes all currently configured {@link ReconnectLocation} values from the pool and replaces them with
* the new set given.
*
* @param replacements
* The new set of reconnect {@link ReconnectLocation} values to serve from this pool.
*/
public void replaceAll(List<ReconnectLocation> replacements) {
synchronized (entries) {
entries.clear();
addAll(replacements);
}
}
/**
* Gets the current list of {@link ReconnectLocation} values. The returned list is a copy.
*
* @return a copy of the current list of {@link ReconnectLocation} values in the pool.
*/
public List<ReconnectLocation> getList() {
synchronized (entries) {
return new ArrayList<>(entries);
}
}
@Override
public String toString() {
synchronized (entries) {
return "ReconnectLocationPool { " + entries + " }";
}
}
//----- Internal methods that require the locks be held ------------------//
private boolean contains(ReconnectLocation newEntry) {
boolean result = false;
for (ReconnectLocation entry : entries) {
if (compareEntries(newEntry, entry)) {
result = true;
break;
}
}
return result;
}
private boolean compareEntries(final ReconnectLocation first, final ReconnectLocation second) {
boolean result = false;
if (first == null || second == null) {
return result;
} else if (first.getPort() == second.getPort()) {
final String firstHost = first.getHost();
final String secondHost = second.getHost();
if (firstHost.equalsIgnoreCase(secondHost)) {
result = true;
}
}
return result;
}
}