package mireka.transmission.immediate; import java.util.ArrayList; import java.util.Collections; import java.util.List; import mireka.smtp.client.BackendServer; /** * An Upstream is a load balanced set of BackendServers. A specific server is * selected randomly but considering its weight and whether it is a backup or * primary server. */ public class Upstream { private List<BackendServer> servers; public List<BackendServer> orderedServerList() { List<BackendServer> shuffledServers = new ArrayList<>(); shuffledServers.addAll(shuffle(getPrimaryServers())); shuffledServers.addAll(shuffle(getBackupServers())); return shuffledServers; } private List<BackendServer> getPrimaryServers() { List<BackendServer> result = new ArrayList<>(); for (BackendServer server : servers) { if (!server.isBackup()) result.add(server); } return result; } private List<BackendServer> getBackupServers() { List<BackendServer> result = new ArrayList<>(); for (BackendServer server : servers) { if (server.isBackup()) result.add(server); } return result; } private List<BackendServer> shuffle(List<BackendServer> servers) { List<BackendServer> shuffledList = new ArrayList<>(); List<BackendServer> remainingServers = new ArrayList<>(servers); if (remainingServers.isEmpty()) return Collections.emptyList(); while (remainingServers.size() > 1) { int iSelected = selectFrom(remainingServers); BackendServer selected = remainingServers.remove(iSelected); shuffledList.add(selected); } shuffledList.add(remainingServers.get(0)); return shuffledList; } private int selectFrom(List<BackendServer> servers) { double drawnNumber = Math.random() * totalWeight(servers); double weigthsUntilNow = 0; for (int i = 0; i < servers.size(); i++) { BackendServer sender = servers.get(i); weigthsUntilNow += sender.getWeight(); if (drawnNumber < weigthsUntilNow) return i; } return servers.size() - 1; } private double totalWeight(List<BackendServer> servers) { double total = 0; for (BackendServer server : servers) { total += server.getWeight(); } return total; } /** @x.category GETSET **/ public List<BackendServer> getServers() { return servers; } /** @x.category GETSET **/ public void setServers(List<BackendServer> servers) { this.servers = servers; } }