org.signserver.test.random.cli.Main.java Source code

Java tutorial

Introduction

Here is the source code for org.signserver.test.random.cli.Main.java

Source

/*************************************************************************
 *                                                                       *
 *  SignServer: The OpenSource Automated Signing Server                  *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.signserver.test.random.cli;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.*;
import org.apache.commons.cli.*;
import org.apache.log4j.Logger;
import org.signserver.common.CryptoTokenOfflineException;
import org.signserver.common.InvalidWorkerIdException;
import org.signserver.common.RequestContext;
import org.signserver.ejb.interfaces.IWorkerSession.IRemote;
import org.signserver.server.UsernamePasswordClientCredential;
import org.signserver.test.random.*;
import org.signserver.test.random.impl.IncrementProperty;
import org.signserver.test.random.impl.IncrementPropertyThread;
import org.signserver.test.random.impl.RenewSigner;
import org.signserver.test.random.impl.RequestContextPreProcessor;
import org.signserver.test.random.impl.SigningThread;

/**
 * Command line interface for random tests.
 *
 * @author Markus Kils
 * @version $Id: Main.java 5610 2015-01-12 09:28:14Z netmackan $
 */
public class Main {

    /** Logger for this class. */
    private static final Logger LOG = Logger.getLogger(IncrementProperty.class);

    private static final String TIME_LIMIT = "timelimit";
    private static final String RANDOM_SEED = "randomseed";
    private static final String WORKER_GROUP_1 = "workergroup1";
    private static final String WORKER_GROUP_2 = "workergroup2";
    private static final String WORKER_GROUP_3 = "workergroup3";
    private static final String TEST_SUITE = "testsuite";
    private static final String THREAD_GROUP_1 = "threadgroup1";
    private static final String THREAD_GROUP_2 = "threadgroup2";
    private static final String USERPREFIX = "userprefix";
    private static final String USERSUFFIXMIN = "usersuffixmin";
    private static final String USERSUFFIXMAX = "usersuffixmax";

    private static final String NL = System.getProperty("line.separator");
    private static final String COMMAND = "randomtest";

    private static int exitCode;
    private static final Options OPTIONS;

    private static void printUsage() {
        StringBuilder footer = new StringBuilder();
        footer.append(NL).append("Sample usages:").append(NL).append("a) ").append(COMMAND).append(
                " -testsuite signWhileUpdatingConfig -workergroup1 5678/xml,5679/tsa,5680/xml -threadgroup1 4 -workergroup2 5677/xml,5678/xml,5679/tsa -threadgroup2 3 -timelimit 30000")
                .append(NL).append("b) ").append(COMMAND)
                .append(" -testsuite signAndCountSignings -workergroup1 5678/xml,5679/tsa,5680/xml -threadgroup1 10 -timelimit 30000")
                .append(NL).append("c) ").append(COMMAND)
                .append(" -testsuite signWhileRenewing -workergroup1 300/xml -workergroup2 301/xml,302/xml -threadgroup1 5 -workergroup3 309/renew -timelimit 20000")
                .append("d) ").append(COMMAND)
                .append(" -testsuite signWithRandomUsers -workergroup1 300/xml -threadgroup1 5 -timelimit 20000 -userprefix testuser -usersuffixmin 1 -usersuffixmax 100")
                .append(NL).append("Available worker types:").append(NL).append(" - workerType can be any of ")
                .append(Arrays.asList(WorkerType.values())).append(NL).append("Test suite: signAndCountSignings")
                .append(NL)
                .append(" - Signs documents with the workers from group 1 with the number of threads defined for group 1")
                .append(NL)
                .append(" - Pauses signings and counts performed signings a compares to the key usage counter value")
                .append(NL).append(" - Notice that it is assumed that all workers use the same key-pair").append(NL)
                .append("Test suite: signWhileUpdatingConfig").append(NL)
                .append(" - Signs documents with the workers from group 1 with the number of threads defined for group 1")
                .append(NL).append(" - Increases a counter in the configuration of group 2").append(NL)
                .append(" - Notice that the size of thread group 2 must be equal to the number of workers in group 2")
                .append(NL).append("Test suite: signWhileRenewing").append(NL)
                .append(" - Signs documents with the workers from group 1 with the number of threads defined for group 1")
                .append(NL).append(" - Renews signers from group 2 using the one renewal worker in group 3")
                .append(NL).append(" - Notice that group 3 should only include one renewal worker").append(NL)
                .append("Test suite: signWithRandomUsers").append(NL)
                .append(" - Signs documents with the workers from group 1 with the number of threads defined for group 1")
                .append(NL)
                .append(" - Sends requests from usernames starting with the supplied 'userprefix' and ends with a random number between 'usersuffixmin' and 'usersuffixmax'")
                .append(NL);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        final HelpFormatter formatter = new HelpFormatter();
        PrintWriter pw = new PrintWriter(bout);
        formatter.printHelp(pw, HelpFormatter.DEFAULT_WIDTH, COMMAND + " <options>", "Random testing tool", OPTIONS,
                HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, footer.toString());
        pw.close();
        LOG.info(bout.toString());
    }

