/**
* Copyright 2016-2017 Sixt GmbH & Co. Autovermietung KG
* Licensed 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.sixt.service.framework.rpc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ServiceEndpointList {
private static final Logger logger = LoggerFactory.getLogger(ServiceEndpointList.class);
protected volatile int size = 0;
protected ServiceEndpointNode returnNext = null;
protected ReentrantReadWriteLock mutex = new ReentrantReadWriteLock();
public void add(ServiceEndpoint sep) {
mutex.writeLock().lock();
try {
if (size == 0) {
ServiceEndpointNode node = new ServiceEndpointNode(sep);
this.returnNext = node;
node.prev = node;
node.next = node;
} else {
ServiceEndpointNode node = new ServiceEndpointNode(sep);
ServiceEndpointNode previous = returnNext;
returnNext = node;
node.next = previous.next;
node.next.prev = node;
node.prev = previous;
previous.next = node;
}
size++;
} finally {
mutex.writeLock().unlock();
}
}
public ServiceEndpoint nextAvailable() {
mutex.writeLock().lock(); //needs write b/c it calls canServeRequests
try {
ServiceEndpointNode retval = returnNext;
for (int i = 0; i < size; i++) {
if (retval.value.canServeRequests()) {
retval.value.incrementServingRequests();
returnNext = retval.next;
return retval.value;
} else {
returnNext = returnNext.next;
retval = returnNext;
}
}
//if we got here, there are none available
return null;
} finally {
mutex.writeLock().unlock();
}
}
public boolean isEmpty() {
mutex.readLock().lock();
try {
return size == 0;
} finally {
mutex.readLock().unlock();
}
}
public int size() {
mutex.readLock().lock();
try {
return size;
} finally {
mutex.readLock().unlock();
}
}
public void updateEndpointHealth(ServiceEndpoint ep, CircuitBreakerState.State state) {
mutex.readLock().lock();
try {
ServiceEndpointNode current = returnNext;
int count = size;
for (int i = 0; i < count; i++) {
if (current.value.getHostAndPort().equals(ep.getHostAndPort())) {
current.value.setCircuitBreakerState(state);
return;
}
current = current.next;
}
logger.error("updateEndpointHealth: endpoint not found: {}", ep.toString());
} finally {
mutex.readLock().unlock();
}
}
//intended only for debugging
public void debugDump(StringBuilder sb) {
mutex.writeLock().lock();
try {
ServiceEndpointNode current = returnNext;
for (int i = 0; i < size; i++) {
sb.append(" ").append(current.toString()).append(": ").
append(current.value.getCircuitBreakerState()).append("\n");
current = current.next;
}
} finally {
mutex.writeLock().unlock();
}
}
}
class ServiceEndpointNode {
ServiceEndpoint value;
ServiceEndpointNode prev;
ServiceEndpointNode next;
public ServiceEndpointNode(ServiceEndpoint value) {
this.value = value;
}
@Override
public String toString() {
return value.toString();
}
}