13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/* 23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc. 33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License. 63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at 73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software 113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and 143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License. 153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp.benchmarks; 173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.google.caliper.Param; 193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.google.caliper.model.ArbitraryMeasurement; 203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.google.caliper.runner.CaliperMain; 2171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport com.squareup.okhttp.HttpUrl; 223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Protocol; 233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.SslContextBuilder; 243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.Dispatcher; 253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.MockResponse; 263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.MockWebServer; 273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.mockwebserver.RecordedRequest; 283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException; 293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList; 303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Arrays; 313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List; 323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Random; 333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.TimeUnit; 343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.logging.Level; 353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.logging.Logger; 363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLContext; 37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.GzipSink; 393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/** 413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * This benchmark is fake, but may be useful for certain relative comparisons. 423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * It uses a local connection to a MockWebServer to measure how many identical 433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * requests per second can be carried over a fixed number of threads. 443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic class Benchmark extends com.google.caliper.Benchmark { 463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int NUM_REPORTS = 10; 473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final boolean VERBOSE = false; 483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final Random random = new Random(0); 503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Which client to run.*/ 523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param 533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Client client; 543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** How many concurrent requests to execute. */ 563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param({ "1", "10" }) 573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int concurrencyLevel; 583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** How many requests to enqueue to await threads to execute them. */ 603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param({ "10" }) 613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int targetBacklog; 623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** True to use TLS. */ 643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // TODO: compare different ciphers? 653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param 663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean tls; 673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** True to use gzip content-encoding for the response body. */ 693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param 703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean gzip; 713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Don't combine chunked with SPDY_3 or HTTP_2; that's not allowed. */ 733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param 743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean chunked; 753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** The size of the HTTP response body, in uncompressed bytes. */ 773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param({ "128", "1048576" }) 783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int bodyByteCount; 793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** How many additional headers were included, beyond the built-in ones. */ 813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Param({ "0", "20" }) 823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int headerCount; 833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Which ALPN protocols are in use. Only useful with TLS. */ 85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller List<Protocol> protocols = Arrays.asList(Protocol.HTTP_1_1); 863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public static void main(String[] args) { 88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller List<String> allArgs = new ArrayList<>(); 893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller allArgs.add("--instrument"); 903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller allArgs.add("arbitrary"); 913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller allArgs.addAll(Arrays.asList(args)); 923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller CaliperMain.main(Benchmark.class, allArgs.toArray(new String[allArgs.size()])); 943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @ArbitraryMeasurement(description = "requests per second") 973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public double run() throws Exception { 983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (VERBOSE) System.out.println(toString()); 993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller HttpClient httpClient = client.create(); 1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Prepare the client & server 1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller httpClient.prepare(this); 1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockWebServer server = startServer(); 10471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller HttpUrl url = server.url("/"); 1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int requestCount = 0; 1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long reportStart = System.nanoTime(); 1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long reportPeriod = TimeUnit.SECONDS.toNanos(1); 1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int reports = 0; 1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller double best = 0.0; 1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Run until we've printed enough reports. 1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller while (reports < NUM_REPORTS) { 1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Print a report if we haven't recently. 1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long now = System.nanoTime(); 1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller double reportDuration = now - reportStart; 1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (reportDuration > reportPeriod) { 1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller double requestsPerSecond = requestCount / reportDuration * TimeUnit.SECONDS.toNanos(1); 1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (VERBOSE) { 1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller System.out.println(String.format("Requests per second: %.1f", requestsPerSecond)); 1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller best = Math.max(best, requestsPerSecond); 1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestCount = 0; 1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller reportStart = now; 1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller reports++; 1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Fill the job queue with work. 1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller while (httpClient.acceptingJobs()) { 1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller httpClient.enqueue(url); 1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestCount++; 1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // The job queue is full. Take a break. 1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sleep(1); 1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return best; 1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public String toString() { 142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller List<Object> modifiers = new ArrayList<>(); 1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (tls) modifiers.add("tls"); 1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (gzip) modifiers.add("gzip"); 1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (chunked) modifiers.add("chunked"); 1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller modifiers.addAll(protocols); 1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return String.format("%s %s\nbodyByteCount=%s headerCount=%s concurrencyLevel=%s", 1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller client, modifiers, bodyByteCount, headerCount, concurrencyLevel); 1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void sleep(int millis) { 1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Thread.sleep(millis); 1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (InterruptedException ignored) { 1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private MockWebServer startServer() throws IOException { 1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Logger.getLogger(MockWebServer.class.getName()).setLevel(Level.WARNING); 1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockWebServer server = new MockWebServer(); 1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (tls) { 1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SSLContext sslContext = SslContextBuilder.localhost(); 1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller server.useHttps(sslContext.getSocketFactory(), false); 166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller server.setProtocols(protocols); 1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final MockResponse response = newResponse(); 1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller server.setDispatcher(new Dispatcher() { 1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public MockResponse dispatch(RecordedRequest request) { 1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return response; 1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }); 1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller server.start(); 1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return server; 1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private MockResponse newResponse() throws IOException { 181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller byte[] bytes = new byte[bodyByteCount]; 182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller random.nextBytes(bytes); 183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Buffer body = new Buffer().write(bytes); 1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockResponse result = new MockResponse(); 1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (gzip) { 188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Buffer gzipBody = new Buffer(); 189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller GzipSink gzipSink = new GzipSink(gzipBody); 190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller gzipSink.write(body, body.size()); 191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller gzipSink.close(); 192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller body = gzipBody; 1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.addHeader("Content-Encoding: gzip"); 1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (chunked) { 1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.setChunkedBody(body, 1024); 1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else { 1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.setBody(body); 2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < headerCount; i++) { 2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.addHeader(randomString(12), randomString(20)); 2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return result; 2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private String randomString(int length) { 2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String alphabet = "-abcdefghijklmnopqrstuvwxyz"; 2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller char[] result = new char[length]; 2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < length; i++) { 2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result[i] = alphabet.charAt(random.nextInt(alphabet.length())); 2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return new String(result); 2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller} 218