    private enum TestSuites {
        signWhileUpdatingConfig, signAndCountSignings, signWhileRenewing, signWithRandomUsers,
    }

    static {
        OPTIONS = new Options();
        OPTIONS.addOption(TEST_SUITE, true,
                "Test suite to run. Any of " + Arrays.asList(TestSuites.values()) + ".");
        OPTIONS.addOption(TIME_LIMIT, true, "Optional. Only run for the specified time (in milliseconds).");
        OPTIONS.addOption(RANDOM_SEED, true, "Optional. Seed to initialize the pseudo random generator with.");
        OPTIONS.addOption(WORKER_GROUP_1, true,
                "First group of workers. Comma separated list of workerId/workerType.");
        OPTIONS.addOption(WORKER_GROUP_2, true,
                "Second group of workers. Comma separated list of workerId/workerType");
        OPTIONS.addOption(WORKER_GROUP_3, true,
                "Third group of workers. Comma separated list of workerId/workerType");
        OPTIONS.addOption(THREAD_GROUP_1, true, "Number of threads in group 1.");
        OPTIONS.addOption(THREAD_GROUP_2, true, "Number of threads in group 2.");
        OPTIONS.addOption(USERPREFIX, true, "Prefix for usernames.");
        OPTIONS.addOption(USERSUFFIXMIN, true,
                "Lowest suffix for usernames in form of an integer value (inclusive).");
        OPTIONS.addOption(USERSUFFIXMAX, true,
                "Highest suffix for usernames in form of an integer value (inclusive).");
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws RemoteException, InvalidWorkerIdException {
        try {
            if (LOG.isDebugEnabled()) {
                LOG.debug("(Debug logging is enabled)");
            }

            final CommandLine commandLine = new GnuParser().parse(OPTIONS, args);

            // Test suite
            final TestSuites ts;
            if (commandLine.hasOption(TEST_SUITE)) {
                ts = TestSuites.valueOf(commandLine.getOptionValue(TEST_SUITE));
            } else {
                throw new ParseException("Missing option: -" + TEST_SUITE);
            }

            // Time limit
            final long limitedTime;
            if (commandLine.hasOption(TIME_LIMIT)) {
                limitedTime = Long.parseLong(commandLine.getOptionValue(TIME_LIMIT));
            } else {
                limitedTime = -1;
            }

            // Random seed
            final long seed;
            if (commandLine.hasOption(RANDOM_SEED)) {
                seed = Long.parseLong(commandLine.getOptionValue(RANDOM_SEED));
            } else {
                seed = new Random().nextLong();
            }
            final Random masterRandom = new Random(seed);

            // Worker group 1
            final List<WorkerSpec> workerGroup1;
            if (commandLine.hasOption(WORKER_GROUP_1)) {
                workerGroup1 = new LinkedList<WorkerSpec>();
                final String list = commandLine.getOptionValue(WORKER_GROUP_1);
                String[] ids = list.split(",");
                for (String id : ids) {
                    workerGroup1.add(WorkerSpec.fromString(id.trim()));
                }
            } else {
                workerGroup1 = null;
            }

            // Worker group 2
            final List<WorkerSpec> workerGroup2;
            if (commandLine.hasOption(WORKER_GROUP_2)) {
                workerGroup2 = new LinkedList<WorkerSpec>();
                final String list = commandLine.getOptionValue(WORKER_GROUP_2);
                String[] ids = list.split(",");
                for (String id : ids) {
                    workerGroup2.add(WorkerSpec.fromString(id.trim()));
                }
            } else {
                workerGroup2 = null;
            }

            // Worker group 3
            final List<WorkerSpec> workerGroup3;
            if (commandLine.hasOption(WORKER_GROUP_3)) {
                workerGroup3 = new LinkedList<WorkerSpec>();
                final String list = commandLine.getOptionValue(WORKER_GROUP_3);
                String[] ids = list.split(",");
                for (String id : ids) {
                    workerGroup3.add(WorkerSpec.fromString(id.trim()));
                }
            } else {
                workerGroup3 = null;
            }

            // Thread group 1
            final Integer threadGroup1;
            if (commandLine.hasOption(THREAD_GROUP_1)) {
                threadGroup1 = Integer.parseInt(commandLine.getOptionValue(THREAD_GROUP_1));
            } else {
                threadGroup1 = null;
            }

            // Thread group 2
            final Integer threadGroup2;
            if (commandLine.hasOption(THREAD_GROUP_2)) {
                threadGroup2 = Integer.parseInt(commandLine.getOptionValue(THREAD_GROUP_2));
            } else {
                threadGroup2 = null;
            }

            final AdminCommandHelper helper = new AdminCommandHelper();
            final IRemote workerSession = helper.getWorkerSession();
            final LinkedList<WorkerThread> threads = new LinkedList<WorkerThread>();
            final FailureCallback callback = new FailureCallback() {

                @Override
                public void failed(WorkerThread thread, String message) {
                    // Stop
                    for (WorkerThread w : threads) {
                        w.stopIt();
                    }

                    // Wait until all stoped
                    try {
                        for (WorkerThread w : threads) {
                            w.join(1000);
                        }
                    } catch (InterruptedException ex) {
                        LOG.error("Interrupted: " + ex.getMessage());
                    }

                    // Print message
                    LOG.error(thread + ": " + message);
                    exitCode = -1;
                }
            };
            Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() {

                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    LOG.error("Uncaught exception from t", e);
                    callback.failed((WorkerThread) t, "Uncaught exception: " + e.getMessage());
                }
            };

            // Init context
            final TestContext context = new TestContext();
            context.setCallback(callback);
            context.setMasterRandom(masterRandom);
            context.setWorkerSession(workerSession);
            context.setPauser(new Pauser());
            context.setWorkerGroup1(workerGroup1);
            context.setThreadsGroup1(threadGroup1);
            context.setWorkerGroup2(workerGroup2);
            context.setThreadsGroup2(threadGroup2);
            context.setWorkerGroup3(workerGroup3);

            // Output information
            final StringBuilder buff = new StringBuilder();
            buff.append("SignServer Random Test\n").append("----------------------\n")
                    .append("Random seed:          ").append(seed).append("\n").append("Time limit:           ")
                    .append(limitedTime < 0 ? "unlimited" : limitedTime).append(" ms").append("\n")
                    .append("Test suite:           ").append(ts).append("\n").append("Worker group 1:       ")
                    .append(context.getWorkerGroup1()).append("\n").append("Worker group 2:       ")
                    .append(context.getWorkerGroup2()).append("\n").append("Worker group 3:       ")
                    .append(context.getWorkerGroup3()).append("\n").append("Thread group 1:       ")
                    .append(context.getThreadsGroup1() == null ? "unspecified" : context.getThreadsGroup1())
                    .append("\n").append("Thread group 2:       ")
                    .append(context.getThreadsGroup2() == null ? "unspecified" : context.getThreadsGroup2())
                    .append("\n");
            LOG.info(buff.toString());

            try {
                // First check all workers
                if (context.getWorkerGroup1() == null) {
                    if (context.getThreadsGroup1() == null) {
                        throw new ParseException("Missing -workergroup1");
                    }
                }
                Collection<WorkerSpec> allWorkers = new LinkedList<WorkerSpec>(context.getWorkerGroup1());
                if (context.getWorkerGroup2() != null) {
                    allWorkers.addAll(context.getWorkerGroup2());
                }
                if (context.getWorkerGroup3() != null) {
                    allWorkers.addAll(context.getWorkerGroup3());
                }
                for (WorkerSpec worker : allWorkers) {
                    List<String> fatalErrors = context.getWorkerSession().getStatus(worker.getWorkerId())
                            .getFatalErrors();
                    if (!fatalErrors.isEmpty()) {
                        System.err.println();
                        throw new FailedException("Worker " + worker + ": " + fatalErrors);
                    }
                }

                // Init test suite
                switch (ts) {
                case signWhileUpdatingConfig:
                    signWhileUpdatingConfig(threads, context);
                    break;
                case signAndCountSignings:
                    signAndCountSignings(threads, context);
                    break;
                case signWhileRenewing:
                    signWhileRenewing(threads, context);
                    break;
                case signWithRandomUsers: {
                    final String userPrefix = commandLine.getOptionValue(USERPREFIX, null);
                    if (userPrefix == null) {
                        throw new ParseException("Missing required option: " + USERPREFIX);
                    }
                    final String userSuffixMin = commandLine.getOptionValue(USERSUFFIXMIN, null);
                    if (userSuffixMin == null) {
                        throw new ParseException("Missing required option: " + USERSUFFIXMIN);
                    }
                    final String userSuffixMax = commandLine.getOptionValue(USERSUFFIXMAX, null);
                    if (userSuffixMax == null) {
                        throw new ParseException("Missing required option: " + USERSUFFIXMAX);
                    }
                    signWithRandomUsers(threads, context, userPrefix, Integer.parseInt(userSuffixMin),
                            Integer.parseInt(userSuffixMax));
                    break;
                }
                default:
                    throw new Exception("Unsupported test suite: " + ts);
                }

                // Wait 1 second to start
                Thread.sleep(1000);

                // Start all threads
                for (WorkerThread w : threads) {
                    w.setUncaughtExceptionHandler(handler);
                    w.start();
                }

                // If time limited
                if (limitedTime > 0) {
                    try {
                        Thread.sleep(limitedTime);
                    } catch (InterruptedException ex) {
                        LOG.error("Interrupted: " + ex.getMessage());
                    }
                    // Stop all threads
                    for (WorkerThread w : threads) {
                        w.stopIt();
                    }
                }

                // Wait until all stopped
                try {
                    for (WorkerThread w : threads) {
                        w.join();
                        LOG.info(w + ": Operations performed: " + w.getOperationsPerformed());
                    }
                } catch (InterruptedException ex) {
                    LOG.error("Interrupted: " + ex.getMessage());
                }
            } catch (Exception ex) {
                LOG.error("Failed: " + ex.getMessage(), ex);
                exitCode = -1;
            }
            System.exit(exitCode);
        } catch (ParseException ex) {
            LOG.error("Parse error: " + ex.getMessage());
            printUsage();
            System.exit(-2);
        }
    }

