/* * 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.LinkedList; import java.util.Random; import com.unboundid.ldap.sdk.Attribute; import com.unboundid.ldap.sdk.Entry; import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPConnectionOptions; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.Modification; import com.unboundid.ldap.sdk.ModificationType; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.ldap.sdk.SearchRequest; 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.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 implements a SLAMD job class that has the ability to generate * various kinds of load against an LDAP directory server. It can perform * search, compare, add, delete, modify, and modify RDN operations. The * relative frequencies of each kind of operation may be specified by the user * scheduling the job for execution. * * * @author Neil A. Wilson */ public class MultiSearchLDAPLoadJobClass extends JobClass { /** * The set of characters that will make up randomly-generated strings. */ public static final char[] ALPHABET = "abcdefghijklmnopqrstuvwxyz".toCharArray(); /** * The name of the stat tracker that counts the number of attempted adds. */ public static final String STAT_TRACKER_ADD_ATTEMPTS = "Add Attempts"; /** * The name of the stat tracker that times add operations. */ public static final String STAT_TRACKER_ADD_TIME = "Add Time (ms)"; /** * The name of the stat tracker that counts the number of attempted compares. */ public static final String STAT_TRACKER_COMPARE_ATTEMPTS = "Compare Attempts"; /** * The name of the stat tracker that times compare operations. */ public static final String STAT_TRACKER_COMPARE_TIME = "Compare Time (ms)"; /** * The name of the stat tracker that counts the number of attempted deletes. */ public static final String STAT_TRACKER_DELETE_ATTEMPTS = "Delete Attempts"; /** * The name of the stat tracker that times delete operations. */ public static final String STAT_TRACKER_DELETE_TIME = "Delete Time (ms)"; /** * The name of the stat tracker that counts the number of attempted modifies. */ public static final String STAT_TRACKER_MODIFY_ATTEMPTS = "Modify Attempts"; /** * The name of the stat tracker that times modify operations. */ public static final String STAT_TRACKER_MODIFY_TIME = "Modify Time (ms)"; /** * The name of the stat tracker that counts the number of attempted modify RDN * operations. */ public static final String STAT_TRACKER_MODIFY_RDN_ATTEMPTS = "Modify RDN Attempts"; /** * The name of the stat tracker that times modify RDN operations. */ public static final String STAT_TRACKER_MODIFY_RDN_TIME = "Modify RDN Time (ms)"; /** * The name of the stat tracker that counts the number of attempted * operations. */ public static final String STAT_TRACKER_OPERATION_ATTEMPTS = "Overall Operations Attempted"; /** * The name of the stat tracker that categorizes the attempted operations. */ public static final String STAT_TRACKER_OPERATION_ATTEMPTS_BY_CATEGORY = "Types of Operations Attempted"; /** * The name of the stat tracker that times attempted operations. */ public static final String STAT_TRACKER_OPERATION_TIME = "Overall Operation Time"; /** * The name of the stat tracker that categorizes the result codes received * from the operations. */ public static final String STAT_TRACKER_RESULT_CODES = "Result Codes"; /** * The name of the stat tracker that counts the number of attempted searches * from the first filter file. */ public static final String STAT_TRACKER_SEARCH_ATTEMPTS_1 = "Search Attempts 1"; /** * The name of the stat tracker that times search operations from the first * filter file. */ public static final String STAT_TRACKER_SEARCH_TIME_1 = "Search Time 1 (ms)"; /** * The name of the stat tracker that counts the number of attempted searches * from the first filter file. */ public static final String STAT_TRACKER_SEARCH_ATTEMPTS_2 = "Search Attempts 2"; /** * The name of the stat tracker that times search operations from the first * filter file. */ public static final String STAT_TRACKER_SEARCH_TIME_2 = "Search Time 2 (ms)"; /** * The name of the stat tracker that counts the number of attempted searches * from the first filter file. */ public static final String STAT_TRACKER_SEARCH_ATTEMPTS_3 = "Search Attempts 3"; /** * The name of the stat tracker that times search operations from the first * filter file. */ public static final String STAT_TRACKER_SEARCH_TIME_3 = "Search Time 3 (ms)"; /** * The name of the stat tracker that counts the number of attempted searches * from the first filter file. */ public static final String STAT_TRACKER_SEARCH_ATTEMPTS_4 = "Search Attempts 4"; /** * The name of the stat tracker that times search operations from the first * filter file. */ public static final String STAT_TRACKER_SEARCH_TIME_4 = "Search Time 4 (ms)"; /** * The name of the stat tracker that counts the number of attempted searches * from the first filter file. */ public static final String STAT_TRACKER_SEARCH_ATTEMPTS_5 = "Search Attempts 5"; /** * The name of the stat tracker that times search operations from the first * filter file. */ public static final String STAT_TRACKER_SEARCH_TIME_5 = "Search Time 5 (ms)"; /** * The name of the stat tracker that counts the number of attempted searches * from the first filter file. */ public static final String STAT_TRACKER_SEARCH_ATTEMPTS_6 = "Search Attempts 6"; /** * The name of the stat tracker that times search operations from the first * filter file. */ public static final String STAT_TRACKER_SEARCH_TIME_6 = "Search Time 6 (ms)"; // 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 that indicates whether the job should clean up any entries // that may have been added during processing. private BooleanParameter cleanUpParameter = new BooleanParameter("cleanup", "Clean Up When Done", "Indicates whether each client should clean up " + "any entries that may have been added during " + "processing that have not yet been removed.", true); // The parameter that indicates whether to disconnect after each operation. private BooleanParameter disconnectParameter = new BooleanParameter("disconnect", "Disconnect when Rebinding", "Indicates whether to close and re-establish the " + "connection to the directory server whenever a " + "rebind occurs.", false); // The parameter that indicates whether to follow referrals. private BooleanParameter referralsParameter = new BooleanParameter("follow_referrals", "Follow Referrals", "Indicates whether to follow referrals " + "encountered while performing operations in the " + "directory.", false); // The parameter that indicates whether to use SSL when communicating with the // directory server. private BooleanParameter useSSLParameter = new BooleanParameter("use_ssl", "Use SSL", "Indicates whether to use SSL when communicating " + "with the directory server.", false); // The parameter that specifies the URL of the search filter file. private FileURLParameter filterFile1URLParameter = new FileURLParameter("filter_file_1", "Search Filter File URL 1", "Specifies the URL (FILE or HTTP) to the file " + "that contains a set of filters that may be used " + "when performing the the first kind of searches.", null, false); // The parameter that specifies the URL of the search filter file. private FileURLParameter filterFile2URLParameter = new FileURLParameter("filter_file_2", "Search Filter File URL 2", "Specifies the URL (FILE or HTTP) to the file " + "that contains a set of filters that may be used " + "when performing the the second kind of searches.", null, false); // The parameter that specifies the URL of the search filter file. private FileURLParameter filterFile3URLParameter = new FileURLParameter("filter_file_3", "Search Filter File URL 3", "Specifies the URL (FILE or HTTP) to the file " + "that contains a set of filters that may be used " + "when performing the the third kind of searches.", null, false); // The parameter that specifies the URL of the search filter file. private FileURLParameter filterFile4URLParameter = new FileURLParameter("filter_file_4", "Search Filter File URL 4", "Specifies the URL (FILE or HTTP) to the file " + "that contains a set of filters that may be used " + "when performing the the fourth kind of searches.", null, false); // The parameter that specifies the URL of the search filter file. private FileURLParameter filterFile5URLParameter = new FileURLParameter("filter_file_5", "Search Filter File URL 5", "Specifies the URL (FILE or HTTP) to the file " + "that contains a set of filters that may be used " + "when performing the the fifth kind of searches.", null, false); // The parameter that specifies the URL of the search filter file. private FileURLParameter filterFile6URLParameter = new FileURLParameter("filter_file_6", "Search Filter File URL 6", "Specifies the URL (FILE or HTTP) to the file " + "that contains a set of filters that may be used " + "when performing the the fifth kind of searches.", null, false); // The parameter that specifies the frequency for add operations. private IntegerParameter addFrequencyParameter = new IntegerParameter("add_frequency", "Add Operation Frequency", "Specifies the frequency with which adds should be " + "performed relative to the other types of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for compare operations. private IntegerParameter compareFrequencyParameter = new IntegerParameter("compare_frequency", "Compare Operation Frequency", "Specifies the frequency with which compares " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // 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 running after ending statistics " + "collection.", true, 0, true, 0, false, 0); // The parameter that specifies the delay between requests. private IntegerParameter delayParameter = new IntegerParameter("request_delay", "Time Between Requests (ms)", "Specifies the length of time in milliseconds " + "that will pass between requests. Note that " + "is the time between requests and not the time " + "between the end of one operation and the " + "beginning of the next. If any operation takes " + "longer than this length of time, then there " + "will be no delay before the start of the next " + "operation.", false, 0, true, 0, false, 0); // The parameter that specifies the frequency for delete operations. private IntegerParameter deleteFrequencyParameter = new IntegerParameter("delete_frequency", "Delete Operation Frequency", "Specifies the frequency with which deletes " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for modify operations. private IntegerParameter modifyFrequencyParameter = new IntegerParameter("modify_frequency", "Modify Operation Frequency", "Specifies the frequency with which modifies " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the maximum request rate. private IntegerParameter maxRateParameter = new IntegerParameter("maxRate", "Max Operation Rate (Ops/Second/Client)", "Specifies the maximum operation rate (in ops 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 operations as quickly as possible.", true, -1); // The parameter that specifies the frequency for modify RDN operations. private IntegerParameter modifyRDNFrequencyParameter = new IntegerParameter("modify_rdn_frequency", "Modify RDN Operation Frequency", "Specifies the frequency with which modify RDNs " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the number of operations between binds. private IntegerParameter opsBetweenBindsParameter = new IntegerParameter("ops_between_binds", "Operations Between Binds", "Specifies the number of operations to perform " + "before re-binding as another user.", false, 0, true, 0, false, 0); // The parameter that specifies the port number for the directory server. private IntegerParameter portParameter = new IntegerParameter("ldap_port", "Directory Server Port", "Specifies 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 that specifies the frequency for search operations. private IntegerParameter searchFrequency1Parameter = new IntegerParameter("search_frequency_1", "Search 1 Frequency", "Specifies the frequency with which search 1 " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for search operations. private IntegerParameter searchFrequency2Parameter = new IntegerParameter("search_frequency_2", "Search 2 Frequency", "Specifies the frequency with which search 2 " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for search operations. private IntegerParameter searchFrequency3Parameter = new IntegerParameter("search_frequency_3", "Search 3 Frequency", "Specifies the frequency with which search 3 " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for search operations. private IntegerParameter searchFrequency4Parameter = new IntegerParameter("search_frequency_4", "Search 4 Frequency", "Specifies the frequency with which search 4 " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for search operations. private IntegerParameter searchFrequency5Parameter = new IntegerParameter("search_frequency_5", "Search 5 Frequency", "Specifies the frequency with which search 5 " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the frequency for search operations. private IntegerParameter searchFrequency6Parameter = new IntegerParameter("search_frequency_6", "Search 6 Frequency", "Specifies the frequency with which search 6 " + "should be performed relative to the other types " + "of operations.", true, 0, true, 0, false, 0); // The parameter that specifies the maximum number of entries that should be // returned from a single search operation. private IntegerParameter sizeLimitParameter = new IntegerParameter("size_limit", "Search Size Limit", "Specifies the maximum number of entries that " + "should be returned from a single search " + "operation. A size limit of zero indicates that " + "there is no limit.", false, 0, true, 0, false, 0); // The parameter that specifies the maximum length of time in seconds that any // operation will be allowed to take before being abandoned. private IntegerParameter timeLimitParameter = new IntegerParameter("time_limit", "Operation Time Limit", "Specifies the maximum length of time in seconds " + "will be allowed for any single operation. If " + "operation takes longer than this length of time " + "it will be abandoned. A time limit of zero " + "indicates that there is no time limit.", false, 0, true, 0, false, 0); // The parameter that specifies the warm-up time in seconds. private IntegerParameter warmUpParameter = new IntegerParameter("warm_up", "Warm Up Time", "The time in seconds that the job should run " + "before beginning statistics collection.", true, 0, true, 0, false, 0); // The parameter that specifies the password to use when binding to the // directory server. private PasswordParameter bindPasswordParameter = new PasswordParameter("bind_pw", "Bind Password", "Specifies the password to use when binding to " + "the directory server. If no password is " + "specified, then the bind will be performed " + "anonymously.", false, ""); // The parameter that specifies the password to access the SSL key store. private PasswordParameter sslKeyPWParameter = new PasswordParameter("ssl_key_pw", "SSL Key Store Password", "Specifies the password to use when accessing " + "the JSSE key store. If SSL is not used, then " + "this does not need to be specified.", false, ""); // The parameter that specifies the password to access the SSL trust store. private PasswordParameter sslTrustPWParameter = new PasswordParameter("ssl_trust_pw", "SSL Trust Store Password", "Specifies the password to use when accessing " + "the JSSE trust store. If SSL is not used, " + "then this does not need to be specified.", false, ""); // A placeholder parameter used to visually group related parameters. private PlaceholderParameter placeholder = new PlaceholderParameter(); // The parameter that specifies the address of the directory server. private StringParameter addressParameter = new StringParameter("ldap_host", "Directory Server Address", "Specifies the address for the directory server.", true, ""); // The parameter that specifies the attribute to target for modify and compare // operations. private StringParameter attrParameter = new StringParameter("attr", "Attribute to Compare/Modify", "Specifies the LDAP attribute at which modify and " + "compare operations will be targeted.", true, "description"); // The parameter that specifies the base DN for operations in the directory. private StringParameter baseDNParameter = new StringParameter("base_dn", "Directory Base DN", "Specifies the base DN under which all operations " + "will be performed in the directory.", true, ""); // The parameter that specifies the DN to use when binding to the directory. private StringParameter bindDNParameter = new StringParameter("bind_dn", "Bind DN", "Specifies the DN to use when binding to the " + "directory server for all operations. If no bind " + "DN is specified, then the bind will be performed " + "anonymously.", true, ""); // The parameter that specifies the location of the JSSE key store. private StringParameter sslKeyStoreParameter = new StringParameter("ssl_key_store", "SSL Key Store", "Specifies the location of the JSSE key store to " + "use with SSL. If SSL is not used, then this " + "value does not need to be specified.", false, ""); // The parameter that specifies the location of the JSSE trust store. private StringParameter sslTrustStoreParameter = new StringParameter("ssl_trust_store", "SSL Trust Store", "Specifies the location of the JSSE trust store " + "to use with SSL. If SSL is not used, then this " + "value does not need to be specified.", false, ""); // Static variables used to hold the values of the parameters in each client // (or variables related to the values of those parameters). private static boolean alwaysDisconnect; private static boolean cleanUp; private static boolean followReferrals; private static boolean useSSL; private static int addFrequency; private static int compareFrequency; private static int coolDownTime; private static int deleteFrequency; private static int ldapPort; private static int modifyFrequency; private static int modifyRDNFrequency; private static int operationDelay; private static int opsBetweenBinds; private static int searchFrequency1; private static int searchFrequency2; private static int searchFrequency3; private static int searchFrequency4; private static int searchFrequency5; private static int searchFrequency6; private static int sizeLimit; private static int timeLimit; private static int totalFrequency; private static int warmUpTime; private static int[] opWeights; private static Random parentRandom; private static String baseDN; private static String bindPW; private static String ldapHost; private static String modAttr; private static String[] searchFilters1; private static String[] searchFilters2; private static String[] searchFilters3; private static String[] searchFilters4; private static String[] searchFilters5; private static String[] searchFilters6; private static ValuePattern bindDNPattern; // Static variables used to keep track of the DNs of all entries added to the // directory. This list will be used for the entries to delete and to rename. private static int dnsToDelete = 0; private static LinkedList<String> addedDNs = new LinkedList<String>(); private static final Object addedDNMutex = new Object(); // The rate limiter for this job. private static FixedRateBarrier rateLimiter; // Instance variables used as the stat trackers. private CategoricalTracker operationTypes; private CategoricalTracker resultCodes; private IncrementalTracker addCount; private IncrementalTracker compareCount; private IncrementalTracker deleteCount; private IncrementalTracker modifyCount; private IncrementalTracker modifyRDNCount; private IncrementalTracker operationCount; private IncrementalTracker searchCount1; private IncrementalTracker searchCount2; private IncrementalTracker searchCount3; private IncrementalTracker searchCount4; private IncrementalTracker searchCount5; private IncrementalTracker searchCount6; private TimeTracker addTimer; private TimeTracker compareTimer; private TimeTracker deleteTimer; private TimeTracker modifyTimer; private TimeTracker modifyRDNTimer; private TimeTracker operationTimer; private TimeTracker searchTimer1; private TimeTracker searchTimer2; private TimeTracker searchTimer3; private TimeTracker searchTimer4; private TimeTracker searchTimer5; private TimeTracker searchTimer6; // Other instance variables used in this thread. private boolean collectingStats; private LDAPConnection conn; private Random random; private String currentBindDN; /** * Creates a new instance of this job thread. This constructor * does not need to do anything other than invoke the constructor * for the superclass. relative*/ public MultiSearchLDAPLoadJobClass() { super(); } /** * {@inheritDoc} */ @Override() public String getJobName() { return "LDAP Load Generator (with multiple searches)"; } /** * {@inheritDoc} */ @Override() public String getShortDescription() { return "Generate various kinds of load against an LDAP directory server"; } /** * {@inheritDoc} */ @Override() public String[] getLongDescription() { return new String[] { "This job can be used to generate various kinds of load against an " + "LDAP directory server. It provides the ability to perform multiple " + "kinds of searches using filters read from different files. It may " + "also be used to perform add, compare, delete, modify, and modify DN " + "operations." }; } /** * {@inheritDoc} */ @Override() public String getJobCategoryName() { return "LDAP"; } /** * {@inheritDoc} */ @Override() public ParameterList getParameterStubs() { Parameter[] params = new Parameter[] { placeholder, addressParameter, portParameter, baseDNParameter, bindDNParameter, bindPasswordParameter, placeholder, addFrequencyParameter, compareFrequencyParameter, deleteFrequencyParameter, modifyFrequencyParameter, modifyRDNFrequencyParameter, searchFrequency1Parameter, searchFrequency2Parameter, searchFrequency3Parameter, searchFrequency4Parameter, searchFrequency5Parameter, searchFrequency6Parameter, placeholder, filterFile1URLParameter, filterFile2URLParameter, filterFile3URLParameter, filterFile4URLParameter, filterFile5URLParameter, filterFile6URLParameter, placeholder, attrParameter, placeholder, sizeLimitParameter, timeLimitParameter, warmUpParameter, coolDownParameter, delayParameter, maxRateParameter, rateLimitDurationParameter, opsBetweenBindsParameter, placeholder, useSSLParameter, blindTrustParameter, sslKeyStoreParameter, sslKeyPWParameter, sslTrustStoreParameter, sslTrustPWParameter, placeholder, cleanUpParameter, disconnectParameter, referralsParameter }; return new ParameterList(params); } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackerStubs(String clientID, String threadID, int collectionInterval) { return new StatTracker[] { new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADD_ATTEMPTS, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_COMPARE_ATTEMPTS, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_COMPARE_TIME, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_DELETE_ATTEMPTS, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_DELETE_TIME, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_MODIFY_ATTEMPTS, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_MODIFY_TIME, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_MODIFY_RDN_ATTEMPTS, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_MODIFY_RDN_TIME, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_1, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_1, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_2, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_2, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_3, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_3, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_4, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_4, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_5, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_5, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_6, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_6, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_OPERATION_ATTEMPTS, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_OPERATION_TIME, collectionInterval), new CategoricalTracker(clientID, threadID, STAT_TRACKER_OPERATION_ATTEMPTS_BY_CATEGORY, collectionInterval), new CategoricalTracker(clientID, threadID, STAT_TRACKER_RESULT_CODES, collectionInterval) }; } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackers() { ArrayList<StatTracker> trackerList = new ArrayList<StatTracker>(); if (addCount.getTotalCount() > 0) { trackerList.add(addCount); trackerList.add(addTimer); } if (compareCount.getTotalCount() > 0) { trackerList.add(compareCount); trackerList.add(compareTimer); } if (deleteCount.getTotalCount() > 0) { trackerList.add(deleteCount); trackerList.add(deleteTimer); } if (modifyCount.getTotalCount() > 0) { trackerList.add(modifyCount); trackerList.add(modifyTimer); } if (modifyRDNCount.getTotalCount() > 0) { trackerList.add(modifyRDNCount); trackerList.add(modifyRDNTimer); } if (searchCount1.getTotalCount() > 0) { trackerList.add(searchCount1); trackerList.add(searchTimer1); } if (searchCount2.getTotalCount() > 0) { trackerList.add(searchCount2); trackerList.add(searchTimer2); } if (searchCount3.getTotalCount() > 0) { trackerList.add(searchCount3); trackerList.add(searchTimer3); } if (searchCount4.getTotalCount() > 0) { trackerList.add(searchCount4); trackerList.add(searchTimer4); } if (searchCount5.getTotalCount() > 0) { trackerList.add(searchCount5); trackerList.add(searchTimer5); } if (searchCount6.getTotalCount() > 0) { trackerList.add(searchCount6); trackerList.add(searchTimer6); } // These will always be added, no matter what. trackerList.add(operationCount); trackerList.add(operationTimer); trackerList.add(operationTypes); trackerList.add(resultCodes); StatTracker[] trackerArray = new StatTracker[trackerList.size()]; trackerList.toArray(trackerArray); return trackerArray; } /** * {@inheritDoc} */ @Override() public void validateJobInfo(int numClients, int threadsPerClient, int threadStartupDelay, Date startTime, Date stopTime, int duration, int collectionInterval, ParameterList parameters) throws InvalidValueException { // The bind DN pattern must be a valid value pattern. StringParameter p = parameters.getStringParameter(bindDNParameter.getName()); if ((p != null) && p.hasValue()) { try { new ValuePattern(p.getValue()); } catch (ParseException pe) { throw new InvalidValueException("The value for the '" + p.getDisplayName() + "' parameter is not a valid value " + "pattern: " + pe.getMessage(), pe); } } // Make sure that at least one of the frequency parameters was given a // positive value. IntegerParameter addFreqParam = parameters.getIntegerParameter(addFrequencyParameter.getName()); if ((addFreqParam != null) && (addFreqParam.getIntValue() > 0)) { return; } IntegerParameter compareFreqParam = parameters.getIntegerParameter(compareFrequencyParameter.getName()); if ((compareFreqParam != null) && (compareFreqParam.getIntValue() > 0)) { return; } IntegerParameter deleteFreqParam = parameters.getIntegerParameter(deleteFrequencyParameter.getName()); if ((deleteFreqParam != null) && (deleteFreqParam.getIntValue() > 0)) { return; } IntegerParameter modifyFreqParam = parameters.getIntegerParameter(modifyFrequencyParameter.getName()); if ((modifyFreqParam != null) && (modifyFreqParam.getIntValue() > 0)) { return; } IntegerParameter modifyRDNFreqParam = parameters.getIntegerParameter(modifyRDNFrequencyParameter.getName()); if ((modifyRDNFreqParam != null) && (modifyRDNFreqParam.getIntValue() > 0)) { return; } IntegerParameter searchFreqParam = parameters.getIntegerParameter(searchFrequency1Parameter.getName()); if ((searchFreqParam != null) && (searchFreqParam.getIntValue() > 0)) { return; } searchFreqParam = parameters.getIntegerParameter(searchFrequency2Parameter.getName()); if ((searchFreqParam != null) && (searchFreqParam.getIntValue() > 0)) { return; } searchFreqParam = parameters.getIntegerParameter(searchFrequency3Parameter.getName()); if ((searchFreqParam != null) && (searchFreqParam.getIntValue() > 0)) { return; } searchFreqParam = parameters.getIntegerParameter(searchFrequency4Parameter.getName()); if ((searchFreqParam != null) && (searchFreqParam.getIntValue() > 0)) { return; } searchFreqParam = parameters.getIntegerParameter(searchFrequency5Parameter.getName()); if ((searchFreqParam != null) && (searchFreqParam.getIntValue() > 0)) { return; } searchFreqParam = parameters.getIntegerParameter(searchFrequency6Parameter.getName()); if ((searchFreqParam != null) && (searchFreqParam.getIntValue() > 0)) { return; } throw new InvalidValueException("At least one operation type must have " + "a nonzero frequency."); } /** * {@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(addressParameter.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(bindPasswordParameter.getName()); if ((bindPWParam != null) && bindPWParam.hasValue()) { bindPassword = bindPWParam.getStringValue(); } StringParameter baseDNParam = parameters.getStringParameter(baseDNParameter.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 { if ((bindDN != null) && (bindDN.length() > 0) && (bindPassword != null) && (bindPassword.length() > 0)) { 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 { // Initialize the parent random number generator. parentRandom = new Random(); // Get the address of the directory server. ldapHost = null; addressParameter = parameters.getStringParameter(addressParameter.getName()); if ((addressParameter != null) && addressParameter.hasValue()) { ldapHost = addressParameter.getStringValue(); } // Get the port for the directory server. ldapPort = 389; portParameter = parameters.getIntegerParameter(portParameter.getName()); if ((portParameter != null) && portParameter.hasValue()) { ldapPort = portParameter.getIntValue(); } // Get the base DN. baseDN = null; baseDNParameter = parameters.getStringParameter(baseDNParameter.getName()); if ((baseDNParameter != null) && baseDNParameter.hasValue()) { baseDN = baseDNParameter.getStringValue(); } // Get the bind DN. try { bindDNPattern = new ValuePattern(""); bindDNParameter = parameters.getStringParameter(bindDNParameter.getName()); if ((bindDNParameter != null) && bindDNParameter.hasValue()) { bindDNPattern = new ValuePattern(bindDNParameter.getStringValue()); } } catch (Exception e) { throw new UnableToRunException("Could not parse bind DN pattern: " + stackTraceToString(e), e); } // Get the bind password. bindPW = ""; bindPasswordParameter = parameters.getPasswordParameter(bindPasswordParameter.getName()); if ((bindPasswordParameter != null) && bindPasswordParameter.hasValue()) { bindPW = bindPasswordParameter.getStringValue(); } // Get the add frequency. addFrequency = 0; addFrequencyParameter = parameters.getIntegerParameter(addFrequencyParameter.getName()); if ((addFrequencyParameter != null) && addFrequencyParameter.hasValue()) { addFrequency = addFrequencyParameter.getIntValue(); } // Get the compare frequency. compareFrequency = 0; compareFrequencyParameter = parameters.getIntegerParameter(compareFrequencyParameter.getName()); if ((compareFrequencyParameter != null) && compareFrequencyParameter.hasValue()) { compareFrequency = compareFrequencyParameter.getIntValue(); } // Get the delete frequency. deleteFrequency = 0; deleteFrequencyParameter = parameters.getIntegerParameter(deleteFrequencyParameter.getName()); if ((deleteFrequencyParameter != null) && deleteFrequencyParameter.hasValue()) { deleteFrequency = deleteFrequencyParameter.getIntValue(); } // Get the modify frequency. modifyFrequency = 0; modifyFrequencyParameter = parameters.getIntegerParameter(modifyFrequencyParameter.getName()); if ((modifyFrequencyParameter != null) && modifyFrequencyParameter.hasValue()) { modifyFrequency = modifyFrequencyParameter.getIntValue(); } // Get the modify RDN frequency. modifyRDNFrequency = 0; modifyRDNFrequencyParameter = parameters.getIntegerParameter(modifyRDNFrequencyParameter.getName()); if ((modifyRDNFrequencyParameter != null) && modifyRDNFrequencyParameter.hasValue()) { modifyRDNFrequency = modifyRDNFrequencyParameter.getIntValue(); } // Get the search frequency. searchFrequency1 = 0; searchFrequency1Parameter = parameters.getIntegerParameter(searchFrequency1Parameter.getName()); if ((searchFrequency1Parameter != null) && searchFrequency1Parameter.hasValue()) { searchFrequency1 = searchFrequency1Parameter.getIntValue(); } // Get the search frequency. searchFrequency2 = 0; searchFrequency2Parameter = parameters.getIntegerParameter(searchFrequency2Parameter.getName()); if ((searchFrequency2Parameter != null) && searchFrequency2Parameter.hasValue()) { searchFrequency2 = searchFrequency2Parameter.getIntValue(); } // Get the search frequency. searchFrequency3 = 0; searchFrequency3Parameter = parameters.getIntegerParameter(searchFrequency3Parameter.getName()); if ((searchFrequency3Parameter != null) && searchFrequency3Parameter.hasValue()) { searchFrequency3 = searchFrequency3Parameter.getIntValue(); } // Get the search frequency. searchFrequency4 = 0; searchFrequency4Parameter = parameters.getIntegerParameter(searchFrequency4Parameter.getName()); if ((searchFrequency4Parameter != null) && searchFrequency4Parameter.hasValue()) { searchFrequency4 = searchFrequency4Parameter.getIntValue(); } // Get the search frequency. searchFrequency5 = 0; searchFrequency5Parameter = parameters.getIntegerParameter(searchFrequency5Parameter.getName()); if ((searchFrequency5Parameter != null) && searchFrequency5Parameter.hasValue()) { searchFrequency5 = searchFrequency5Parameter.getIntValue(); } // Get the search frequency. searchFrequency6 = 0; searchFrequency6Parameter = parameters.getIntegerParameter(searchFrequency6Parameter.getName()); if ((searchFrequency6Parameter != null) && searchFrequency6Parameter.hasValue()) { searchFrequency6 = searchFrequency6Parameter.getIntValue(); } // Calculate the total of all the frequencies and create the frequency array totalFrequency = addFrequency + compareFrequency + deleteFrequency + modifyFrequency + modifyRDNFrequency + searchFrequency1 + searchFrequency2 + searchFrequency3 + searchFrequency4 + searchFrequency5 + searchFrequency6; opWeights = new int[11]; opWeights[0] = addFrequency; opWeights[1] = opWeights[0] + compareFrequency; opWeights[2] = opWeights[1] + deleteFrequency; opWeights[3] = opWeights[2] + modifyFrequency; opWeights[4] = opWeights[3] + modifyRDNFrequency; opWeights[5] = opWeights[4] + searchFrequency1; opWeights[6] = opWeights[5] + searchFrequency2; opWeights[7] = opWeights[6] + searchFrequency3; opWeights[8] = opWeights[7] + searchFrequency4; opWeights[9] = opWeights[8] + searchFrequency5; opWeights[10] = opWeights[9] + searchFrequency6; // Get the list of filters to use. searchFilters1 = new String[0]; filterFile1URLParameter = parameters.getFileURLParameter(filterFile1URLParameter.getName()); if ((filterFile1URLParameter != null) && (filterFile1URLParameter.hasValue())) { try { searchFilters1 = filterFile1URLParameter.getNonBlankFileLines(); } catch (Exception e) { throw new UnableToRunException("Unable to retrieve filter list 1: " + e, e); } } // Get the list of filters to use. searchFilters2 = new String[0]; filterFile2URLParameter = parameters.getFileURLParameter(filterFile2URLParameter.getName()); if ((filterFile2URLParameter != null) && (filterFile2URLParameter.hasValue())) { try { searchFilters2 = filterFile2URLParameter.getNonBlankFileLines(); } catch (Exception e) { throw new UnableToRunException("Unable to retrieve filter list 2: " + e, e); } } // Get the list of filters to use. searchFilters3 = new String[0]; filterFile3URLParameter = parameters.getFileURLParameter(filterFile3URLParameter.getName()); if ((filterFile3URLParameter != null) && (filterFile3URLParameter.hasValue())) { try { searchFilters3 = filterFile3URLParameter.getNonBlankFileLines(); } catch (Exception e) { throw new UnableToRunException("Unable to retrieve filter list 3: " + e, e); } } // Get the list of filters to use. searchFilters4 = new String[0]; filterFile4URLParameter = parameters.getFileURLParameter(filterFile4URLParameter.getName()); if ((filterFile4URLParameter != null) && (filterFile4URLParameter.hasValue())) { try { searchFilters4 = filterFile4URLParameter.getNonBlankFileLines(); } catch (Exception e) { throw new UnableToRunException("Unable to retrieve filter list 4: " + e, e); } } // Get the list of filters to use. searchFilters5 = new String[0]; filterFile5URLParameter = parameters.getFileURLParameter(filterFile5URLParameter.getName()); if ((filterFile5URLParameter != null) && (filterFile5URLParameter.hasValue())) { try { searchFilters5 = filterFile5URLParameter.getNonBlankFileLines(); } catch (Exception e) { throw new UnableToRunException("Unable to retrieve filter list 5: " + e, e); } } // Get the list of filters to use. searchFilters6 = new String[0]; filterFile6URLParameter = parameters.getFileURLParameter(filterFile6URLParameter.getName()); if ((filterFile6URLParameter != null) && (filterFile6URLParameter.hasValue())) { try { searchFilters6 = filterFile6URLParameter.getNonBlankFileLines(); } catch (Exception e) { throw new UnableToRunException("Unable to retrieve filter list 6: " + e, e); } } // Get the attribute to compare/modify attrParameter = parameters.getStringParameter(attrParameter.getName()); if ((attrParameter != null) && attrParameter.hasValue()) { modAttr = attrParameter.getStringValue(); } // Get the size limit sizeLimit = 0; sizeLimitParameter = parameters.getIntegerParameter(sizeLimitParameter.getName()); if ((sizeLimitParameter != null) && sizeLimitParameter.hasValue()) { sizeLimit = sizeLimitParameter.getIntValue(); } // Get the time limit timeLimit = 0; timeLimitParameter = parameters.getIntegerParameter(timeLimitParameter.getName()); if ((timeLimitParameter != null) && timeLimitParameter.hasValue()) { timeLimit = timeLimitParameter.getIntValue(); } // Get the warm-up time. warmUpTime = 0; warmUpParameter = parameters.getIntegerParameter(warmUpParameter.getName()); if ((warmUpParameter != null) && warmUpParameter.hasValue()) { warmUpTime = warmUpParameter.getIntValue(); } // Get the cool-down time. coolDownTime = 0; coolDownParameter = parameters.getIntegerParameter(coolDownParameter.getName()); if ((coolDownParameter != null) && coolDownParameter.hasValue()) { coolDownTime = coolDownParameter.getIntValue(); } // Get the time between requests operationDelay = 0; delayParameter = parameters.getIntegerParameter(delayParameter.getName()); if ((delayParameter != null) && delayParameter.hasValue()) { operationDelay = 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 number of operations between rebinds. opsBetweenBinds = 0; opsBetweenBindsParameter = parameters.getIntegerParameter(opsBetweenBindsParameter.getName()); if ((opsBetweenBindsParameter != null) && (opsBetweenBindsParameter.hasValue())) { opsBetweenBinds = opsBetweenBindsParameter.getIntValue(); } // Get the use SSL flag useSSL = false; useSSLParameter = parameters.getBooleanParameter(useSSLParameter.getName()); if (useSSLParameter != null) { useSSL = useSSLParameter.getBooleanValue(); } // Determine whether to perform cleanup after the job is done. cleanUp = true; cleanUpParameter = parameters.getBooleanParameter(cleanUpParameter.getName()); if (cleanUpParameter != null) { cleanUp = cleanUpParameter.getBooleanValue(); } // Get the always disconnect flag alwaysDisconnect = false; disconnectParameter = parameters.getBooleanParameter(disconnectParameter.getName()); if (disconnectParameter != null) { alwaysDisconnect = disconnectParameter.getBooleanValue(); } // Get the follow referrals flag followReferrals = false; referralsParameter = parameters.getBooleanParameter(referralsParameter.getName()); if (referralsParameter != null) { followReferrals = referralsParameter.getBooleanValue(); } // Make sure that the list of DNs to delete is empty. addedDNs.clear(); dnsToDelete = 0; } /** * {@inheritDoc} */ @Override() public void initializeThread(String clientID, String threadID, int collectionInterval, ParameterList parameters) throws UnableToRunException { // Create all the stat trackers. addCount = new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADD_ATTEMPTS, collectionInterval); compareCount = new IncrementalTracker(clientID, threadID, STAT_TRACKER_COMPARE_ATTEMPTS, collectionInterval); deleteCount = new IncrementalTracker(clientID, threadID, STAT_TRACKER_DELETE_ATTEMPTS, collectionInterval); modifyCount = new IncrementalTracker(clientID, threadID, STAT_TRACKER_MODIFY_ATTEMPTS, collectionInterval); modifyRDNCount = new IncrementalTracker(clientID, threadID, STAT_TRACKER_MODIFY_RDN_ATTEMPTS, collectionInterval); searchCount1 = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_1, collectionInterval); searchCount2 = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_2, collectionInterval); searchCount3 = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_3, collectionInterval); searchCount4 = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_4, collectionInterval); searchCount5 = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_5, collectionInterval); searchCount6 = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCH_ATTEMPTS_6, collectionInterval); operationCount = new IncrementalTracker(clientID, threadID, STAT_TRACKER_OPERATION_ATTEMPTS, collectionInterval); addTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_TIME, collectionInterval); compareTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_COMPARE_TIME, collectionInterval); deleteTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_DELETE_TIME, collectionInterval); modifyTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_MODIFY_TIME, collectionInterval); modifyRDNTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_MODIFY_RDN_TIME, collectionInterval); searchTimer1 = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_1, collectionInterval); searchTimer2 = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_2, collectionInterval); searchTimer3 = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_3, collectionInterval); searchTimer4 = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_4, collectionInterval); searchTimer5 = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_5, collectionInterval); searchTimer6 = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_TIME_6, collectionInterval); operationTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_OPERATION_TIME, collectionInterval); resultCodes = new CategoricalTracker(clientID, threadID, STAT_TRACKER_RESULT_CODES, collectionInterval); operationTypes = new CategoricalTracker(clientID, threadID, STAT_TRACKER_OPERATION_ATTEMPTS_BY_CATEGORY, collectionInterval); // Enable real-time reporting of the data for these stat trackers. RealTimeStatReporter statReporter = getStatReporter(); if (statReporter != null) { String jobID = getJobID(); addCount.enableRealTimeStats(statReporter, jobID); compareCount.enableRealTimeStats(statReporter, jobID); deleteCount.enableRealTimeStats(statReporter, jobID); modifyCount.enableRealTimeStats(statReporter, jobID); modifyRDNCount.enableRealTimeStats(statReporter, jobID); searchCount1.enableRealTimeStats(statReporter, jobID); searchCount2.enableRealTimeStats(statReporter, jobID); searchCount3.enableRealTimeStats(statReporter, jobID); searchCount4.enableRealTimeStats(statReporter, jobID); searchCount5.enableRealTimeStats(statReporter, jobID); searchCount6.enableRealTimeStats(statReporter, jobID); operationCount.enableRealTimeStats(statReporter, jobID); addTimer.enableRealTimeStats(statReporter, jobID); compareTimer.enableRealTimeStats(statReporter, jobID); deleteTimer.enableRealTimeStats(statReporter, jobID); modifyTimer.enableRealTimeStats(statReporter, jobID); modifyRDNTimer.enableRealTimeStats(statReporter, jobID); searchTimer1.enableRealTimeStats(statReporter, jobID); searchTimer2.enableRealTimeStats(statReporter, jobID); searchTimer3.enableRealTimeStats(statReporter, jobID); searchTimer4.enableRealTimeStats(statReporter, jobID); searchTimer5.enableRealTimeStats(statReporter, jobID); searchTimer6.enableRealTimeStats(statReporter, jobID); operationTimer.enableRealTimeStats(statReporter, jobID); } // Create the random number generator for this thread random = new Random(parentRandom.nextLong()); // If SSL will be used, then establish an SSL-based connection to the // directory server. When using JSSE, the first connection always takes // longer than the subsequent connections, so we want to get that out of the // way before the job actually starts running. if (useSSL) { try { SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); conn = new LDAPConnection(sslUtil.createSSLSocketFactory()); conn.connect(ldapHost, ldapPort, 10000); conn.close(); } catch (Exception e) { throw new UnableToRunException("Unable to establish an SSL-based " + "connection to the directory: " + e, e); } } else { conn = new LDAPConnection(); } } /** * {@inheritDoc} */ @Override() public void runJob() { // Determine the range of time for which we should collect statistics. long currentTime = System.currentTimeMillis(); collectingStats = false; long startCollectingTime = currentTime + (1000 * warmUpTime); long stopCollectingTime = Long.MAX_VALUE; if ((coolDownTime > 0) && (getShouldStopTime() > 0)) { stopCollectingTime = getShouldStopTime() - (1000 * coolDownTime); } // Set a variable that we can use to determine if the connection is alive boolean bound = false; boolean connected = false; currentBindDN = null; int currentOpNumber = 0; // Create a loop that will run for the appropriate length of time. while (! shouldStop()) { if (rateLimiter != null) { if (rateLimiter.await()) { continue; } } long opStartTime = System.currentTimeMillis(); if ((! collectingStats) && (opStartTime >= startCollectingTime) && (opStartTime < stopCollectingTime)) { // Tell the stat trackers that they should start tracking now addCount.startTracker(); compareCount.startTracker(); deleteCount.startTracker(); modifyCount.startTracker(); modifyRDNCount.startTracker(); searchCount1.startTracker(); searchCount2.startTracker(); searchCount3.startTracker(); searchCount4.startTracker(); searchCount5.startTracker(); searchCount6.startTracker(); operationCount.startTracker(); addTimer.startTracker(); compareTimer.startTracker(); deleteTimer.startTracker(); modifyTimer.startTracker(); modifyRDNTimer.startTracker(); searchTimer1.startTracker(); searchTimer2.startTracker(); searchTimer3.startTracker(); searchTimer4.startTracker(); searchTimer5.startTracker(); searchTimer6.startTracker(); operationTimer.startTracker(); operationTypes.startTracker(); resultCodes.startTracker(); collectingStats = true; } else if ((collectingStats) && (opStartTime >= stopCollectingTime)) { addCount.stopTracker(); compareCount.stopTracker(); deleteCount.stopTracker(); modifyCount.stopTracker(); modifyRDNCount.stopTracker(); searchCount1.stopTracker(); searchCount2.stopTracker(); searchCount3.stopTracker(); searchCount4.stopTracker(); searchCount5.stopTracker(); searchCount6.stopTracker(); operationCount.stopTracker(); addTimer.stopTracker(); compareTimer.stopTracker(); deleteTimer.stopTracker(); modifyTimer.stopTracker(); modifyRDNTimer.stopTracker(); searchTimer1.stopTracker(); searchTimer2.stopTracker(); searchTimer3.stopTracker(); searchTimer4.stopTracker(); searchTimer5.stopTracker(); searchTimer6.stopTracker(); operationTimer.stopTracker(); operationTypes.stopTracker(); resultCodes.stopTracker(); collectingStats = false; } // If the connection is currently not connected, then establish it if (! connected) { try { currentBindDN = bindDNPattern.nextValue(); conn.connect(ldapHost, ldapPort, 10000); if ((currentBindDN != null) && (currentBindDN.length() > 0) && (bindPW != null) && (bindPW.length() > 0)) { conn.bind(currentBindDN, bindPW); } bound = true; connected = true; LDAPConnectionOptions opts = conn.getConnectionOptions(); opts.setFollowReferrals(followReferrals); opts.setResponseTimeoutMillis(1000L * timeLimit); conn.setConnectionOptions(opts); } catch (Exception e) { logMessage("ERROR -- Could not connect to " + ldapHost + ':' + ldapPort + " (" + e + ") -- aborting thread"); if (collectingStats) { if (e instanceof LDAPException) { LDAPException le = (LDAPException) e; resultCodes.increment(String.valueOf(le.getResultCode())); } else { resultCodes.increment(String.valueOf(ResultCode.CONNECT_ERROR)); } } indicateStoppedDueToError(); break; } } else if (! bound) { try { currentBindDN = bindDNPattern.nextValue(); if ((currentBindDN != null) && (currentBindDN.length() > 0) && (bindPW != null) && (bindPW.length() > 0)) { conn.bind(currentBindDN, bindPW); } bound = true; connected = true; } catch (Exception e) { if (collectingStats) { if (e instanceof LDAPException) { LDAPException le = (LDAPException) e; resultCodes.increment(String.valueOf(le.getResultCode())); } else { resultCodes.increment(String.valueOf(ResultCode.CONNECT_ERROR)); } } } } // Pick the type of operation to perform and then do it. ResultCode resultCode; int opType = (random.nextInt() & 0x7FFFFFFF) % totalFrequency; if (collectingStats) { operationCount.increment(); operationTimer.startTimer(); } currentOpNumber++; if (opType < opWeights[0]) { if (collectingStats) { operationTypes.increment("Add"); } resultCode = doAdd(); } else if (opType < opWeights[1]) { if (collectingStats) { operationTypes.increment("Compare"); } resultCode = doCompare(); } else if (opType < opWeights[2]) { if (collectingStats) { operationTypes.increment("Delete"); } resultCode = doDelete(); } else if (opType < opWeights[3]) { if (collectingStats) { operationTypes.increment("Modify"); } resultCode = doModify(); } else if (opType < opWeights[4]) { if (collectingStats) { operationTypes.increment("Modify RDN"); } resultCode = doModifyRDN(); } else if (opType < opWeights[5]) { if (collectingStats) { operationTypes.increment("Search 1"); } resultCode = doSearch1(); } else if (opType < opWeights[6]) { if (collectingStats) { operationTypes.increment("Search 2"); } resultCode = doSearch2(); } else if (opType < opWeights[7]) { if (collectingStats) { operationTypes.increment("Search 3"); } resultCode = doSearch3(); } else if (opType < opWeights[8]) { if (collectingStats) { operationTypes.increment("Search 4"); } resultCode = doSearch4(); } else if (opType < opWeights[9]) { if (collectingStats) { operationTypes.increment("Search 5"); } resultCode = doSearch5(); } else { if (collectingStats) { operationTypes.increment("Search 6"); } resultCode = doSearch6(); } if (collectingStats) { operationTimer.stopTimer(); resultCodes.increment(String.valueOf(resultCode)); } // See if we need to close the connection to the directory. if ((opsBetweenBinds > 0) && (currentOpNumber >= opsBetweenBinds)) { if (alwaysDisconnect) { try { conn.close(); } catch (Exception e) {} bound = false; connected = false; } else { bound = false; } currentOpNumber = 0; } // See if we need to sleep until the next operation. if (operationDelay > 0) { long opTime = System.currentTimeMillis() - opStartTime; if ((opTime < operationDelay) && (! shouldStop())) { try { Thread.sleep(operationDelay - opTime); } catch (InterruptedException ie) {} } } } // If we are still collecting statistics, then stop. if (collectingStats) { addCount.stopTracker(); compareCount.stopTracker(); deleteCount.stopTracker(); modifyCount.stopTracker(); modifyRDNCount.stopTracker(); searchCount1.stopTracker(); searchCount2.stopTracker(); searchCount3.stopTracker(); searchCount4.stopTracker(); searchCount5.stopTracker(); searchCount6.stopTracker(); operationCount.stopTracker(); addTimer.stopTracker(); compareTimer.stopTracker(); deleteTimer.stopTracker(); modifyTimer.stopTracker(); modifyRDNTimer.stopTracker(); searchTimer1.stopTracker(); searchTimer2.stopTracker(); searchTimer3.stopTracker(); searchTimer4.stopTracker(); searchTimer5.stopTracker(); searchTimer6.stopTracker(); operationTimer.stopTracker(); operationTypes.stopTracker(); resultCodes.stopTracker(); } // If we are still connected to the directory, then disconnect. try { conn.close(); } catch (Exception e) {} } /** * {@inheritDoc} */ @Override() public void finalizeClient() { if (cleanUp) { if (useSSL) { try { SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); conn = new LDAPConnection(sslUtil.createSSLSocketFactory()); } catch (Exception e) { logMessage("Unable to establish an SSL-based connection to the " + "directory to perform cleanup: " + e); return; } } else { conn = new LDAPConnection(); } try { conn.connect(ldapHost, ldapPort, 10000); String bindDN = bindDNPattern.nextValue(); if ((bindDN != null) && (bindDN.length() > 0) && (bindPW != null) && (bindPW.length() > 0)) { conn.bind(bindDN, bindPW); } } catch (Exception e) { logMessage("Unable to establish a connection to the directory to " + "perform cleanup: " + e); return; } while (! addedDNs.isEmpty()) { String dnToDelete = addedDNs.removeFirst(); try { conn.delete(dnToDelete); } catch (LDAPException le) { logMessage("Unable to perform cleanup -- exception thrown while " + "trying to delete entry \"" + dnToDelete + "\": " + le); try { conn.close(); } catch (Exception e) {} return; } } try { conn.close(); } catch (Exception e) {} } } /** * {@inheritDoc} */ @Override() public void destroyThread() { if (conn != null) { try { conn.close(); } catch (Exception e) {} conn = null; } } /** * Performs an add operation in the directory. * * @return The result code of the add operation. */ private ResultCode doAdd() { ResultCode resultCode = ResultCode.SUCCESS; Entry entry = getEntry(); String entryDN = entry.getDN(); if (collectingStats) { addCount.increment(); addTimer.startTimer(); } try { conn.add(entry); addDNToDelete(entryDN); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { addTimer.stopTimer(); } return resultCode; } /** * Performs a compare operation in the directory. * * @return The result code of the compare operation. */ private ResultCode doCompare() { ResultCode resultCode; if (collectingStats) { compareCount.increment(); compareTimer.startTimer(); } try { LDAPResult result = conn.compare(currentBindDN, modAttr, getRandomString(80)); resultCode = result.getResultCode(); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { compareTimer.stopTimer(); } return resultCode; } /** * Performs a delete operation in the directory. * * @return The result code of the delete operation. */ private ResultCode doDelete() { ResultCode resultCode = ResultCode.SUCCESS; String dn = getDNToDelete(); if (dn == null) { return ResultCode.PARAM_ERROR; } if (collectingStats) { deleteCount.increment(); deleteTimer.startTimer(); } try { conn.delete(dn); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { deleteTimer.stopTimer(); } return resultCode; } /** * Performs a modify operation in the directory. * * @return The result code of the modify operation. */ private ResultCode doModify() { ResultCode resultCode = ResultCode.SUCCESS; String dn = currentBindDN; Modification mod = new Modification(ModificationType.REPLACE, modAttr, getRandomString(80)); if (collectingStats) { modifyCount.increment(); modifyTimer.startTimer(); } try { conn.modify(dn, mod); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { modifyTimer.stopTimer(); } return resultCode; } /** * Performs a modify RDN (i.e., rename) operation in the directory. * * @return The result code of the modify RDN operation. */ private ResultCode doModifyRDN() { ResultCode resultCode = ResultCode.SUCCESS; String dn = getDNToDelete(); String newRDNValue = getRandomString(80); if (dn == null) { return ResultCode.PARAM_ERROR; } if (collectingStats) { modifyRDNCount.increment(); modifyRDNTimer.startTimer(); } try { conn.modifyDN(dn, "uid=" + newRDNValue, true); addDNToDelete("uid=" + newRDNValue + ',' + baseDN); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { modifyRDNTimer.stopTimer(); } return resultCode; } /** * Performs a search operation in the directory. * * @return The result code of the search operation. */ private ResultCode doSearch1() { ResultCode resultCode = ResultCode.SUCCESS; String filter = searchFilters1[(random.nextInt() & 0x7FFFFFFF) % searchFilters1.length]; if (collectingStats) { searchCount1.increment(); searchTimer1.startTimer(); } try { SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter); searchRequest.setSizeLimit(sizeLimit); searchRequest.setTimeLimitSeconds(timeLimit); conn.search(searchRequest); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { searchTimer1.stopTimer(); } return resultCode; } /** * Performs a search operation in the directory. * * @return The result code of the search operation. */ private ResultCode doSearch2() { ResultCode resultCode = ResultCode.SUCCESS; String filter = searchFilters2[(random.nextInt() & 0x7FFFFFFF) % searchFilters2.length]; if (collectingStats) { searchCount2.increment(); searchTimer2.startTimer(); } try { SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter); searchRequest.setSizeLimit(sizeLimit); searchRequest.setTimeLimitSeconds(timeLimit); conn.search(searchRequest); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { searchTimer2.stopTimer(); } return resultCode; } /** * Performs a search operation in the directory. * * @return The result code of the search operation. */ private ResultCode doSearch3() { ResultCode resultCode = ResultCode.SUCCESS; String filter = searchFilters3[(random.nextInt() & 0x7FFFFFFF) % searchFilters3.length]; if (collectingStats) { searchCount3.increment(); searchTimer3.startTimer(); } try { SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter); searchRequest.setSizeLimit(sizeLimit); searchRequest.setTimeLimitSeconds(timeLimit); conn.search(searchRequest); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { searchTimer3.stopTimer(); } return resultCode; } /** * Performs a search operation in the directory. * * @return The result code of the search operation. */ private ResultCode doSearch4() { ResultCode resultCode = ResultCode.SUCCESS; String filter = searchFilters4[(random.nextInt() & 0x7FFFFFFF) % searchFilters4.length]; if (collectingStats) { searchCount4.increment(); searchTimer4.startTimer(); } try { SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter); searchRequest.setSizeLimit(sizeLimit); searchRequest.setTimeLimitSeconds(timeLimit); conn.search(searchRequest); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { searchTimer4.stopTimer(); } return resultCode; } /** * Performs a search operation in the directory. * * @return The result code of the search operation. */ private ResultCode doSearch5() { ResultCode resultCode = ResultCode.SUCCESS; String filter = searchFilters5[(random.nextInt() & 0x7FFFFFFF) % searchFilters5.length]; if (collectingStats) { searchCount5.increment(); searchTimer5.startTimer(); } try { SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter); searchRequest.setSizeLimit(sizeLimit); searchRequest.setTimeLimitSeconds(timeLimit); conn.search(searchRequest); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { searchTimer5.stopTimer(); } return resultCode; } /** * Performs a search operation in the directory. * * @return The result code of the search operation. */ private ResultCode doSearch6() { ResultCode resultCode = ResultCode.SUCCESS; String filter = searchFilters6[(random.nextInt() & 0x7FFFFFFF) % searchFilters6.length]; if (collectingStats) { searchCount6.increment(); searchTimer6.startTimer(); } try { SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter); searchRequest.setSizeLimit(sizeLimit); searchRequest.setTimeLimitSeconds(timeLimit); conn.search(searchRequest); } catch (LDAPException le) { resultCode = le.getResultCode(); } if (collectingStats) { searchTimer6.stopTimer(); } return resultCode; } /** * Retrieves a randomly-generated entry that may be added to the directory. * * @return A randomly-generated entry that may be added to the directory. */ private Entry getEntry() { String randomString = getRandomString(80); return new Entry("uid=" + randomString + ',' + baseDN, new Attribute("objectClass", "top", "person", "organizationalPerson", "inetOrgPerson"), new Attribute("uid", randomString), new Attribute("givenName", randomString), new Attribute("sn", randomString), new Attribute("cn", randomString), new Attribute("userPassword", randomString)); } /** * Retrieves a randomly-generated string with the specified number of * characters. * * @param length The number of characters to include in the string. * * @return The randomly-generated string. */ private String getRandomString(int length) { char[] returnChars = new char[length]; for (int i=0; i < returnChars.length; i++) { returnChars[i] = ALPHABET[(random.nextInt() & 0x7FFFFFFF) % ALPHABET.length]; } return new String(returnChars); } /** * Adds the specified DN to the list of DNs to be deleted and/or renamed. * * @param entryDN The DN to be added to the list. */ private static void addDNToDelete(String entryDN) { synchronized (addedDNMutex) { dnsToDelete++; addedDNs.add(entryDN); } } /** * Retrieves the DN of an entry to be deleted or renamed. Delete and modify * RDN operations will only be performed on entries that have been added. * * @return The DN of an entry that can be deleted or renamed. If there are * no available DNs, then <CODE>null</CODE> will be returned. */ private static String getDNToDelete() { synchronized (addedDNMutex) { if (dnsToDelete > 0) { dnsToDelete--; return addedDNs.removeFirst(); } } return null; } }