/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.jobs;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import java.util.StringTokenizer;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.util.FixedRateBarrier;
import com.unboundid.util.ValuePattern;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
import com.slamd.job.JobClass;
import com.slamd.job.UnableToRunException;
import com.slamd.parameter.BooleanParameter;
import com.slamd.parameter.FileURLParameter;
import com.slamd.parameter.IntegerParameter;
import com.slamd.parameter.InvalidValueException;
import com.slamd.parameter.MultiLineTextParameter;
import com.slamd.parameter.Parameter;
import com.slamd.parameter.ParameterList;
import com.slamd.parameter.PasswordParameter;
import com.slamd.parameter.PlaceholderParameter;
import com.slamd.parameter.StringParameter;
import com.slamd.stat.CategoricalTracker;
import com.slamd.stat.IncrementalTracker;
import com.slamd.stat.RealTimeStatReporter;
import com.slamd.stat.StatTracker;
import com.slamd.stat.TimeTracker;
/**
* This class defines a SLAMD job that simulates the load that some versions of
* Netegrity SiteMinder have been observed to place on an LDAP directory server.
* The load that it uses is based on the following (rather inefficient) sequence
* of events:
*
* <OL>
* <LI>Perform a subtree search from the directory suffix to find the user's
* entry based on a login ID.</LI>
* <LI>Perform a base-level search on the user's entry to retrieve the
* objectClass attribute.</LI>
* <LI>Perform a bind as the user. This is done on a different connection
* than all of the other steps.</LI>
* <LI>Perform a base-level search on the user's entry to retrieve a given
* user-specified attribute (attr1).</LI>
* <LI>Perform a base-level search on the user's entry to retrieve a second
* user-specified attribute (attr2).</LI>
* <LI>Perform a base-level search on the user's entry to retrieve the first
* attribute (attr1).</LI>
* <LI>Perform a modification on the user's entry.</LI>
* <LI>Perform a base-level search on the user's entry to retrieve the first
* attribute again.</LI>
* <LI>Perform a base-level search on the user's entry to retrieve the first
* attribute again.</LI>
* <LI>Perform a base-level search on the user's entry to retrieve a third
* attribute (attr3).</LI>
* </OL>
*
*
* @author Neil A. Wilson
*/
public class SiteMinderJobClass
extends JobClass
{
/**
* The set of characters that will be used to generate random values for the
* modifications.
*/
public static final char[] ALPHABET =
"abcdefghijklmnopqrstuvwxyz".toCharArray();
/**
* The default value for the first attribute to retrieve during the
* authentication process.
*/
public static final String DEFAULT_ATTR1 = "givenName";
/**
* The default value for the second attribute to retrieve during the
* authentication process.
*/
public static final String DEFAULT_ATTR2 = "sn";
/**
* The default value for the third attribute to retrieve during the
* authentication process.
*/
public static final String DEFAULT_ATTR3= "cn";
/**
* The default attribute used as the login ID.
*/
public static final String DEFAULT_LOG_ID_ATTR = "uid";
/**
* The name of the stat tracker that will be used to count the number of
* authentication attempts.
*/
public static final String STAT_TRACKER_AUTHENTICATION_ATTEMPTS =
"Authentication Attempts";
/**
* The name of the stat tracker that will be used to keep track of the time
* required to perform each authentication.
*/
public static final String STAT_TRACKER_AUTHENTICATION_TIME =
"Authentication Time (ms)";
/**
* The name of the stat tracker that will be used to keep track of the time
* required to perform each bind.
*/
public static final String STAT_TRACKER_BIND_TIME = "Bind Time (ms)";
/**
* The name of the stat tracker that will be used to count the number of
* failed authentications.
*/
public static final String STAT_TRACKER_FAILED_AUTHENTICATIONS =
"Failed Authentications";
/**
* The name of the stat tracker that will be used to categorize the reasons
* for the failed auths.
*/
public static final String STAT_TRACKER_FAIL_REASON = "Failure Reason";
/**
* The name of the stat tracker that will be used to keep track of the time
* required to perform the initial search (to find the user's entry).
*/
public static final String STAT_TRACKER_INITIAL_SEARCH_TIME =
"Initial Search Time (ms)";
/**
* The name of the stat tracker that will be used to keep track of the time
* required to perform each modification.
*/
public static final String STAT_TRACKER_MOD_TIME = "Modify Time (ms)";
/**
* The name of the stat tracker that will be used to keep track of the number
* of bind operations performed.
*/
public static final String STAT_TRACKER_NUM_BINDS =
"Bind Operations Performed";
/**
* The name of the stat tracker that will be used to keep track of the number
* of modify operations performed.
*/
public static final String STAT_TRACKER_NUM_MODS =
"Modify Operations Performed";
/**
* The name of the stat tracker that will be used to keep track of the number
* of search operations performed.
*/
public static final String STAT_TRACKER_NUM_SEARCH =
"Search Operations Performed";
/**
* The name of the stat tracker that will be used to keep track of the time
* required to perform subsequent searches (to retrieve specific attributes
* from the user's entry).
*/
public static final String STAT_TRACKER_SUBSEQUENT_SEARCH_TIME =
"Subsequent Search Time (ms)";
/**
* The name of the stat tracker that will be used to count the number of
* successful authentications.
*/
public static final String STAT_TRACKER_SUCCESSFUL_AUTHENTICATIONS =
"Successful Authentications";
/**
* The default set of attributes to include in the modification.
*/
public static final String[] DEFAULT_ATTRS_TO_MODIFY = new String[]
{
"description"
};
// Indicates whether bind failures because of invalid credentials will be
// ignored (so you don't actually have to know user passwords).
private static boolean ignoreInvalidCredentials;
// Indicates whether the bind should be attempted or skipped.
private static boolean skipBind;
// Indicates whether login IDs/passwords are read from a data file or from
// parameters.
private static boolean useDataFile;
// Indicates whether all threads will used a shared set of connections or if
// each thread will have its own connection.
private static boolean useSharedConnections;
// Indicates whether to use SSL when communicating with the directory.
private static boolean useSSL;
// The search filter to use when searching on the first attribute.
private static Filter filter1;
// The search filter to use when searching on the second attribute.
private static Filter filter2;
// The search filter to use when searching on the third attribute.
private static Filter filter3;
// The rate limiter for this job.
private static FixedRateBarrier rateLimiter;
// The time to keep working after stopping statistics collection.
private static int coolDownTime;
// The port number of the directory server.
private static int directoryPort;
// The maximum length of time that any single LDAP operation will be allowed
// to take before it is cancelled.
private static int timeLimit;
// The time to start working before beginning statistics collection.
private static int warmUpTime;
// The delay in milliseconds between authentication attempts.
private static long delay;
// The LDAP connection that will be used for shared authentication operations.
private static LDAPConnection sharedAuthConnection;
// The LDAP connection that will be used for shared bind operations.
private static LDAPConnection sharedBindConnection;
// The random number generator that will seed the thread-specific random
// number generators.
private static Random parentRandom;
// The DN to use to bind to the directory when performing the search and
// modify operations.
private static String bindDN;
// The password for the bind DN.
private static String bindPW;
// The address of the directory server.
private static String directoryHost;
// The name of the attribute that will be used to initially find the user's
// entry (the login ID attribute).
private static String loginIDAttr;
// The password to use when authenticating.
private static String loginPassword;
// The name of the first attribute to retrieve.
private static String searchAttr1;
// The name of the second attribute to retrieve.
private static String searchAttr2;
// The name of the third attribute to retrieve.
private static String searchAttr3;
// The DN to use as the search base when trying to find user entries in the
// directory.
private static String searchBase;
// The set of login IDs that will be used to authenticate.
private static String[] loginIDs;
// The set of passwords associated with the login IDs.
private static String[] loginPasswords;
// The names of the attributes to alter in the modification.
private static String[] modAttrs;
// The set of attributes to return when retrieving the first attribute.
private static String[] returnAttrs1;
// The set of attributes to return when retrieving the second attribute.
private static String[] returnAttrs2;
// The set of attributes to return when retrieving the third attribute.
private static String[] returnAttrs3;
// The set of attributes to return when retrieving the set of object classes.
private static String[] returnAttrsOC;
// The value pattern to use to construct the login ID.
private static ValuePattern loginIDPattern;
// The parameter that indicates whether the client should trust any SSL cert.
private BooleanParameter blindTrustParameter =
new BooleanParameter("blind_trust", "Blindly Trust Any Certificate",
"Indicates whether the client should blindly trust " +
"any certificate presented by the server, or " +
"whether the key and trust stores should be used.",
true);
// The parameter used to indicate whether invalid credential results are
// ignored.
private BooleanParameter ignoreInvCredParameter =
new BooleanParameter("ignore_49", "Ignore Invalid Credentials Errors",
"Indicates whether bind failures because of " +
"invalid credentials (err=49). This makes it " +
"possible to use this job without actually " +
"know user passwords.", false);
// The parameter used to indicate whether connections are shared.
private BooleanParameter shareConnsParameter =
new BooleanParameter("share_conns", "Share Connections between Threads",
"Indicates whether the connections to the " +
"directory server will be shared between threads " +
"or if each client thread will have its own " +
"connections.", true);
// The parameter used to indicate whether to skip the bind operation.
private BooleanParameter skipBindParameter =
new BooleanParameter("skip_bind", "Skip Bind Operation",
"Indicates whether the bind attempt should be " +
"skipped as part of the authentication process.",
false);
// The parameter that indicates whether the connection should use SSL or not
private BooleanParameter useSSLParameter =
new BooleanParameter("usessl", "Use SSL",
"Indicates whether to use SSL to encrypt the " +
"communication with the directory server", false);
// The parameter used to indicate the login ID/password file.
private FileURLParameter loginDataFileParameter =
new FileURLParameter("login_data_file", "Login Data File URL",
"The URL (FILE or HTTP) of the file containing " +
"the login IDs and passwords to use for the " +
"authentication.", null, false);
// The parameter that specifies the cool-down time in seconds.
private IntegerParameter coolDownParameter =
new IntegerParameter("cool_down", "Cool Down Time",
"The time in seconds that the job should " +
"continue searching after ending statistics " +
"collection.", true, 0, true, 0, false, 0);
// The parameter that indicates the delay that should be used between each
// authentication attempt.
private IntegerParameter delayParameter =
new IntegerParameter("delay", "Time Between Authentications (ms)",
"Specifies the length of time in milliseconds " +
"each thread should wait between authentication " +
"attempts. Note that this delay will be " +
"between the starts of consecutive attempts and " +
"not between the end of one attempt and the " +
"beginning of the next. If an authentication " +
"takes longer than this length of time, then " +
"there will be no delay.", true, 0, true, 0, false,
0);
// The parameter that specifies the maximum request rate.
private IntegerParameter maxRateParameter = new IntegerParameter("maxRate",
"Max Authentication Rate (Auths/Second/Client)",
"Specifies the maximum authentication rate (in auths per second per " +
"client) to attempt to maintain. If multiple clients are used, " +
"then each client will attempt to maintain this rate. A value " +
"less than or equal to zero indicates that the client should " +
"attempt to perform authentications as quickly as possible.",
true, -1);
// The parameter used to indicate the port number for the directory server.
private IntegerParameter portParameter =
new IntegerParameter("ldap_port", "Directory Server Port",
"The port number for the directory server.", true,
389, true, 1, true, 65535);
// The parameter that specifies the interval over which to enforce the maximum
// request rate.
private IntegerParameter rateLimitDurationParameter = new IntegerParameter(
"maxRateDuration", "Max Rate Enforcement Interval (Seconds)",
"Specifies the duration in seconds of the interval over which to " +
"attempt to maintain the configured maximum rate. A value of " +
"zero indicates that it should be equal to the statistics " +
"collection interval. Large values may allow more variation but " +
"may be more accurate over time. Small values can better " +
"ensure that the rate doesn't exceed the requested level but may " +
"be less able to achieve the desired rate.",
true, 0, true,0, false, 0);
// The parameter used to indicate the maximum length of time that any single
// LDAP operation will be allowed to take.
private IntegerParameter timeLimitParameter =
new IntegerParameter("time_limit", "Operation Time Limit",
"The maximum length of time in seconds that any " +
"single LDAP operation will be allowed to take " +
"before it is cancelled.", true, 0, true, 0, false,
0);
// The parameter that specifies the cool-down time in seconds.
private IntegerParameter warmUpParameter =
new IntegerParameter("warm_up", "Warm Up Time",
"The time in seconds that the job should " +
"search before beginning statistics collection.",
true, 0, true, 0, false, 0);
// The parameter used to indicate the attributes to modify.
private MultiLineTextParameter modAttrsParameter =
new MultiLineTextParameter("mod_attrs", "Attributes to Modify",
"The set of attributes to modify.",
DEFAULT_ATTRS_TO_MODIFY, false);
// The parameter used to indicate the password for the bind DN.
private PasswordParameter bindPWParameter =
new PasswordParameter("bindpw", "Directory Bind Password",
"The password to use when binding to the " +
"directory server to perform search and modify " +
"operations.", false, "");
// The parameter that specifies the password for the SSL key store
private PasswordParameter keyPWParameter =
new PasswordParameter("sslkeypw", "SSL Key Store Password",
"The password for the JSSE key store", false, "");
// The parameter used to indicate the password to use when authenticating to
// the directory.
private PasswordParameter loginPasswordParameter =
new PasswordParameter("login_id_pw", "Login Password",
"The password to use when authenticating to the " +
"directory for user authentications.", false, "");
// The parameter that specifies the password for the SSL key store
private PasswordParameter trustPWParameter =
new PasswordParameter("ssltrustpw", "SSL Trust Store Password",
"The password for the JSSE trust store", false, "");
// The placeholder parameter used as a spacer in the admin interface.
private PlaceholderParameter placeholder = new PlaceholderParameter();
// The parameter used to indicate the first attribute to retrieve.
private StringParameter attr1Parameter =
new StringParameter("attr1", "First Attribute to Retrieve",
"The first attribute to retrieve from the user's " +
"entry as part of the authentication process.",
true, DEFAULT_ATTR1);
// The parameter used to indicate the first attribute to retrieve.
private StringParameter attr2Parameter =
new StringParameter("attr2", "Second Attribute to Retrieve",
"The second attribute to retrieve from the user's " +
"entry as part of the authentication process.",
true, DEFAULT_ATTR2);
// The parameter used to indicate the first attribute to retrieve.
private StringParameter attr3Parameter =
new StringParameter("attr3", "Third Attribute to Retrieve",
"The third attribute to retrieve from the user's " +
"entry as part of the authentication process.",
true, DEFAULT_ATTR3);
// The parameter used to indicate the bind DN.
private StringParameter bindDNParameter =
new StringParameter("binddn", "Directory Bind DN",
"The DN to use when binding to the directory " +
"server to perform search and modify operations.",
false, "");
// The parameter used to indicate the address of the directory server.
private StringParameter hostParameter =
new StringParameter("ldap_host", "Directory Server Address",
"The address for the directory server.", true, "");
// The parameter that specifies the location of the SSL key store
private StringParameter keyStoreParameter =
new StringParameter("sslkeystore", "SSL Key Store",
"The path to the JSSE key store to use for an " +
"SSL-based connection", false, "");
// The parameter used to indicate the attribute to use for the login ID.
private StringParameter loginIDParameter =
new StringParameter("login_id_attr", "Login ID Attribute",
"The attribute to use as the login ID to find the " +
"user's entry.", true, DEFAULT_LOG_ID_ATTR);
// The parameter used to indicate the login ID value or value pattern.
private StringParameter loginIDValueParameter =
new StringParameter("login_id_value", "Login ID Value",
"The text to use as the value of the login ID " +
"attribute in search filters. The value may " +
"contain a range of numbers in square brackets.",
false, "");
// The parameter used to indicate the search base for the directory.
private StringParameter searchBaseParameter =
new StringParameter("search_base", "User Search Base",
"The DN in the directory server under which user " +
"entries may be found.", true, "");
// The parameter that specifies the location of the SSL trust store
private StringParameter trustStoreParameter =
new StringParameter("ssltruststore", "SSL Trust Store",
"The path to the JSSE trust store to use for an " +
"SSL-based connection", false, "");
// The stat tracker that will categorize the reasons for auth failures.
private CategoricalTracker failureReasonTracker;
// The stat tracker that will count the number of authentication attempts.
private IncrementalTracker attemptCounter;
// The stat tracker that will count the number of binds performed.
private IncrementalTracker bindCounter;
// The stat tracker that will count the number of failed authentications.
private IncrementalTracker failureCounter;
// The stat tracker that will count the number of modify operations performed.
private IncrementalTracker modCounter;
// The stat tracker that will count the number of searches performed.
private IncrementalTracker searchCounter;
// The stat tracker that will count the number of successful authentications.
private IncrementalTracker successCounter;
// The LDAP connection that will be used for authentication operations by this
// thread.
private LDAPConnection authConnection;
// The LDAP connection that will be used for bind operations by this thread.
private LDAPConnection bindConnection;
// The random number generator for this thread.
private Random random;
// The stat tracker that will time each authentication.
private TimeTracker authTimer;
// The stat tracker that will time each bind attempt.
private TimeTracker bindTimer;
// The stat tracker that will time the initial search to find the user's
// entry.
private TimeTracker initialSearchTimer;
// The stat tracker that will time each modify attempt.
private TimeTracker modTimer;
// The stat tracker that will time each search to retrieve specific attributes
// from the user's entry.
private TimeTracker subsequentSearchTimer;
/**
* Creates a new instance of this job thread. This constructor does not need
* to do anything other than invoke the constructor for the superclass.
*/
public SiteMinderJobClass()
{
super();
}
/**
* {@inheritDoc}
*/
@Override()
public String getJobName()
{
return "LDAP SiteMinder Load Simulator";
}
/**
* {@inheritDoc}
*/
@Override()
public String getShortDescription()
{
return "Process a sequence of LDAP search, bind, and update operations";
}
/**
* {@inheritDoc}
*/
@Override()
public String[] getLongDescription()
{
return new String[]
{
"This job can be used to simulate the load observed when using some " +
"versions of Netegrity SiteMinder. That load consists of eight " +
"search, one bind, and one modify operation."
};
}
/**
* {@inheritDoc}
*/
@Override()
public String getJobCategoryName()
{
return "LDAP";
}
/**
* {@inheritDoc}
*/
@Override()
public ParameterList getParameterStubs()
{
Parameter[] parameterArray = new Parameter[]
{
placeholder,
hostParameter,
portParameter,
bindDNParameter,
bindPWParameter,
placeholder,
searchBaseParameter,
loginDataFileParameter,
loginIDValueParameter,
loginPasswordParameter,
loginIDParameter,
placeholder,
attr1Parameter,
attr2Parameter,
attr3Parameter,
modAttrsParameter,
placeholder,
warmUpParameter,
coolDownParameter,
timeLimitParameter,
delayParameter,
maxRateParameter,
rateLimitDurationParameter,
placeholder,
useSSLParameter,
blindTrustParameter,
keyStoreParameter,
keyPWParameter,
trustStoreParameter,
trustPWParameter,
placeholder,
skipBindParameter,
ignoreInvCredParameter,
shareConnsParameter
};
return new ParameterList(parameterArray);
}
/**
* {@inheritDoc}
*/
@Override()
public StatTracker[] getStatTrackerStubs(String clientID, String threadID,
int collectionInterval)
{
return new StatTracker[]
{
new IncrementalTracker(clientID, threadID,
STAT_TRACKER_AUTHENTICATION_ATTEMPTS,
collectionInterval),
new IncrementalTracker(clientID, threadID,
STAT_TRACKER_SUCCESSFUL_AUTHENTICATIONS,
collectionInterval),
new IncrementalTracker(clientID, threadID,
STAT_TRACKER_FAILED_AUTHENTICATIONS,
collectionInterval),
new TimeTracker(clientID, threadID, STAT_TRACKER_AUTHENTICATION_TIME,
collectionInterval),
new IncrementalTracker(clientID, threadID, STAT_TRACKER_NUM_BINDS,
collectionInterval),
new TimeTracker(clientID, threadID, STAT_TRACKER_BIND_TIME,
collectionInterval),
new IncrementalTracker(clientID, threadID, STAT_TRACKER_NUM_MODS,
collectionInterval),
new TimeTracker(clientID, threadID, STAT_TRACKER_MOD_TIME,
collectionInterval),
new IncrementalTracker(clientID, threadID, STAT_TRACKER_NUM_SEARCH,
collectionInterval),
new TimeTracker(clientID, threadID, STAT_TRACKER_INITIAL_SEARCH_TIME,
collectionInterval),
new TimeTracker(clientID, threadID, STAT_TRACKER_SUBSEQUENT_SEARCH_TIME,
collectionInterval),
new CategoricalTracker(clientID, threadID, STAT_TRACKER_FAIL_REASON,
collectionInterval)
};
}
/**
* {@inheritDoc}
*/
@Override()
public StatTracker[] getStatTrackers()
{
return new StatTracker[]
{
attemptCounter,
successCounter,
failureCounter,
authTimer,
bindCounter,
bindTimer,
modCounter,
modTimer,
searchCounter,
initialSearchTimer,
subsequentSearchTimer,
failureReasonTracker
};
}
/**
* {@inheritDoc}
*/
@Override()
public void validateJobInfo(int numClients, int threadsPerClient,
int threadStartupDelay, Date startTime,
Date stopTime, int duration,
int collectionInterval, ParameterList parameters)
throws InvalidValueException
{
// The login ID parameter must be parseable as a value pattern.
StringParameter p =
parameters.getStringParameter(loginIDValueParameter.getName());
if ((p != null) && p.hasValue())
{
try
{
new ValuePattern(p.getValue());
}
catch (ParseException pe)
{
throw new InvalidValueException("The value provided for the '" +
p.getDisplayName() + "' parameter is not a valid value " +
"pattern: " + pe.getMessage(), pe);
}
}
FileURLParameter loginDataURLParameter =
parameters.getFileURLParameter(loginDataFileParameter.getName());
if ((loginDataURLParameter == null) ||
(! loginDataURLParameter.hasValue()))
{
StringParameter loginValueParameter =
parameters.getStringParameter(loginIDValueParameter.getName());
PasswordParameter loginPWParameter =
parameters.getPasswordParameter(loginPasswordParameter.getName());
if ((loginValueParameter == null) ||
(! loginValueParameter.hasValue()) ||
(loginPWParameter == null) ||
(! loginPWParameter.hasValue()))
{
throw new InvalidValueException("You must specify either a login " +
"data file URL or a login ID value " +
"and password");
}
}
}
/**
* {@inheritDoc}
*/
@Override()
public boolean providesParameterTest()
{
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public boolean testJobParameters(ParameterList parameters,
ArrayList<String> outputMessages)
{
// Get all the parameters that we might need to perform the test.
StringParameter hostParam =
parameters.getStringParameter(hostParameter.getName());
if ((hostParam == null) || (! hostParam.hasValue()))
{
outputMessages.add("ERROR: No directory server address was provided.");
return false;
}
String host = hostParam.getStringValue();
IntegerParameter portParam =
parameters.getIntegerParameter(portParameter.getName());
if ((portParam == null) || (! hostParam.hasValue()))
{
outputMessages.add("ERROR: No directory server port was provided.");
return false;
}
int port = portParam.getIntValue();
boolean useSSL = false;
BooleanParameter useSSLParam =
parameters.getBooleanParameter(useSSLParameter.getName());
if (useSSLParam != null)
{
useSSL = useSSLParam.getBooleanValue();
}
String bindDN = "";
StringParameter bindDNParam =
parameters.getStringParameter(bindDNParameter.getName());
if ((bindDNParam != null) && bindDNParam.hasValue())
{
bindDN = bindDNParam.getStringValue();
}
String bindPassword = "";
PasswordParameter bindPWParam =
parameters.getPasswordParameter(bindPWParameter.getName());
if ((bindPWParam != null) && bindPWParam.hasValue())
{
bindPassword = bindPWParam.getStringValue();
}
StringParameter baseDNParam =
parameters.getStringParameter(searchBaseParameter.getName());
if ((baseDNParam == null) || (! baseDNParam.hasValue()))
{
outputMessages.add("ERROR: No base DN was provided.");
return false;
}
String baseDN = baseDNParam.getStringValue();
// Create the LDAPConnection object that we will use to communicate with the
// directory server.
LDAPConnection conn;
if (useSSL)
{
try
{
SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
conn = new LDAPConnection(sslUtil.createSSLSocketFactory());
}
catch (Exception e)
{
outputMessages.add("ERROR: Unable to instantiate the blind trust " +
"socket factory for use in creating the SSL " +
"connection: " + stackTraceToString(e));
return false;
}
}
else
{
conn = new LDAPConnection();
}
// Attempt to establish a connection to the directory server.
try
{
if (useSSL)
{
outputMessages.add("Attempting to establish an SSL-based connection " +
"to " + host + ':' + port + "....");
}
else
{
outputMessages.add("Attempting to establish a connection to " + host +
':' + port + "....");
}
conn.connect(host, port, 10000);
outputMessages.add("Connected successfully.");
outputMessages.add("");
}
catch (Exception e)
{
outputMessages.add("ERROR: Unable to connect to the directory " +
"server: " + stackTraceToString(e));
return false;
}
// Attempt to bind to the directory server using the bind DN and password.
try
{
outputMessages.add("Attempting to perform an LDAPv3 bind to the " +
"directory server with a DN of '" + bindDN + "'....");
conn.bind(bindDN, bindPassword);
outputMessages.add("Bound successfully.");
outputMessages.add("");
}
catch (Exception e)
{
try
{
conn.close();
} catch (Exception e2) {}
outputMessages.add("ERROR: Unable to bind to the directory server: " +
stackTraceToString(e));
return false;
}
// Make sure that the entry specified as the base DN exists.
try
{
outputMessages.add("Checking to make sure that the base DN entry '" +
baseDN + "' exists in the directory....");
Entry baseDNEntry = conn.getEntry(baseDN, "1.1");
if (baseDNEntry == null)
{
try
{
conn.close();
} catch (Exception e2) {}
outputMessages.add("ERROR: Unable to retrieve the base DN entry.");
return false;
}
else
{
outputMessages.add("Successfully read the base DN entry.");
outputMessages.add("");
}
}
catch (Exception e)
{
try
{
conn.close();
} catch (Exception e2) {}
outputMessages.add("ERROR: Unable to retrieve the base DN entry: " +
stackTraceToString(e));
return false;
}
// At this point, all tests have passed. Close the connection and return
// true.
try
{
conn.close();
} catch (Exception e) {}
outputMessages.add("All tests completed successfully.");
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public void initializeClient(String clientID, ParameterList parameters)
throws UnableToRunException
{
// Seed the parent random number generator.
parentRandom = new Random();
// Get the directory server address
hostParameter = parameters.getStringParameter(hostParameter.getName());
if (hostParameter == null)
{
throw new UnableToRunException("No directory server host provided.");
}
else
{
directoryHost = hostParameter.getStringValue();
}
// Get the directory server port
portParameter = parameters.getIntegerParameter(portParameter.getName());
if (portParameter != null)
{
directoryPort = portParameter.getIntValue();
}
// Get the DN to use to bind to the directory server.
bindDNParameter = parameters.getStringParameter(bindDNParameter.getName());
if (bindDNParameter == null)
{
bindDN = "";
}
else
{
bindDN = bindDNParameter.getStringValue();
}
// Get the password to use to bind to the directory server.
bindPWParameter =
parameters.getPasswordParameter(bindPWParameter.getName());
if (bindPWParameter == null)
{
bindPW = "";
}
else
{
bindPW = bindPWParameter.getStringValue();
}
// Get the search base
searchBaseParameter =
parameters.getStringParameter(searchBaseParameter.getName());
if (searchBaseParameter != null)
{
searchBase = searchBaseParameter.getStringValue();
}
// Get the data from the login ID file.
useDataFile = false;
loginDataFileParameter =
parameters.getFileURLParameter(loginDataFileParameter.getName());
if ((loginDataFileParameter != null) && (loginDataFileParameter.hasValue()))
{
String[] fileLines;
try
{
fileLines = loginDataFileParameter.getNonBlankFileLines();
}
catch (Exception e)
{
throw new UnableToRunException("Unable to retrieve the login data " +
"from the file: " + e, e);
}
// Break the login data up into ID and passwords
ArrayList<String> loginIDList = new ArrayList<String>(fileLines.length);
ArrayList<String> passwordList = new ArrayList<String>(fileLines.length);
for (int i=0; i < fileLines.length; i++)
{
try
{
StringTokenizer tokenizer = new StringTokenizer(fileLines[i], "\t");
String loginID = tokenizer.nextToken();
String password = tokenizer.nextToken();
loginIDList.add(loginID);
passwordList.add(password);
} catch (Exception e) {}
}
// Convert the lists into arrays and make sure that at least one login
// ID/password has been provided.
loginIDs = new String[loginIDList.size()];
loginPasswords = new String[passwordList.size()];
loginIDList.toArray(loginIDs);
passwordList.toArray(loginPasswords);
if (loginIDs.length == 0)
{
throw new UnableToRunException("No login IDs/passwords extracted " +
"from the login data file.");
}
useDataFile = true;
}
else
{
loginPasswordParameter =
parameters.getPasswordParameter(loginPasswordParameter.getName());
if ((loginPasswordParameter != null) &&
(loginPasswordParameter.hasValue()))
{
loginPassword = loginPasswordParameter.getStringValue();
}
loginIDValueParameter =
parameters.getStringParameter(loginIDValueParameter.getName());
try
{
loginIDPattern =
new ValuePattern(loginIDValueParameter.getStringValue());
}
catch (Exception e)
{
throw new UnableToRunException(
"Unable to parse the login ID pattern: " + stackTraceToString(e),
e);
}
}
// Get the login ID attribute.
loginIDParameter =
parameters.getStringParameter(loginIDParameter.getName());
if (loginIDParameter != null)
{
loginIDAttr = loginIDParameter.getStringValue();
}
// Get the attributes to retrieve.
attr1Parameter = parameters.getStringParameter(attr1Parameter.getName());
if (attr1Parameter != null)
{
searchAttr1 = attr1Parameter.getStringValue();
filter1 = Filter.createPresenceFilter(searchAttr1);
returnAttrs1 = new String[] { searchAttr1 };
}
attr2Parameter = parameters.getStringParameter(attr2Parameter.getName());
if (attr2Parameter != null)
{
searchAttr2 = attr2Parameter.getStringValue();
filter2 = Filter.createPresenceFilter(searchAttr2);
returnAttrs2 = new String[] { searchAttr2 };
}
attr3Parameter = parameters.getStringParameter(attr3Parameter.getName());
if (attr3Parameter != null)
{
searchAttr3 = attr3Parameter.getStringValue();
filter3 = Filter.createPresenceFilter(searchAttr3);
returnAttrs3 = new String[] { searchAttr3 };
}
returnAttrsOC = new String[] { "objectClass" };
// Get the attributes to modify.
modAttrs = null;
modAttrsParameter =
parameters.getMultiLineTextParameter(modAttrsParameter.getName());
if ((modAttrsParameter != null) && (modAttrsParameter.hasValue()))
{
modAttrs = modAttrsParameter.getNonBlankLines();
}
// Determine whether to skip the bind attempt.
skipBind = false;
skipBindParameter =
parameters.getBooleanParameter(skipBindParameter.getName());
if (skipBindParameter != null)
{
skipBind = skipBindParameter.getBooleanValue();
}
// Get the warm up time.
warmUpTime = 0;
warmUpParameter = parameters.getIntegerParameter(warmUpParameter.getName());
if (warmUpParameter != null)
{
warmUpTime = warmUpParameter.getIntValue();
}
// Get the cool down time.
coolDownTime = 0;
coolDownParameter =
parameters.getIntegerParameter(coolDownParameter.getName());
if (coolDownParameter != null)
{
coolDownTime = coolDownParameter.getIntValue();
}
// Get the max operation time limit.
timeLimitParameter =
parameters.getIntegerParameter(timeLimitParameter.getName());
if (timeLimitParameter != null)
{
timeLimit = timeLimitParameter.getIntValue();
}
// Get the delay between authentication attempts.
delay = 0;
delayParameter = parameters.getIntegerParameter(delayParameter.getName());
if (delayParameter != null)
{
delay = delayParameter.getIntValue();
}
// Initialize the rate limiter.
rateLimiter = null;
maxRateParameter =
parameters.getIntegerParameter(maxRateParameter.getName());
if ((maxRateParameter != null) && maxRateParameter.hasValue())
{
int maxRate = maxRateParameter.getIntValue();
if (maxRate > 0)
{
int rateIntervalSeconds = 0;
rateLimitDurationParameter = parameters.getIntegerParameter(
rateLimitDurationParameter.getName());
if ((rateLimitDurationParameter != null) &&
rateLimitDurationParameter.hasValue())
{
rateIntervalSeconds = rateLimitDurationParameter.getIntValue();
}
if (rateIntervalSeconds <= 0)
{
rateIntervalSeconds = getClientSideJob().getCollectionInterval();
}
rateLimiter = new FixedRateBarrier(rateIntervalSeconds * 1000L,
maxRate * rateIntervalSeconds);
}
}
// Get the flag indicating whether we should use SSL or not
useSSL = false;
useSSLParameter = parameters.getBooleanParameter(useSSLParameter.getName());
if (useSSLParameter != null)
{
useSSL = useSSLParameter.getBooleanValue();
}
// Get the indicator that specifies whether to ignore invalid credentials
// errors.
ignoreInvCredParameter =
parameters.getBooleanParameter(ignoreInvCredParameter.getName());
if (ignoreInvCredParameter != null)
{
ignoreInvalidCredentials = ignoreInvCredParameter.getBooleanValue();
}
// Get the indicator that specifies whether to use shared connections.
shareConnsParameter =
parameters.getBooleanParameter(shareConnsParameter.getName());
if (shareConnsParameter != null)
{
useSharedConnections = shareConnsParameter.getBooleanValue();
}
// If we are to use shared connections, then establish them now.
if (useSharedConnections)
{
if (useSSL)
{
try
{
SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
sharedAuthConnection =
new LDAPConnection(sslUtil.createSSLSocketFactory());
sharedBindConnection =
new LDAPConnection(sslUtil.createSSLSocketFactory());
}
catch (Exception e)
{
throw new UnableToRunException(String.valueOf(e), e);
}
}
else
{
sharedAuthConnection = new LDAPConnection();
sharedBindConnection = new LDAPConnection();
}
try
{
sharedAuthConnection.connect(directoryHost, directoryPort, 10000);
sharedAuthConnection.bind(bindDN, bindPW);
sharedBindConnection.connect(directoryHost, directoryPort, 10000);
}
catch (Exception e)
{
throw new UnableToRunException("Could not establish shared " +
"connections to the directory: " + e,
e);
}
LDAPConnectionOptions opts = sharedAuthConnection.getConnectionOptions();
opts.setResponseTimeoutMillis(1000L * timeLimit);
sharedAuthConnection.setConnectionOptions(opts);
sharedBindConnection.setConnectionOptions(opts);
}
}
/**
* {@inheritDoc}
*/
@Override()
public void initializeThread(String clientID, String threadID,
int collectionInterval, ParameterList parameters)
throws UnableToRunException
{
// Seed the random number generator for this thread.
random = new Random(parentRandom.nextLong());
// If we are not going to use shared connections, then create the
// connections for use by this thread. Otherwise, just grab the shared
// connections.
if (useSharedConnections)
{
authConnection = sharedAuthConnection;
bindConnection = sharedBindConnection;
}
else
{
if (useSSL)
{
try
{
SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
authConnection =
new LDAPConnection(sslUtil.createSSLSocketFactory());
bindConnection =
new LDAPConnection(sslUtil.createSSLSocketFactory());
}
catch (Exception e)
{
throw new UnableToRunException(String.valueOf(e), e);
}
}
else
{
authConnection = new LDAPConnection();
bindConnection = new LDAPConnection();
}
try
{
authConnection.connect(directoryHost, directoryPort, 10000);
authConnection.bind(bindDN, bindPW);
bindConnection.connect(directoryHost, directoryPort, 10000);
}
catch (Exception e)
{
throw new UnableToRunException("Unable to establish the connections " +
"to the directory server: " + e, e);
}
LDAPConnectionOptions opts = authConnection.getConnectionOptions();
opts.setResponseTimeoutMillis(1000L * timeLimit);
authConnection.setConnectionOptions(opts);
bindConnection.setConnectionOptions(opts);
}
// Create the stat trackers.
attemptCounter =
new IncrementalTracker(clientID, threadID,
STAT_TRACKER_AUTHENTICATION_ATTEMPTS,
collectionInterval);
successCounter =
new IncrementalTracker(clientID, threadID,
STAT_TRACKER_SUCCESSFUL_AUTHENTICATIONS,
collectionInterval);
failureCounter =
new IncrementalTracker(clientID, threadID,
STAT_TRACKER_FAILED_AUTHENTICATIONS,
collectionInterval);
bindCounter = new IncrementalTracker(clientID, threadID,
STAT_TRACKER_NUM_BINDS,
collectionInterval);
modCounter = new IncrementalTracker(clientID, threadID,
STAT_TRACKER_NUM_MODS,
collectionInterval);
searchCounter = new IncrementalTracker(clientID, threadID,
STAT_TRACKER_NUM_SEARCH,
collectionInterval);
authTimer = new TimeTracker(clientID, threadID,
STAT_TRACKER_AUTHENTICATION_TIME,
collectionInterval);
bindTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_BIND_TIME,
collectionInterval);
modTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_MOD_TIME,
collectionInterval);
initialSearchTimer = new TimeTracker(clientID, threadID,
STAT_TRACKER_INITIAL_SEARCH_TIME,
collectionInterval);
subsequentSearchTimer = new TimeTracker(clientID, threadID,
STAT_TRACKER_SUBSEQUENT_SEARCH_TIME,
collectionInterval);
failureReasonTracker = new CategoricalTracker(clientID, threadID,
STAT_TRACKER_FAIL_REASON,
collectionInterval);
// Enable real-time reporting of the data for these stat trackers.
RealTimeStatReporter statReporter = getStatReporter();
if (statReporter != null)
{
String jobID = getJobID();
attemptCounter.enableRealTimeStats(statReporter, jobID);
successCounter.enableRealTimeStats(statReporter, jobID);
failureCounter.enableRealTimeStats(statReporter, jobID);
bindCounter.enableRealTimeStats(statReporter, jobID);
modCounter.enableRealTimeStats(statReporter, jobID);
searchCounter.enableRealTimeStats(statReporter, jobID);
authTimer.enableRealTimeStats(statReporter, jobID);
bindTimer.enableRealTimeStats(statReporter, jobID);
modTimer.enableRealTimeStats(statReporter, jobID);
initialSearchTimer.enableRealTimeStats(statReporter, jobID);
subsequentSearchTimer.enableRealTimeStats(statReporter, jobID);
}
}
/**
* {@inheritDoc}
*/
@Override()
public void runJob()
{
// Determine the range of time for which we should collect statistics.
long currentTime = System.currentTimeMillis();
boolean collectingStats = false;
long startCollectingTime = currentTime + (1000 * warmUpTime);
long stopCollectingTime = Long.MAX_VALUE;
if ((coolDownTime > 0) && (getShouldStopTime() > 0))
{
stopCollectingTime = getShouldStopTime() - (1000 * coolDownTime);
}
// Define a variable that will be used to determine how long to sleep
// between attempts.
long authStartTime = 0;
// Loop until it is time to stop.
while (! shouldStop())
{
if (rateLimiter != null)
{
if (rateLimiter.await())
{
continue;
}
}
currentTime = System.currentTimeMillis();
if ((! collectingStats) && (currentTime >= startCollectingTime) &&
(currentTime < stopCollectingTime))
{
// Start all the stat trackers.
attemptCounter.startTracker();
successCounter.startTracker();
failureCounter.startTracker();
authTimer.startTracker();
bindCounter.startTracker();
modCounter.startTracker();
searchCounter.startTracker();
bindTimer.startTracker();
modTimer.startTracker();
initialSearchTimer.startTracker();
subsequentSearchTimer.startTracker();
failureReasonTracker.startTracker();
collectingStats = true;
}
else if ((collectingStats) && (currentTime >= stopCollectingTime))
{
attemptCounter.stopTracker();
successCounter.stopTracker();
failureCounter.stopTracker();
authTimer.stopTracker();
bindCounter.stopTracker();
modCounter.stopTracker();
searchCounter.stopTracker();
bindTimer.stopTracker();
modTimer.stopTracker();
initialSearchTimer.stopTracker();
subsequentSearchTimer.stopTracker();
failureReasonTracker.stopTracker();
collectingStats = false;
}
// See if we need to sleep before the next attempt
if ((delay > 0) && (authStartTime > 0))
{
long now = System.currentTimeMillis();
long sleepTime = delay - (now - authStartTime);
if (sleepTime > 0)
{
try
{
Thread.sleep(sleepTime);
} catch (InterruptedException ie) {}
if (shouldStop())
{
break;
}
}
}
// Get a random user number and translate that to a login ID and password.
String[] loginInfo = getLoginInfo();
String loginID = loginInfo[0];
String password = loginInfo[1];
// Start the auth attempt timer now.
if (delay > 0)
{
authStartTime = System.currentTimeMillis();
}
// Increment the number of authentication attempts and start the timer
if (collectingStats)
{
attemptCounter.increment();
authTimer.startTimer();
}
String failureReason = "Search 1";
try
{
// First, issue a search to try to find the user's entry.
String userDN = null;
Filter filter = Filter.createEqualityFilter(loginIDAttr, loginID);
if (collectingStats)
{
searchCounter.increment();
initialSearchTimer.startTimer();
}
SearchResult searchResult = authConnection.search(searchBase,
SearchScope.SUB, filter, returnAttrsOC);
if (collectingStats)
{
initialSearchTimer.stopTimer();
}
userDN = searchResult.getSearchEntries().get(0).getDN();
// Make sure that we got a user DN. If not, then it's a failed attempt.
if (userDN == null)
{
if (collectingStats)
{
failureCounter.increment();
authTimer.stopTimer();
failureReasonTracker.increment(failureReason);
}
continue;
}
// Now do a base-level search on the user's entry to retrieve the
// objectClass attribute.
failureReason = "Search 2";
filter = Filter.createPresenceFilter("objectClass");
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter, returnAttrsOC);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
// Now bind as the user.
if (! skipBind)
{
failureReason = "Bind";
try
{
if (collectingStats)
{
bindCounter.increment();
bindTimer.startTimer();
}
bindConnection.bind(userDN, password);
if (collectingStats)
{
bindTimer.stopTimer();
}
}
catch (LDAPException le)
{
if (collectingStats)
{
bindTimer.stopTimer();
}
if (le.getResultCode() == ResultCode.INVALID_CREDENTIALS)
{
if (! ignoreInvalidCredentials)
{
if (collectingStats)
{
failureCounter.increment();
authTimer.stopTimer();
failureReasonTracker.increment(failureReason);
}
continue;
}
}
else
{
if (collectingStats)
{
failureCounter.increment();
authTimer.stopTimer();
failureReasonTracker.increment(failureReason);
}
continue;
}
}
}
// Now retrieve the "attr1" attribute from the user's entry.
failureReason = "Search 3";
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter1, returnAttrs1);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
// Retrieve the "attr2" attribute from the user's entry.
failureReason = "Search 4";
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter2, returnAttrs2);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
// Retrieve the first attribute again.
failureReason = "Search 5";
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter1, returnAttrs1);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
// Perform a modification on the entry
if ((modAttrs != null) && (modAttrs.length > 0))
{
failureReason = "Modify";
Modification[] mods = new Modification[modAttrs.length];
for (int i=0; i < modAttrs.length; i++)
{
mods[i] = new Modification(ModificationType.REPLACE, modAttrs[i],
getRandomString(80));
}
if (collectingStats)
{
modCounter.increment();
modTimer.startTimer();
}
authConnection.modify(userDN, mods);
if (collectingStats)
{
modTimer.stopTimer();
}
}
// Retrieve the first attribute again.
failureReason = "Search 6";
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter1, returnAttrs1);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
// Retrieve the first attribute again.
failureReason = "Search 7";
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter1, returnAttrs1);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
// Retrieve the third attribute.
failureReason = "Search 8";
if (collectingStats)
{
searchCounter.increment();
subsequentSearchTimer.startTimer();
}
authConnection.search(userDN, SearchScope.BASE, filter3, returnAttrs3);
if (collectingStats)
{
subsequentSearchTimer.stopTimer();
}
}
catch (Exception e)
{
if (collectingStats)
{
failureCounter.increment();
authTimer.stopTimer();
failureReasonTracker.increment(failureReason);
}
continue;
}
// If we have gotten here, then everything is done and we can consider the
// authentication successful.
if (collectingStats)
{
successCounter.increment();
authTimer.stopTimer();
}
}
// Stop all the stat trackers.
if (collectingStats)
{
attemptCounter.stopTracker();
successCounter.stopTracker();
failureCounter.stopTracker();
authTimer.stopTracker();
bindCounter.stopTracker();
modCounter.stopTracker();
searchCounter.stopTracker();
bindTimer.stopTracker();
modTimer.stopTracker();
initialSearchTimer.stopTracker();
subsequentSearchTimer.stopTracker();
failureReasonTracker.stopTracker();
}
// Close the connections to the directory server if appropriate.
if (! useSharedConnections)
{
try
{
authConnection.close();
} catch (Exception e) {}
try
{
bindConnection.close();
} catch (Exception e) {}
}
}
/**
* {@inheritDoc}
*/
@Override()
public void destroyThread()
{
if (authConnection != null)
{
try
{
authConnection.close();
} catch (Exception e) {}
authConnection = null;
}
if (bindConnection != null)
{
try
{
bindConnection.close();
} catch (Exception e) {}
bindConnection = null;
}
}
/**
* {@inheritDoc}
*/
@Override()
public void finalizeClient()
{
// Make sure that the shared connections get closed properly.
if (useSharedConnections)
{
try
{
sharedAuthConnection.close();
} catch (Exception e) {}
try
{
sharedBindConnection.close();
} catch (Exception e) {}
}
}
/**
* Retrieves an array containing the login ID and password that should be
* used to authenticate to the directory.
*
* @return An array containing the login ID and password.
*/
private String[] getLoginInfo()
{
String[] loginInfo = new String[2];
if (useDataFile)
{
int slot = (random.nextInt() & 0x7FFFFFFF) % loginIDs.length;
loginInfo[0] = loginIDs[slot];
loginInfo[1] = loginPasswords[slot];
}
else
{
loginInfo[0] = loginIDPattern.nextValue();
loginInfo[1] = loginPassword;
}
return loginInfo;
}
/**
* Retrieves a string of random characters of the specified length.
*
* @param length The number of characters to include in the string.
*
* @return The generated string of random characters.
*/
private String getRandomString(int length)
{
char[] returnArray = new char[length];
for (int i=0; i < returnArray.length; i++)
{
returnArray[i] = ALPHABET[Math.abs((random.nextInt()) & 0x7FFFFFFF) %
ALPHABET.length];
}
return new String(returnArray);
}
}