/******************************************************************************* * Copyright 2013-2015 alladin-IT GmbH * * 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 at.alladin.rmbt.client; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import at.alladin.rmbt.client.v2.task.AbstractQoSTask; import at.alladin.rmbt.client.v2.task.DnsTask; import at.alladin.rmbt.client.v2.task.HttpProxyTask; import at.alladin.rmbt.client.v2.task.NonTransparentProxyTask; import at.alladin.rmbt.client.v2.task.QoSControlConnection; import at.alladin.rmbt.client.v2.task.QoSTestEnum; import at.alladin.rmbt.client.v2.task.QoSTestErrorEnum; import at.alladin.rmbt.client.v2.task.TaskDesc; import at.alladin.rmbt.client.v2.task.TcpTask; import at.alladin.rmbt.client.v2.task.TracerouteTask; import at.alladin.rmbt.client.v2.task.UdpTask; import at.alladin.rmbt.client.v2.task.VoipTask; import at.alladin.rmbt.client.v2.task.WebsiteTask; import at.alladin.rmbt.client.v2.task.result.QoSResultCollector; import at.alladin.rmbt.client.v2.task.result.QoSTestResult; import at.alladin.rmbt.client.v2.task.result.QoSTestResultEnum; import at.alladin.rmbt.client.v2.task.service.TestProgressListener.TestProgressEvent; import at.alladin.rmbt.client.v2.task.service.TestSettings; import at.alladin.rmbt.client.v2.task.service.TrafficService; /** * * @author lb * */ public class QualityOfServiceTest implements Callable<QoSResultCollector> { private final RMBTClient client; private final AtomicInteger progress = new AtomicInteger(); private final AtomicInteger testCount = new AtomicInteger(); private final AtomicInteger concurrentGroupCount = new AtomicInteger(); private final AtomicReference<QoSTestEnum> status = new AtomicReference<QoSTestEnum>(); private final AtomicReference<QoSTestErrorEnum> errorStatus = new AtomicReference<QoSTestErrorEnum>(QoSTestErrorEnum.NONE); private final ExecutorService executor; private final ExecutorCompletionService<QoSTestResult> executorService; private final TestSettings qoSTestSettings; final TreeMap<Integer, List<AbstractQoSTask>> concurrentTasks = new TreeMap<Integer, List<AbstractQoSTask>>(); final TreeMap<QoSTestResultEnum, List<AbstractQoSTask>> testMap = new TreeMap<QoSTestResultEnum, List<AbstractQoSTask>>(); final TreeMap<String, QoSControlConnection> controlConnectionMap = new TreeMap<String, QoSControlConnection>(); private TreeMap<QoSTestResultEnum, Counter> testGroupCounterMap = new TreeMap<QoSTestResultEnum, Counter>(); /** * * @param client * @param websiteTestImpl * @param trafficService */ public QualityOfServiceTest(RMBTClient client, TestSettings nnTestSettings) { System.out.println("\n\n---- Initializing QoS Tests ----\n"); this.client = client; executor = Executors.newFixedThreadPool(client.getTaskDescList().size()); executorService = new ExecutorCompletionService<QoSTestResult>(executor); status.set(QoSTestEnum.START); testCount.set(client.getTaskDescList().size()); this.qoSTestSettings = nnTestSettings; int threadCounter = 0; for (TaskDesc taskDesc : client.getTaskDescList()) { String taskId = (String) taskDesc.getParams().get(TaskDesc.QOS_TEST_IDENTIFIER_KEY); AbstractQoSTask test = null; if (RMBTClient.TASK_HTTP.equals(taskId)) { test = new HttpProxyTask(this, taskDesc, threadCounter++); } else if (RMBTClient.TASK_NON_TRANSPARENT_PROXY.equals(taskId)) { test = new NonTransparentProxyTask(this, taskDesc, threadCounter++); } else if (RMBTClient.TASK_DNS.equals(taskId)) { test = new DnsTask(this, taskDesc, threadCounter++); } else if (RMBTClient.TASK_TCP.equals(taskId)) { test = new TcpTask(this, taskDesc, threadCounter++); } else if (RMBTClient.TASK_UDP.equals(taskId)) { test = new UdpTask(this, taskDesc, threadCounter++); } else if (RMBTClient.TASK_VOIP.equals(taskId)) { test = new VoipTask(this, taskDesc, threadCounter++); } else if (RMBTClient.TASK_TRACEROUTE.equals(taskId)) { if (nnTestSettings != null && nnTestSettings.getTracerouteServiceClazz() != null) { test = new TracerouteTask(this, taskDesc, threadCounter++); } else { System.out.println("No TracerouteService implementation: Skipping TracerouteTask: " + taskDesc); } } else if (RMBTClient.TASK_WEBSITE.equals(taskId)) { if (nnTestSettings != null && nnTestSettings.getWebsiteTestService() != null) { test = new WebsiteTask(this, taskDesc, threadCounter++); } else { System.out.println("No WebsiteTestService implementation: Skipping WebsiteTask: " + taskDesc); } } if (test != null) { //manage taskMap: List<AbstractQoSTask> testList = null; testList = testMap.get(test.getTestType()); if (testList == null) { testList = new ArrayList<AbstractQoSTask>(); testMap.put(test.getTestType(), testList); } testList.add(test); Counter testTypeCounter; if (testGroupCounterMap.containsKey(test.getTestType())) { testTypeCounter = testGroupCounterMap.get(test.getTestType()); testTypeCounter.increaseCounter(test.getConcurrencyGroup()); } else { testTypeCounter = new Counter(test.getTestType(), 1, test.getConcurrencyGroup()); testGroupCounterMap.put(test.getTestType(), testTypeCounter); } //manage concurrent test groups List<AbstractQoSTask> tasks = null; if (concurrentTasks.containsKey(test.getConcurrencyGroup())) { tasks = concurrentTasks.get(test.getConcurrencyGroup()); } else { tasks = new ArrayList<AbstractQoSTask>(); concurrentTasks.put(test.getConcurrencyGroup(), tasks); } if (tasks != null) { tasks.add(test); } if (!controlConnectionMap.containsKey(test.getTestServerAddr())) { RMBTTestParameter params = new RMBTTestParameter(test.getTestServerAddr(), test.getTestServerPort(), nnTestSettings.isUseSsl(), test.getTaskDesc().getToken(), test.getTaskDesc().getDuration(), test.getTaskDesc().getNumThreads(), test.getTaskDesc().getNumPings(), test.getTaskDesc().getStartTime()); controlConnectionMap.put(test.getTestServerAddr(), new QoSControlConnection(getRMBTClient(), params)); } //check if qos test need test server if (test.needsQoSControlConnection()) { test.setControlConnection(controlConnectionMap.get(test.getTestServerAddr())); controlConnectionMap.get(test.getTestServerAddr()).getConcurrencyGroupSet().add(test.getConcurrencyGroup()); } } } if (qoSTestSettings != null) { qoSTestSettings.dispatchTestProgressEvent(TestProgressEvent.ON_CREATED, null, this); } } /** * */ public QoSResultCollector call() throws Exception { status.set(QoSTestEnum.QOS_RUNNING); QoSResultCollector result = new QoSResultCollector(); final int testSize = testCount.get(); int trafficServiceStatus = TrafficService.SERVICE_NOT_SUPPORTED; if (qoSTestSettings != null && qoSTestSettings.getTrafficService() != null) { trafficServiceStatus = qoSTestSettings.getTrafficService().start(); } Iterator<Integer> groupIterator = concurrentTasks.keySet().iterator(); while (groupIterator.hasNext() && !status.get().equals(QoSTestEnum.ERROR)) { final int groupId = groupIterator.next(); concurrentGroupCount.set(groupId); //check if a qos control server connection needs to be initialized: openControlConnections(groupId); if (status.get().equals(QoSTestEnum.ERROR)) { break; } List<AbstractQoSTask> tasks = concurrentTasks.get(groupId); for (AbstractQoSTask task : tasks) { executorService.submit(task); } for (int i = 0; i < tasks.size(); i++) { try { Future<QoSTestResult> testResult = executorService.take(); if (testResult!=null) { QoSTestResult curResult = testResult.get(); if (curResult.isFatalError()) { throw new InterruptedException("interrupted due to test fatal error: " + curResult.toString()); } if (!curResult.getQosTask().hasConnectionError()) { result.getResults().add(curResult); } else { System.out.println("test: " + curResult.getTestType().name() + " failed. Could not connect to QoSControlServer."); } System.out.println("test " + curResult.getTestType().name() + " finished (" + (progress.get() + 1) + " out of " + testSize + ", CONCURRENCY GROUP=" + groupId + ")"); Counter testTypeCounter = testGroupCounterMap.get(curResult.getTestType()); if (testTypeCounter != null) { testTypeCounter.value++; } } } catch (InterruptedException e) { executor.shutdownNow(); e.printStackTrace(); status.set(QoSTestEnum.ERROR); break; } catch (Exception e) { e.printStackTrace(); } finally { progress.incrementAndGet(); } } closeControlConnections(groupId); } if (status.get().equals(QoSTestEnum.ERROR)) { progress.set(testCount.get()); } if (trafficServiceStatus != TrafficService.SERVICE_NOT_SUPPORTED) { qoSTestSettings.getTrafficService().stop(); System.out.println("TRAFFIC SERVICE: Tx Bytes = " + qoSTestSettings.getTrafficService().getTxBytes() + ", Rx Bytes = " + qoSTestSettings.getTrafficService().getRxBytes()); } if (status.get() != QoSTestEnum.ERROR) { status.set(QoSTestEnum.QOS_FINISHED); } if (executor != null) executor.shutdownNow(); return result; } /** * * @return */ public int getProgress() { final int progress = this.progress.get(); return progress; } /** * * @return */ public int getTestSize() { final int testSize = this.testCount.get(); return testSize; } /** * * @return */ public QoSTestEnum getStatus() { final QoSTestEnum status = this.status.get(); return status; } /** * * @param newStatus */ public void setStatus(QoSTestEnum newStatus) { this.status.set(newStatus); } /** * * @return */ public QoSTestErrorEnum getErrorStatus() { final QoSTestErrorEnum status = this.errorStatus.get(); return status; } /** * * @param newStatus */ public void setErrorStatus(QoSTestErrorEnum newStatus) { this.errorStatus.set(newStatus); } /** * * @return */ public int getCurrentConcurrentGroup() { final int currentGroupCount = this.concurrentGroupCount.get(); return currentGroupCount; } /** * * @return */ public Map<QoSTestResultEnum, Counter> getTestGroupCounterMap() { return testGroupCounterMap; } /** * * @return */ public TestSettings getTestSettings() { return qoSTestSettings; } /** * * @return */ public RMBTClient getRMBTClient() { return client; } /** * * @return */ public TreeMap<QoSTestResultEnum, List<AbstractQoSTask>> getTestMap() { return testMap; } /** * @return * */ public synchronized void interrupt() { if (executor != null) executor.shutdownNow(); } @Override protected void finalize() throws Throwable { super.finalize(); if (executor != null) executor.shutdownNow(); } /** * * @author lb * */ public final class Counter { public QoSTestResultEnum testType; public int value; public int target; public int firstTest; public int lastTest; public Counter(QoSTestResultEnum testType, int target, int concurrencyGroup) { this.testType = testType; this.value = 0; this.target = target; this.firstTest = concurrencyGroup; this.lastTest = concurrencyGroup; } public void increaseCounter(int concurrencyGroup) { this.target++; lastTest = concurrencyGroup > lastTest ? concurrencyGroup : lastTest; firstTest = concurrencyGroup < firstTest ? concurrencyGroup : firstTest; } @Override public String toString() { return "Counter [testType=" + testType + ", value=" + value + ", target=" + target + ", firstTest=" + firstTest + ", lastTest=" + lastTest + "]"; } } private void openControlConnections(int concurrencyGroup) { manageControlConnections(concurrencyGroup, true); } private void closeControlConnections(int concurrencyGroup) { manageControlConnections(concurrencyGroup, false); } private void manageControlConnections(int concurrencyGroup, boolean openAll) { Iterator<QoSControlConnection> iterator = controlConnectionMap.values().iterator(); while (iterator.hasNext()) { final QoSControlConnection controlConnection = iterator.next(); try { if (controlConnection.getConcurrencyGroupSet().size() > 0) { if (openAll) { if (controlConnection.getConcurrencyGroupSet().first() == concurrencyGroup) { controlConnection.connect(); RMBTClient.getCommonThreadPool().execute(controlConnection); } } else { if (controlConnection.getConcurrencyGroupSet().last() == concurrencyGroup) { controlConnection.close(); } } } } catch (Exception e) { e.printStackTrace(); // executor.shutdownNow(); // status.set(QoSTestEnum.ERROR); // break; } } } }