    /**
    * Creates one group of signing threads and one "pauser" thread. The signing threads
    * signs documents and holds a counter for how many signings they have performed.
    * The pauser thread pauses the other threads every 5 seconds and checks and
    * summarizes all performed signings and compares with the key usage counter 
    * value in the database.
    * 
    * Notice: It is assumed that all signers use the same key.
    * 
    * The goals with the test are:
    * a) Test that no updates to key usage counter is missed
    * 
    */
    private static void signAndCountSignings(final List<WorkerThread> threads, final TestContext context)
            throws Exception {
        final LinkedList<SigningThread> signingThreads = new LinkedList<SigningThread>();

        if (context.getThreadsGroup1() == null) {
            throw new ParseException("Missing -threadgroup1");
        }

        // Threads signing documents
        // Group 1: Threads signing documents with the workers in group 1
        for (int i = 0; i < context.getThreadsGroup1(); i++) {
            final WorkerSpec worker = context.getWorkerGroup1().get(i % context.getWorkerGroup1().size());
            final SigningThread signingThread = new SigningThread("Signer-" + i + "-" + worker.getWorkerId(),
                    context.getCallback(), context.getPauser(), context.getMasterRandom().nextLong(), worker,
                    context.getWorkerSession(), null);
            signingThreads.add(signingThread);
        }
        threads.addAll(signingThreads);

        final int workerId = context.getWorkerGroup1().get(0).getWorkerId();

        final long startValue = context.getWorkerSession().getKeyUsageCounterValue(workerId);

        // Thread pausing signings
        WorkerThread pauseThread = new WorkerThread("Pause", context.getCallback()) {
            @Override
            public void run() {
                final Pauser pauser = context.getPauser();
                try {
                    while (!isStop()) {
                        try {
                            Thread.sleep(5000);
                            pauser.startPause();
                            Thread.sleep(5000);

                            long signings = 0;
                            for (SigningThread w : signingThreads) {
                                signings += w.getOperationsPerformed();
                            }
                            long expected = startValue + signings;
                            long actual = context.getWorkerSession().getKeyUsageCounterValue(workerId);
                            if (expected != actual) {
                                fireFailure("Key usage counter value incorrect. Expected " + expected + ", actual "
                                        + actual);
                                break;
                            }
                            increaseOperationsPerformed();
                        } finally {
                            pauser.stopPause();
                        }
                    }
                } catch (CryptoTokenOfflineException ex) {
                    fireFailure("Worker offline: " + ex.getMessage());
                } catch (InterruptedException ex) {
                    LOG.error("Interrupted: " + ex.getMessage());
                }
            }
        };
        threads.add(pauseThread);
    }

