package org.simpleframework.http.validate; import java.io.File; import java.io.InputStream; import java.io.PushbackInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Vector; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.simpleframework.common.buffer.Buffer; import org.simpleframework.xml.Attribute; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; import org.simpleframework.xml.Transient; import org.simpleframework.xml.core.Commit; import org.simpleframework.xml.core.Persister; /** * <test debug="false" validate="false" repeat='10' timeout='10000'> * <connection host='localhost' port='8080'> * <request method='GET' target='/path.html'> * <header name='Content-Type'>text/plain</header> * <header name='Content-Length'>10</header> * <body>name=value&a=b</body> * </request> * </connection> * </test> * */ @Root public class Test { @Element private ConnectionTask connection; @Element private Analyser analyser; @Attribute private int timeout; @Attribute private int startConcurrency; @Attribute private int finishConcurrency; @Attribute private int sampleDuration; @Attribute private File logFile; @Attribute(required=false) private boolean validate; @Attribute(required=false) private boolean debug; @Transient private int repeat; @Commit private void commit() throws Exception { if(startConcurrency < 1) { throw new IllegalArgumentException("Start concurrency must be greater than '"+startConcurrency+"'"); } if(startConcurrency > finishConcurrency) { throw new IllegalArgumentException("Start concurrency '"+startConcurrency+"' must be less than finish concurrency than '"+finishConcurrency+"'"); } repeat = connection.getRepeat(); } public void execute(File configDir) throws Exception { ExecutorService executor = Executors.newFixedThreadPool((finishConcurrency + 1) * 2); StatisticsFileLogger logWriter = new StatisticsFileLogger(logFile); AtomicInteger activeClients = new AtomicInteger(startConcurrency); Logger logger = new Logger(logWriter, activeClients, sampleDuration); try { for(int concurrency = startConcurrency; concurrency <= finishConcurrency; concurrency++) { List<Buffer> list = execute(executor, configDir, logger, concurrency); if(validate) { List<Report> report = validate(list, configDir, concurrency); dump(report, configDir, debug); } activeClients.getAndIncrement(); } }finally { executor.shutdown(); executor.awaitTermination(5000L, TimeUnit.MILLISECONDS); } } public void investigate(Buffer buffer, File configDir) throws Exception { if(debug) { System.out.println("Investigating an issue"); } List<Buffer> list = Collections.singletonList(buffer); List<Report> report = validate(list, configDir, 1); dump(report, configDir, debug); } private void dump(List<Report> report, File configDir, boolean debug) throws Exception { Persister persister = new Persister(); int count = report.size(); for(int i = 0; i < count; i++) { Report entry = report.get(i); if(entry.isError() || debug) { String output = String.format("report-%s.xml", i); File file = new File(configDir, output); persister.write(entry, file); } else if(debug){ System.out.printf("PASS: %s of %s - %s%n", i, count, entry.getStatusLine()); } } } private List<Buffer> execute(ExecutorService executor, File configDir, Logger logger, int activeClients) throws Exception { Client client = new Client(executor, logger, configDir, this, activeClients, timeout, repeat, debug); List<Buffer> list = new Vector<Buffer>(); CountDownLatch start = new CountDownLatch(activeClients); CountDownLatch finish = new CountDownLatch(activeClients); ExecutionTask task = new ExecutionTask(start, finish, client, list); for(int i = 0; i < activeClients; i++) { executor.execute(task); } finish.await(); logger.sample(); return list; } private List<Report> validate(List<Buffer> list, File configDir, int activeClients) throws Exception { List<Report> reportList = new ArrayList<Report>(); Extractor extractor = new Extractor(debug); int repeat = connection.getRepeat(); int total = list.size(); int pipeline = 0; if(debug) { System.out.println("Validating '"+total+"' responses for '"+activeClients+"'"); } if(total != activeClients) { System.out.println("Received only '"+total+"' responses when expected '"+activeClients+"'"); System.exit(0); } for(Buffer buffer : list) { InputStream stream = buffer.open(); PushbackInputStream pushback = new PushbackInputStream(stream, 1024); List<Report> pipelineList = new ArrayList<Report>(); pipeline++; for(int i = 0; i < repeat; i++) { Result response = extractor.extractResponse(pushback); if(response != null) { if(debug) { System.out.printf("VALIDATE: pipeline %s of %s request %s - %s remaining [%s]%n", pipeline, total, i, response.getStatusLine(), stream.available()); } Buffer nextLine = extractor.extractLine(pushback); String nextStatus = null; if(nextLine != null) { nextStatus = nextLine.encode("ISO-8859-1").trim(); } Report report = new Report(response, nextStatus); try { String unread = nextStatus + "\r\n"; byte[] data = unread.getBytes("ISO-8859-1"); analyser.analyse(response, debug); if(nextStatus != null) { pushback.unread(data); } } catch(Exception e) { e.printStackTrace(); report.addException(e); pipelineList.add(report); dump(pipelineList, configDir, true); System.exit(0); } pipelineList.add(report); } } reportList.addAll(pipelineList); int size = pushback.available(); if(size > 0) { System.out.println("Excess information sent in response of size '"+size+"' after reading '"+pipelineList.size()+"' requests of expected '"+repeat+"' was"); System.out.println(extractor.extractAll(pushback).encode("ISO-8859-1")); dump(pipelineList, configDir, true); System.exit(0); } if(reportList.size() != repeat * pipeline) { System.out.println("Only recieved '"+reportList.size()+"' from a pipeline of '"+(repeat * pipeline)+"'"); dump(pipelineList, configDir, true); System.exit(0); } } System.err.println("Validated '"+activeClients+"' clients and '"+(pipeline * repeat)+"' requests"); return reportList; } private class ExecutionTask implements Runnable { private final List<Buffer> list; private final CountDownLatch start; private final CountDownLatch finish; private final Client client; public ExecutionTask(CountDownLatch start, CountDownLatch finish, Client client, List<Buffer> list) { this.start = start; this.finish = finish; this.client = client; this.list = list; } public void run() { try { start.countDown(); start.await(); execute(); }catch(Exception e) { e.printStackTrace(); System.exit(0); } finally { finish.countDown(); } } private void execute() throws Exception { Buffer result = connection.execute(client); if(result == null) { System.err.println("Returned a null buffer"); System.exit(0); } list.add(result); } } }