/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2016, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.jdiameter.client.impl.router;
import org.jdiameter.api.*;
import org.jdiameter.api.app.StateChangeListener;
import org.jdiameter.client.api.IAnswer;
import org.jdiameter.client.api.IMessage;
import org.jdiameter.client.api.IRequest;
import org.jdiameter.client.api.controller.IPeer;
import org.jdiameter.client.api.controller.IRealmTable;
import org.jdiameter.client.api.fsm.EventTypes;
import org.jdiameter.client.api.io.IConnectionListener;
import org.jdiameter.client.api.io.TransportException;
import org.jdiameter.client.impl.helpers.XMLConfiguration;
import org.jdiameter.common.api.statistic.IStatistic;
import org.jdiameter.common.api.statistic.IStatisticManager;
import org.jdiameter.common.api.statistic.IStatisticRecord;
import org.jdiameter.common.impl.controller.AbstractPeer;
import org.jdiameter.common.impl.statistic.StatisticManagerImpl;
import org.jdiameter.server.api.agent.IAgentConfiguration;
import org.testng.annotations.Test;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URISyntaxException;
import java.net.UnknownServiceException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static org.testng.AssertJUnit.assertEquals;
/**
* Various testcases for Router implementations
*
* @author <a href="mailto:n.sowen@2scale.net">Nils Sowen</a>
*/
public class TestRouter {
@Test
public void testWeightedRoundRobin() throws Exception {
Configuration config = new XMLConfiguration("src/test/resources/jdiameter-weightedroundrobin-config.xml");
WeightedRoundRobinRouter router = new WeightedRoundRobinRouter(new RealmTableTest(), config);
IStatisticManager manager = new StatisticManagerImpl(config);
PeerTest p1 = new PeerTest(1, 1, true, manager);
PeerTest p2 = new PeerTest(2, 1, true, manager);
PeerTest p3 = new PeerTest(3, 1, true, manager);
PeerTest p4 = new PeerTest(4, 1, true, manager);
List<IPeer> peers = new ArrayList<IPeer>(3);
peers.add(p1);
peers.add(p2);
peers.add(p3);
// Test simple round robin (all weight = 1)
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
// Test weighted round robin (p1=2, p2=1, p3=1)
p1.setRating(2);
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
// Test weighted round robin (p1=2, p2=2, p3=1)
p2.setRating(2);
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
// Test equally weighted round robin (p1=2, p2=2, p3=2)
p3.setRating(2);
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
// Add Peer-4 with weight 1 to list
peers.add(p4);
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
// expected glitch here: due to the sudden availibity of Peer-4, the algorithm is disturbed
assertEquals(p4.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p4.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
assertEquals(p4.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p3.toString(), router.selectPeer(peers).toString());
// Next cycle would produce Peer-4, but reduce peer list now
peers = peers.subList(0,2); // now: Peer-1, Peer-2
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
}
@Test
public void testWeightedLeastConnections() throws Exception {
Configuration config = new XMLConfiguration("src/test/resources/jdiameter-weightedleastconnections-config.xml");
WeightedLeastConnectionsRouter router = new WeightedLeastConnectionsRouter(new RealmTableTest(), config);
IStatisticManager manager = new StatisticManagerImpl(config);
PeerTest p1 = new PeerTest(1, 1, true, manager);
PeerTest p2 = new PeerTest(2, 1, true, manager);
PeerTest p3 = new PeerTest(3, 1, true, manager);
List<IPeer> peers = new ArrayList<IPeer>(2);
peers.add(p1);
peers.add(p2);
// Test simple round robin (all weight = 1)
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
// increase p1 requests/s by 1
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).inc();
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
// increase p2 requests/s by 1
p2.getStatistic().getRecordByName(IStatisticRecord.Counters.NetGenRequestPerSecond.name()+'.'+p2.getUri()).inc();
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
// decrease p1 requests/s by 1
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.NetGenResponsePerSecond.name()+'.'+p1.getUri()).dec();
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
// increase p1 requests/s by 3
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).inc();
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).inc();
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).inc();
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
// increase p2 requests/s by 1
p2.getStatistic().getRecordByName(IStatisticRecord.Counters.NetGenRequestPerSecond.name()+'.'+p2.getUri()).inc();
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
// increase weight of p1
p1.setRating(2);
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
// decrease p1 requests/s by 1
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).dec();
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
// increase p1 requests/s by 2
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).inc();
p1.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequestPerSecond.name()+'.'+p1.getUri()).inc();
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
// increase weight and requests/s of p2
p2.setRating(2);
p2.getStatistic().getRecordByName(IStatisticRecord.Counters.NetGenRequestPerSecond.name()+'.'+p2.getUri()).inc();
p2.getStatistic().getRecordByName(IStatisticRecord.Counters.NetGenRequestPerSecond.name()+'.'+p2.getUri()).inc();
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
assertEquals(p2.toString(), router.selectPeer(peers).toString());
// increase p2 requests/s by 1
p2.getStatistic().getRecordByName(IStatisticRecord.Counters.NetGenRequestPerSecond.name()+'.'+p2.getUri()).inc();
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
assertEquals(p1.toString(), router.selectPeer(peers).toString());
}
private static class RealmTableTest implements IRealmTable {
public Realm matchRealm(IRequest request) {
return null;
}
public Realm matchRealm(IAnswer message, String destRealm) {
return null;
}
public Realm getRealm(String realmName, ApplicationId applicationId) {
return null;
}
public Realm removeRealmApplicationId(String realmName, ApplicationId appId) {
return null;
}
public Collection<Realm> removeRealm(String realmName) {
return null;
}
public Collection<Realm> getRealms(String realm) {
return null;
}
public Collection<Realm> getRealms() {
return null;
}
public String getRealmForPeer(String fqdn) {
return null;
}
public void addLocalApplicationId(ApplicationId ap) {
}
public void removeLocalApplicationId(ApplicationId a) {
}
public void addLocalRealm(String localRealm, String fqdn) {
}
public Realm addRealm(String name, ApplicationId appId, LocalAction locAction, IAgentConfiguration agentConfImpl, boolean isDynamic, long expirationTime, String[] hosts) throws InternalException {
return null;
}
public Statistic getStatistic(String realmName) {
return null;
}
public Realm addRealm(String realmName, ApplicationId applicationId, LocalAction action, String agentConfiguration, boolean dynamic, long expirationTime, String[] hosts) throws InternalException {
return null;
}
public boolean realmExists(String realmName) {
return false;
}
public boolean isWrapperFor(Class<?> iface) throws InternalException {
return false;
}
public <T> T unwrap(Class<T> iface) throws InternalException {
return null;
}
public List<String> getAllRealmSet(){
return null;
}
}
private static class PeerTest extends AbstractPeer implements IPeer {
private int id;
private int rating;
private boolean connected;
public PeerTest(int id, int rating, boolean connected, IStatisticManager manager) throws URISyntaxException, UnknownServiceException {
super(new URI("aaa://"+id), manager);
this.id = id;
this.rating = rating;
this.connected = connected;
createPeerStatistics();
}
public void setRating(int rating) {
this.rating = rating;
}
public int getRating() {
return rating;
}
public long getHopByHopIdentifier() {
return 0;
}
public void addMessage(IMessage message) {
}
public void remMessage(IMessage message) {
}
public IMessage[] remAllMessage() {
return new IMessage[0];
}
public boolean handleMessage(EventTypes type, IMessage message, String key) throws TransportException, OverloadException, InternalException {
return false;
}
public boolean sendMessage(IMessage message) throws TransportException, OverloadException, InternalException {
return false;
}
public boolean hasValidConnection() {
return connected;
}
public void setRealm(String realm) {
}
public void addStateChangeListener(StateChangeListener listener) {
}
public void remStateChangeListener(StateChangeListener listener) {
}
public void addConnectionListener(IConnectionListener listener) {
}
public void remConnectionListener(IConnectionListener listener) {
}
public IStatistic getStatistic() {
return statistic;
}
public boolean isConnected() {
return connected;
}
public void connect() throws InternalException, IOException, IllegalDiameterStateException {
}
@Override
public void disconnect(int disconnectCause) throws InternalException, IllegalDiameterStateException {
}
public <E> E getState(Class<E> enumc) {
return null;
}
public URI getUri() {
return uri;
}
public InetAddress[] getIPAddresses() {
return new InetAddress[0];
}
public String getRealmName() {
return null;
}
public long getVendorId() {
return 0;
}
public String getProductName() {
return null;
}
public long getFirmware() {
return 0;
}
public Set<ApplicationId> getCommonApplications() {
return null;
}
public void addPeerStateListener(PeerStateListener listener) {
}
public void removePeerStateListener(PeerStateListener listener) {
}
@Override
public String toString() {
return "Peer-"+id;
}
}
}