    /**
     * Creates two groups of threads, one performing signings and the other updating
     * the worker configurations.
     * 
     * There can be any number of workers and threads in group 1.
     * In group 2 the number of threads should be equal to the number of workers 
     * so that no two threads update the configuration of the same worker on the 
     * same time.
     * 
     * The goals with the test are:
     * a) Test that it is possible to sign at the same time as the signers configuration is updated.
     * b) Test that updating the configuration of different workers at the same time does not infer with each other
     * c) No configuration update is missed
     * 
     * The test reads a counter value from a worker property, increases it with one
     * and updates the configuration with the new value. Every 10th iteration 
     * the value is read from the configuration and checked that it equals the
     * expected value.
     * 
     */
    private static void signWhileUpdatingConfig(final List<WorkerThread> threads, final TestContext context)
            throws Exception {

        if (context.getThreadsGroup1() == null) {
            throw new ParseException("Missing -threadgroup1");
        }

        // As updating a counter in the worker configuration can not be done automically
        // we only allow one thread to update each worker otherwise the tests will fail
        if (context.getThreadsGroup2() == null) {
            context.setThreadsGroup2(context.getWorkerGroup2().size());
        } else if (context.getThreadsGroup2() != context.getWorkerGroup2().size()) {
            throw new ParseException("Size of thread group 2 must be equal to size of worker group 2");
        }

        // Group 1: Threads signing documents with the workers in group 1
        for (int i = 0; i < context.getThreadsGroup1(); i++) {
            final WorkerSpec worker = context.getWorkerGroup1().get(i % context.getWorkerGroup1().size());
            final SigningThread signingThread = new SigningThread("Signer-" + i + "-" + worker.getWorkerId(),
                    context.getCallback(), null, context.getMasterRandom().nextLong(), worker,
                    context.getWorkerSession(), null);
            threads.add(signingThread);
        }

        // Group 2: Threads updating the configuration of the workers in group 2
        for (int i = 0; i < context.getThreadsGroup2(); i++) {
            final int workerId = context.getWorkerGroup2().get(i % context.getWorkerGroup2().size()).getWorkerId();
            final IncrementPropertyThread incr = new IncrementPropertyThread("ConfUpdater-" + i + "-" + workerId,
                    context.getCallback(), context.getMasterRandom().nextLong(), workerId, "PROPERTY",
                    context.getWorkerSession());
            threads.add(incr);
        }
    }

