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