package com.workshare.msnos.usvc.api.routing; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.workshare.msnos.usvc.IMicroservice; import com.workshare.msnos.usvc.RemoteMicroservice; import com.workshare.msnos.usvc.api.RestApi; import com.workshare.msnos.usvc.api.routing.strategies.CachingRoutingStrategy; import com.workshare.msnos.usvc.api.routing.strategies.CompositeStrategy; import com.workshare.msnos.usvc.api.routing.strategies.LocationBasedStrategy; import com.workshare.msnos.usvc.api.routing.strategies.PriorityRoutingStrategy; import com.workshare.msnos.usvc.api.routing.strategies.RoundRobinRoutingStrategy; import com.workshare.msnos.usvc.api.routing.strategies.SkipFaultiesRoutingStrategy; public class ApiList { private static Logger log = LoggerFactory.getLogger(ApiList.class); private volatile List<ApiEndpoint> endpointsList; private volatile RestApi affinite; transient private final RoutingStrategy routing; transient private final Lock addRemoveLock = new ReentrantLock(); public ApiList() { this(defaultRoutingStrategy()); } public ApiList(RoutingStrategy routingStrategy) { this.endpointsList = new ArrayList<ApiEndpoint>(); this.routing = routingStrategy; } public void add(RemoteMicroservice remote, RestApi rest) { addRemoveLock.lock(); try { LinkedHashSet<ApiEndpoint> newEndpoints = new LinkedHashSet<ApiEndpoint>(endpointsList); newEndpoints.add(new ApiEndpoint(remote, rest)); endpointsList = new ArrayList<ApiEndpoint>(newEndpoints); } finally { addRemoveLock.unlock(); } } public void remove(RemoteMicroservice toRemove) { addRemoveLock.lock(); try { List<ApiEndpoint> newEndpoints = new ArrayList<ApiEndpoint>(endpointsList); for (int i = 0; i < newEndpoints.size(); i++) { final ApiEndpoint endpoint = newEndpoints.get(i); if (endpoint.service().equals(toRemove)) { newEndpoints.remove(i); endpoint.api().markFaulty(); if (affinite == endpoint.api()) { affinite = null; } break; } } endpointsList = newEndpoints; } finally { addRemoveLock.unlock(); } } public int size() { return endpointsList.size(); } // TODO FIXME this need to be changed for performance reason // as we are creating a new list every time public List<RestApi> getApis() { List<RestApi> result = new ArrayList<RestApi>(); for (ApiEndpoint api : endpointsList) { result.add(api.api()); } return result; } public List<ApiEndpoint> getEndpoints() { return Collections.unmodifiableList(endpointsList); } public RestApi get(IMicroservice from) { if (endpointsList.size() == 0) return null; if (affinite != null && !affinite.isFaulty()) { return affinite; } RestApi result = getUsingStrategies(from); affinite = null; if (result != null && result.hasAffinity()) { affinite = result; } return result; } private RestApi getUsingStrategies(IMicroservice from) { ApiEndpoint res; try { res = routing.select(from, endpointsList).get(0); } catch (Throwable ex) { log.warn("Unexpected error selecting API", ex); res = endpointsList.size() > 0 ? endpointsList.get(0) : null; } return res == null ? null : res.api(); } @Override public String toString() { return "endpoints="+this.endpointsList+", affinite="+affinite+",hashcode="+super.hashCode(); } static RoutingStrategy defaultRoutingStrategy() { List<RoutingStrategy> strategies = new ArrayList<RoutingStrategy>(Arrays.asList(new SkipFaultiesRoutingStrategy(), new LocationBasedStrategy(), new RoundRobinRoutingStrategy())); if (PriorityRoutingStrategy.isEnabled()) { strategies.add(1, new PriorityRoutingStrategy()); } final CompositeStrategy composite = new CompositeStrategy(strategies.toArray(new RoutingStrategy[strategies.size()])); return new CachingRoutingStrategy(composite); } }