    /**
     * Creates two groups of threads, one performing signings and the other 
     * renewing the signers.
     * 
     * There can be any number of workers and threads in group 1 performing signings.
     * 
     * Group 2 is the workers that will be renewed by the renewalworker in group 3.
     * 
     * The goals with the test are:
     * a) Test that it is possible to sign at the same time as the signers are renewed.
     *
     * - Notice that for instance a FirstActiveDispatcher might have to be used 
     *   in order to be able to sign while some of the workers are being renewed
     *   as there is a time for which the worker will be offline during renewal.
     */
    private static void signWhileRenewing(final List<WorkerThread> threads, final TestContext context)
            throws Exception {

        if (context.getThreadsGroup1() == null) {
            throw new ParseException("Missing -threadgroup1");
        }

        if (context.getWorkerGroup3() == null) {
            throw new ParseException("Missing -workergroup3");
        }

        if (context.getWorkerGroup3().size() != 1) {
            throw new ParseException("Only one (renewal) worker allowed (worker group 3)");
        }

        if (!context.getWorkerGroup3().get(0).getWorkerType().equals(WorkerType.renew)) {
            throw new ParseException("The worker in group 3 must be of type " + WorkerType.renew + ".");
        }

        // Group 1: Threads signing documents with the workers in group 1
        for (int i = 0; i < context.getThreadsGroup1(); i++) {
            final WorkerSpec worker = context.getWorkerGroup1().get(i % context.getWorkerGroup1().size());
            final SigningThread signingThread = new SigningThread("Signer-" + i + "-" + worker.getWorkerId(),
                    context.getCallback(), null, context.getMasterRandom().nextLong(), worker,
                    context.getWorkerSession(), null);
            threads.add(signingThread);
        }

        // Group 3: Threads updating the configuration of the workers in group 2
        final int workerId = context.getWorkerGroup3().get(0).getWorkerId();
        final List<WorkerSpec> renewees = context.getWorkerGroup2();
        final List<RenewSigner> renewers = new LinkedList<RenewSigner>();
        for (WorkerSpec renewee : renewees) {
            renewers.add(new RenewSigner(workerId, renewee.getWorkerId(), context.getWorkerSession()));
        }

        final long seed = context.getMasterRandom().nextLong();
        final WorkerThread renewals = new WorkerThread("Renewal-" + workerId, context.getCallback()) {

            @Override
            public void run() {
                final Pauser pauser = context.getPauser();
                final Random random = new Random(seed);
                try {
                    while (!isStop()) {
                        for (final RenewSigner renewer : renewers) {
                            if (pauser != null) {
                                pauser.pause();
                            }
                            try {
                                renewer.run();
                            } catch (FailedException ex) {
                                fireFailure("WORKER" + workerId + " renewing of " + renewer.getReneweeId()
                                        + " failed after " + getOperationsPerformed() + " signings: "
                                        + ex.getMessage());
                                break;
                            }
                            // Sleep
                            Thread.sleep((int) (random.nextDouble() * 500.0));
                            increaseOperationsPerformed();
                        }
                    }
                } catch (InterruptedException ex) {
                    LOG.error("Interrupted: " + ex.getMessage());
                }
            }

        };
        threads.add(renewals);
    }

    /**
     * Creates one group of threads performing signings and sends the request
     * with usernames as specified.
     * @param threads to run
     * @param context for the tests
     * @param userPrefix Prefix for the username
     * @param userSuffixMin Minimum username number
     * @param userSuffixMax Maximum username number
     * @throws Exception in case of errors
     */
    private static void signWithRandomUsers(final List<WorkerThread> threads, final TestContext context,
            final String userPrefix, final int userSuffixMin, final int userSuffixMax) throws Exception {

        if (context.getThreadsGroup1() == null) {
            throw new ParseException("Missing -threadgroup1");
        }

        // Group 1: Threads signing documents with the workers in group 1
        for (int i = 0; i < context.getThreadsGroup1(); i++) {
            final WorkerSpec worker = context.getWorkerGroup1().get(i % context.getWorkerGroup1().size());
            final SigningThread signingThread = new SigningThread("Signer-" + i + "-" + worker.getWorkerId(),
                    context.getCallback(), null, context.getMasterRandom().nextLong(), worker,
                    context.getWorkerSession(), new RequestContextPreProcessor() {

                        @Override
                        public void preProcess(RequestContext requestContext) {
                            final String username = userPrefix + (userSuffixMin
                                    + context.getMasterRandom().nextInt(userSuffixMax - userSuffixMin + 1));
                            requestContext.put(RequestContext.CLIENT_CREDENTIAL,
                                    new UsernamePasswordClientCredential(username, ""));
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Username: " + username);
                            }
                        }
                    });
            threads.add(signingThread);
        }
    }

}