/* * 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.dslogplay; import java.util.ArrayList; import java.util.Date; import java.util.Random; import com.unboundid.ldap.sdk.LDAPConnection; 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.IntegerParameter; import com.slamd.parameter.InvalidValueException; import com.slamd.parameter.MultiChoiceParameter; 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.StatTracker; import com.slamd.stat.TimeTracker; /** * This class defines a SLAMD job that can be used to replay requests parsed * from one or more Netscape / iPlanet / Sun ONE Directory Server access log * files. It can be used to replay various kinds of operations either in their * original order or at random. * * * @author Neil A. Wilson */ public class LogPlaybackJobClass extends JobClass { /** * The display name for the stat tracker used to count the total number of * operations of all types replayed. */ public static final String STAT_TRACKER_TOTAL_REPLAYED = "Total Operations Replayed"; /** * The display name for the stat tracker used to count the total number of add * operations replayed. */ public static final String STAT_TRACKER_ADDS_REPLAYED = "Add Operations Replayed"; /** * The display name for the stat tracker used to count the total number of * bind operations replayed. */ public static final String STAT_TRACKER_BINDS_REPLAYED = "Bind Operations Replayed"; /** * The display name for the stat tracker used to count the total number of * compare operations replayed. */ public static final String STAT_TRACKER_COMPARES_REPLAYED = "Compare Operations Replayed"; /** * The display name for the stat tracker used to count the total number of * delete operations replayed. */ public static final String STAT_TRACKER_DELETES_REPLAYED = "Delete Operations Replayed"; /** * The display name for the stat tracker used to count the total number of * modify operations replayed. */ public static final String STAT_TRACKER_MODS_REPLAYED = "Modify Operations Replayed"; /** * The display name for the stat tracker used to count the total number of * search operations replayed. */ public static final String STAT_TRACKER_SEARCHES_REPLAYED = "Search Operations Replayed"; /** * The display name for the stat tracker used to track the average duration * for all operations replayed. */ public static final String STAT_TRACKER_TOTAL_DURATION = "Overall Operation Duration (ms)"; /** * The display name for the stat tracker used to track the average duration * for add operations. */ public static final String STAT_TRACKER_ADD_DURATION = "Add Operation Duration (ms)"; /** * The display name for the stat tracker used to track the average duration * for bind operations. */ public static final String STAT_TRACKER_BIND_DURATION = "Bind Operation Duration (ms)"; /** * The display name for the stat tracker used to track the average duration * for compare operations. */ public static final String STAT_TRACKER_COMPARE_DURATION = "Compare Operation Duration (ms)"; /** * The display name for the stat tracker used to track the average duration * for delete operations. */ public static final String STAT_TRACKER_DELETE_DURATION = "Delete Operation Duration (ms)"; /** * The display name for the stat tracker used to track the average duration * for modify operations. */ public static final String STAT_TRACKER_MODIFY_DURATION = "Modify Operation Duration (ms)"; /** * The display name for the stat tracker used to track the average duration * for search operations. */ public static final String STAT_TRACKER_SEARCH_DURATION = "Search Operation Duration (ms)"; /** * The display name for the stat tracker used to track the ratio of operations * replayed from the log file. */ public static final String STAT_TRACKER_OPERATION_RATIOS = "Operation Ratios"; /** * The display name for the stat tracker used to track the result codes for * the operations replayed. */ public static final String STAT_TRACKER_RESULT_CODES = "Operation Result Codes"; /** * The replay operation type that indicates that the operations should be * replayed in random order. */ public static final String REPLAY_TYPE_RANDOM = "Replay in Random Order"; /** * The replay operation type that indicates that the operations should be * replayed once in the order they appear in the log file. */ public static final String REPLAY_TYPE_SEQUENTIAL_ONCE = "Replay Once in Sequential Order"; /** * The replay operation type that indicates that the operations should be * replayed in the order they appear in the log file and that the sequence * should repeat when the end is reached. */ public static final String REPLAY_TYPE_SEQUENTIAL_REPEATED = "Replay in Sequential Order Repeatedly"; /** * The set of options that may be used to control how the operations are * replayed. */ public static final String[] REPLAY_TYPES = new String[] { REPLAY_TYPE_RANDOM, REPLAY_TYPE_SEQUENTIAL_ONCE, REPLAY_TYPE_SEQUENTIAL_REPEATED }; /** * The set of characters to include in randomly-generated values. */ public static final char[] ALPHABET = "abcdefghijklmnopqrstuvwxyz".toCharArray(); // The parameter indicating whether to add an entry if an attempt to delete // it indicates that it doesn't exist. private BooleanParameter addMissingDeletesParameter = new BooleanParameter("add_missing_deletes", "Add Missing Entries on Delete Attempts", "Indicates whether attempts to delete an entry " + "that does not exist should cause that entry to " + "be added.", true); // The parameter indicating whether to perform a delete when attempting to add // an entry that already exists. private BooleanParameter deleteExistingAddsParameter = new BooleanParameter("delete_existing_add", "Delete If Add Already Exists", "Indicates whether an attempt to add an entry " + "that already exists should delete that existing " + "entry instead.", true); // The parameter indicating whether to replay adds. private BooleanParameter replayAddParameter = new BooleanParameter("replay_adds", "Replay Add Operations", "Indicates whether the job should replay add " + "operations contained in the access log.", false); // The parameter indicating whether to replay binds. private BooleanParameter replayBindParameter = new BooleanParameter("replay_binds", "Replay Bind Operations", "Indicates whether the job should replay bind " + "operations contained in the access log.", false); // The parameter indicating whether to replay compares. private BooleanParameter replayCompareParameter = new BooleanParameter("replay_compares", "Replay Compare Operations", "Indicates whether the job should replay compare " + "operations contained in the access log.", false); // The parameter indicating whether to replay deletes. private BooleanParameter replayDeleteParameter = new BooleanParameter("replay_deletes", "Replay Delete Operations", "Indicates whether the job should replay delete " + "operations contained in the access log.", false); // The parameter indicating whether to replay modifies. private BooleanParameter replayModifyParameter = new BooleanParameter("replay_modifies", "Replay Modify Operations", "Indicates whether the job should replay modify " + "operations contained in the access log.", false); // The parameter indicating whether to replay searches. private BooleanParameter replaySearchParameter = new BooleanParameter("replay_searches", "Replay Search Operations", "Indicates whether the job should replay search " + "operations contained in the access log.", false); // The parameter indicating whether to communicate with the server over SSL. private BooleanParameter useSSLParameter = new BooleanParameter("use_ssl", "Connect Using SSL", "Indicates whether to connect to the directory " + "server using SSL.", false); // The parameter specifying the number of operations between disconnects. private IntegerParameter opsBetweenDisconnectsParameter = new IntegerParameter("ops_between_disconnects", "Operations Between Disconnects", "Specifies the number of requests to process " + "before disconnecting from and re-connecting to " + "the directory server. A value of zero " + "indicates that all connections should be " + "persistent.", true, 0, true, 0, false, 0); // The parameter specifying the port of the directory server. private IntegerParameter portParameter = new IntegerParameter("port", "Directory Server Port", "The port of the directory server to which the " + "connections should be established.", true, 389, true, 1, true, 65535); // The parameter specifying the length of time between requests. private IntegerParameter timeBetweenRequestsParameter = new IntegerParameter("time_between_requests", "Time Between Requests (ms)", "The length of time in milliseconds that should " + "elapse between the beginning of one request and " + "the beginning of the next. If a request takes " + "longer to process than this time, then there " + "will be no delay before the next request.", true, 0, true, 0, false, 0); // The parameter specifying the order for the replayed operations. private MultiChoiceParameter replayOrderParameter = new MultiChoiceParameter("replay_order", "Replay Order", "The order in which to replay the operations " + "from the log files.", REPLAY_TYPES, REPLAY_TYPE_RANDOM); // The parameter specifying the paths to the log files containing the requests // to replay. private MultiLineTextParameter logFilesParameter = new MultiLineTextParameter("log_files", "Access Log Files to Replay", "The path(s) to the access log file(s) to " + "be replayed against the directory server.", null, true); // The parameter specifying the password to use to bind to the server. private PasswordParameter bindPasswordParameter = new PasswordParameter("bind_password", "Directory Server Bind Password", "The password to use to bind to the directory " + "server for replaying all operations other than " + "binds.", false, ""); // The parameter specifying the password to use for bind operations. private PasswordParameter bindOperationPasswordParameter = new PasswordParameter("bind_operation_password", "Password for Bind Operations", "The password to use when replaying bind " + "operations.", false, ""); // The placeholder used for spacing the parameters. private PlaceholderParameter placeholder = new PlaceholderParameter(); // The parameter specifying the address of the directory server. private StringParameter addressParameter = new StringParameter("address", "Directory Server Address", "The address of the directory server to which the " + "connections should be established.", true, ""); // The parameter specifying the DN to use to bind to the server. private StringParameter bindDNParameter = new StringParameter("bind_dn", "Directory Server Bind DN", "The DN to use to bind to the directory server " + "for replaying all operations other than binds.", false, ""); // The parameter specifying the attribute to replace for modify operations. private StringParameter modifyAttributeParameter = new StringParameter("modify_attr", "Attribute to Modify", "The name of the attribute to replace when " + "replaying modify operations.", true, "description"); // The static class variables that correspond to the parameter values. protected static boolean addMissingDeletes; protected static boolean deleteExistingAdds; protected static boolean doBind; protected static boolean randomOrder; protected static boolean repeatOrder; protected static boolean replayAdds; protected static boolean replayBinds; protected static boolean replayCompares; protected static boolean replayDeletes; protected static boolean replayModifies; protected static boolean replaySearches; protected static boolean useSSL; protected static int opsBetweenDisconnects; protected static int port; protected static int timeBetweenRequests; protected static String address; protected static String bindDN; protected static String bindPassword; protected static String bindOperationPassword; protected static String modifyAttribute; protected static String[] logFiles; // The information about the operations to replay. private static int nextOperation; private static LogOperation[] operationsToReplay; // The stat trackers used for this thread. protected CategoricalTracker opRatios; protected CategoricalTracker resultCodes; protected IncrementalTracker addsReplayed; protected IncrementalTracker bindsReplayed; protected IncrementalTracker comparesReplayed; protected IncrementalTracker deletesReplayed; protected IncrementalTracker modifiesReplayed; protected IncrementalTracker searchesReplayed; protected IncrementalTracker totalReplayed; protected TimeTracker addTimer; protected TimeTracker bindTimer; protected TimeTracker compareTimer; protected TimeTracker deleteTimer; protected TimeTracker modifyTimer; protected TimeTracker searchTimer; protected TimeTracker totalTimer; // The connections to the directory server to use for bind operations and all // other operations, respectively. protected LDAPConnection bindConnection; protected LDAPConnection opConnection; // The random number generators used by this job. private static Random parentRandom; private Random random; // The thread that will be interrupted if we need to forcefully stop the job. private Thread jobThread; /** * The default constructor used to create a new instance of the job class. * The only thing it should do is to invoke the superclass constructor. All * other initialization should be performed in the <CODE>initialize</CODE> * method. */ public LogPlaybackJobClass() { super(); logFilesParameter.setVisibleRows(5); logFilesParameter.setVisibleColumns(80); jobThread = null; } /** * {@inheritDoc} */ @Override() public String getJobName() { return "Directory Server Log Playback"; } /** * {@inheritDoc} */ @Override() public String getShortDescription() { return "Replay operations from a Sun DSEE access log file"; } /** * {@inheritDoc} */ @Override() public String[] getLongDescription() { return new String[] { "This job can be used to replay operations from one or more Sun Java " + "System Directory Server access log files. It was originally written " + "to work with DSEE 5.2 and may or may not work with log files " + "generated by other server versions." }; } /** * {@inheritDoc} */ @Override() public String getJobCategoryName() { return "LDAP"; } /** * {@inheritDoc} */ @Override() public ParameterList getParameterStubs() { Parameter[] parameters = new Parameter[] { placeholder, addressParameter, portParameter, useSSLParameter, bindDNParameter, bindPasswordParameter, placeholder, logFilesParameter, replayOrderParameter, placeholder, replayAddParameter, replayBindParameter, replayCompareParameter, replayDeleteParameter, replayModifyParameter, replaySearchParameter, placeholder, bindOperationPasswordParameter, modifyAttributeParameter, addMissingDeletesParameter, deleteExistingAddsParameter, opsBetweenDisconnectsParameter, timeBetweenRequestsParameter }; return new ParameterList(parameters); } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackerStubs(String clientID, String threadID, int collectionInterval) { return new StatTracker[] { new IncrementalTracker(clientID, threadID, STAT_TRACKER_TOTAL_REPLAYED, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADDS_REPLAYED, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_BINDS_REPLAYED, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_COMPARES_REPLAYED, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_DELETES_REPLAYED, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_MODS_REPLAYED, collectionInterval), new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCHES_REPLAYED, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_TOTAL_DURATION, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_DURATION, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_BIND_DURATION, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_COMPARE_DURATION, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_DELETE_DURATION, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_MODIFY_DURATION, collectionInterval), new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_DURATION, collectionInterval), new CategoricalTracker(clientID, threadID, STAT_TRACKER_RESULT_CODES, collectionInterval), new CategoricalTracker(clientID, threadID, STAT_TRACKER_OPERATION_RATIOS, collectionInterval) }; } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackers() { return new StatTracker[] { totalReplayed, addsReplayed, bindsReplayed, comparesReplayed, deletesReplayed, modifiesReplayed, searchesReplayed, totalTimer, addTimer, bindTimer, compareTimer, deleteTimer, modifyTimer, searchTimer, resultCodes, opRatios }; } /** * {@inheritDoc} */ @Override() public void validateJobInfo(int numClients, int threadsPerClient, int threadStartupDelay, Date startTime, Date stopTime, int duration, int collectionInterval, ParameterList parameters) throws InvalidValueException { // Make sure that at least one type of operation was selected. boolean replayEnabled = false; boolean replayWrites = false; boolean replayBinds = false; boolean replayMods = false; BooleanParameter replayAddParam = parameters.getBooleanParameter(replayAddParameter.getName()); if ((replayAddParam != null) && replayAddParam.getBooleanValue()) { replayEnabled = true; replayWrites = true; } BooleanParameter replayBindParam = parameters.getBooleanParameter(replayBindParameter.getName()); if ((replayBindParam != null) && replayBindParam.getBooleanValue()) { replayEnabled = true; replayBinds = true; } BooleanParameter replayCompareParam = parameters.getBooleanParameter(replayCompareParameter.getName()); if ((replayCompareParam != null) && replayCompareParam.getBooleanValue()) { replayEnabled = true; } BooleanParameter replayDeleteParam = parameters.getBooleanParameter(replayDeleteParameter.getName()); if ((replayDeleteParam != null) && replayDeleteParam.getBooleanValue()) { replayEnabled = true; replayWrites = true; } BooleanParameter replayModifyParam = parameters.getBooleanParameter(replayModifyParameter.getName()); if ((replayModifyParam != null) && replayModifyParam.getBooleanValue()) { replayEnabled = true; replayWrites = true; replayMods = true; } BooleanParameter replaySearchParam = parameters.getBooleanParameter(replaySearchParameter.getName()); if ((replaySearchParam != null) && replaySearchParam.getBooleanValue()) { replayEnabled = true; } if (! replayEnabled) { throw new InvalidValueException("At least one type of operation to " + "replay must be selected."); } // If any write operations were selected, make sure that a bind DN and // password were provided. if (replayWrites) { String bindDN = null; String bindPW = null; StringParameter bindDNParam = parameters.getStringParameter(bindDNParameter.getName()); if ((bindDNParam != null) && bindDNParam.hasValue()) { bindDN = bindDNParam.getStringValue(); } PasswordParameter bindPWParam = parameters.getPasswordParameter(bindPasswordParameter.getName()); if ((bindPWParam != null) && bindPWParam.hasValue()) { bindPW = bindPWParam.getStringValue(); } if ((bindDN == null) || (bindPW == null)) { throw new InvalidValueException("A bind DN and password must be " + "provided if any write operations " + "are to be replayed."); } } // If bind operations were selected, then make sure that a bind operation // password was provided. if (replayBinds) { PasswordParameter bindOpPWParam = parameters.getPasswordParameter( bindOperationPasswordParameter.getName()); if ((bindOpPWParam == null) || (! bindOpPWParam.hasValue())) { throw new InvalidValueException("A bind operation password must be " + "provided if bind operations are to " + "be replayed."); } } // If modify operations were selected, then make sure that a modify // attribute was provided. if (replayMods) { StringParameter modAttrParam = parameters.getStringParameter(modifyAttributeParameter.getName()); if ((modAttrParam == null) || (! modAttrParam.hasValue())) { throw new InvalidValueException("A modify attribute must be provided " + "if modify operations are to be " + "replayed."); } } } /** * {@inheritDoc} */ @Override() public boolean providesParameterTest() { return true; } /** * {@inheritDoc} */ @Override() public boolean testJobParameters(ParameterList parameters, ArrayList<String> outputMessages) { // Get the necessary parameter values to connect to the server. boolean useSSL = false; int port = 389; String address = null; String bindDN = null; String bindPW = null; StringParameter addressParam = parameters.getStringParameter(addressParameter.getName()); if ((addressParam != null) && addressParam.hasValue()) { address = addressParam.getStringValue(); } else { outputMessages.add("ERROR: No directory server address was specified."); return false; } IntegerParameter portParam = parameters.getIntegerParameter(portParameter.getName()); if ((portParam != null) && portParam.hasValue()) { port = portParam.getIntValue(); } else { outputMessages.add("ERROR: No directory server port was specified."); return false; } BooleanParameter sslParam = parameters.getBooleanParameter(useSSLParameter.getName()); if (sslParam != null) { useSSL = sslParam.getBooleanValue(); } StringParameter dnParam = parameters.getStringParameter(bindDNParameter.getName()); if ((dnParam != null) && (dnParam.hasValue())) { bindDN = dnParam.getStringValue(); } PasswordParameter pwParam = parameters.getPasswordParameter(bindPasswordParameter.getName()); if ((pwParam != null) && (pwParam.hasValue())) { bindPW = pwParam.getStringValue(); } boolean doBind = false; if (bindDN == null) { if (bindPW != null) { outputMessages.add("WARNING: A bind password was provided but no " + "bind DN. No authentication will be performed."); outputMessages.add(""); } } else { if (bindPW == null) { outputMessages.add("WARNING: A bind DN was provided but no " + "password. No authentication will be performed."); outputMessages.add(""); } else { doBind = true; } } // Initialize the LDAP connection. LDAPConnection conn; if (useSSL) { try { outputMessages.add("Going to perform SSL initialization."); SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); conn = new LDAPConnection(sslUtil.createSSLSocketFactory()); outputMessages.add("SSL initialization completed successfully."); outputMessages.add(""); } catch (Exception e) { outputMessages.add("ERROR: SSL initialization failed: " + e); return false; } } else { conn = new LDAPConnection(); } // Connect to the directory server and optionally bind. if (doBind) { try { outputMessages.add("Going to connect to " + address + ':' + port + " as " + bindDN); conn.connect(address, port, 10000); conn.bind(bindDN, bindPW); outputMessages.add("Connected successfully."); outputMessages.add(""); } catch (Exception e) { outputMessages.add("Connection attempt failed: " + e); return false; } } else { try { outputMessages.add("Going to connect to " + address + ':' + port); conn.connect(address, port, 10000); outputMessages.add("Connected successfully."); outputMessages.add(""); } catch (Exception e) { outputMessages.add("Connection attempt failed: " + e); return false; } } // Disconnect from the server and indicate that everything was successful. 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 information needed to connect to the server. addressParameter = parameters.getStringParameter(addressParameter.getName()); if (addressParameter != null) { address = addressParameter.getStringValue(); } portParameter = parameters.getIntegerParameter(portParameter.getName()); if (portParameter != null) { port = portParameter.getIntValue(); } useSSL = false; useSSLParameter = parameters.getBooleanParameter(useSSLParameter.getName()); if (useSSLParameter != null) { useSSL = useSSLParameter.getBooleanValue(); } bindDN = null; bindDNParameter = parameters.getStringParameter(bindDNParameter.getName()); if ((bindDNParameter != null) && bindDNParameter.hasValue()) { bindDN = bindDNParameter.getStringValue(); } bindPassword = null; bindPasswordParameter = parameters.getPasswordParameter(bindPasswordParameter.getName()); if ((bindPasswordParameter != null) && bindPasswordParameter.hasValue()) { bindPassword = bindPasswordParameter.getStringValue(); } doBind = ((bindDN != null) && (bindPassword != null)); // Get the information about the log files to replay. logFiles = new String[0]; logFilesParameter = parameters.getMultiLineTextParameter(logFilesParameter.getName()); if (logFilesParameter != null) { logFiles = logFilesParameter.getNonBlankLines(); } randomOrder = true; repeatOrder = true; nextOperation = -1; replayOrderParameter = parameters.getMultiChoiceParameter(replayOrderParameter.getName()); if (replayOrderParameter != null) { String orderStr = replayOrderParameter.getStringValue(); if (orderStr.equals(REPLAY_TYPE_SEQUENTIAL_ONCE)) { randomOrder = false; repeatOrder = false; } else if (orderStr.equals(REPLAY_TYPE_SEQUENTIAL_REPEATED)) { randomOrder = false; repeatOrder = true; } } // Get the information about the types of operations to replay. replayAdds = false; replayAddParameter = parameters.getBooleanParameter(replayAddParameter.getName()); if (replayAddParameter != null) { replayAdds = replayAddParameter.getBooleanValue(); } replayBinds = false; replayBindParameter = parameters.getBooleanParameter(replayBindParameter.getName()); if (replayBindParameter != null) { replayBinds = replayBindParameter.getBooleanValue(); } replayCompares = false; replayCompareParameter = parameters.getBooleanParameter(replayCompareParameter.getName()); if (replayCompareParameter != null) { replayCompares = replayCompareParameter.getBooleanValue(); } replayDeletes = false; replayDeleteParameter = parameters.getBooleanParameter(replayDeleteParameter.getName()); if (replayDeleteParameter != null) { replayDeletes = replayDeleteParameter.getBooleanValue(); } replayModifies = false; replayModifyParameter = parameters.getBooleanParameter(replayModifyParameter.getName()); if (replayModifyParameter != null) { replayModifies = replayModifyParameter.getBooleanValue(); } replaySearches = false; replaySearchParameter = parameters.getBooleanParameter(replaySearchParameter.getName()); if (replaySearchParameter != null) { replaySearches = replaySearchParameter.getBooleanValue(); } // Get the information needed for specific types of operations. bindOperationPassword = null; bindOperationPasswordParameter = parameters.getPasswordParameter( bindOperationPasswordParameter.getName()); if (bindOperationPasswordParameter != null) { bindOperationPassword = bindOperationPasswordParameter.getStringValue(); } modifyAttribute = "description"; modifyAttributeParameter = parameters.getStringParameter(modifyAttributeParameter.getName()); if (modifyAttributeParameter != null) { modifyAttribute = modifyAttributeParameter.getStringValue(); } addMissingDeletes = false; addMissingDeletesParameter = parameters.getBooleanParameter(addMissingDeletesParameter.getName()); if (addMissingDeletesParameter != null) { addMissingDeletes = addMissingDeletesParameter.getBooleanValue(); } deleteExistingAdds = false; deleteExistingAddsParameter = parameters.getBooleanParameter(deleteExistingAddsParameter.getName()); if (deleteExistingAddsParameter != null) { deleteExistingAdds = deleteExistingAddsParameter.getBooleanValue(); } opsBetweenDisconnects = 0; opsBetweenDisconnectsParameter = parameters.getIntegerParameter( opsBetweenDisconnectsParameter.getName()); if (opsBetweenDisconnectsParameter != null) { opsBetweenDisconnects = opsBetweenDisconnectsParameter.getIntValue(); } timeBetweenRequests = 0; timeBetweenRequestsParameter = parameters.getIntegerParameter(timeBetweenRequestsParameter.getName()); if (timeBetweenRequestsParameter != null) { timeBetweenRequests = timeBetweenRequestsParameter.getIntValue(); } // Parse the specified log files. String currentLog = null; try { LogParser parser = new LogParser(this, replayAdds, replayBinds, replayCompares, replayDeletes, replayModifies, replaySearches); for (int i=0; i < logFiles.length; i++) { currentLog = logFiles[i]; parser.parseLogFile(currentLog); } operationsToReplay = parser.getOperations(); } catch (Exception e) { throw new UnableToRunException("An error occurred while trying to " + "parse the log file \"" + currentLog + "\": " + e, e); } if ((operationsToReplay == null) || (operationsToReplay.length == 0)) { throw new UnableToRunException("The provided log files did not appear " + "to contain any operations to replay."); } } /** * {@inheritDoc} */ @Override() public void initializeThread(String clientID, String threadID, int collectionInterval, ParameterList parameters) throws UnableToRunException { // Initialize the child random number generator. random = new Random(parentRandom.nextLong()); // Initialize all the stat trackers. totalReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_TOTAL_REPLAYED, collectionInterval); addsReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_ADDS_REPLAYED, collectionInterval); bindsReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_BINDS_REPLAYED, collectionInterval); comparesReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_COMPARES_REPLAYED, collectionInterval); deletesReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_DELETES_REPLAYED, collectionInterval); modifiesReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_MODS_REPLAYED, collectionInterval); searchesReplayed = new IncrementalTracker(clientID, threadID, STAT_TRACKER_SEARCHES_REPLAYED, collectionInterval); totalTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_TOTAL_DURATION, collectionInterval); addTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_ADD_DURATION, collectionInterval); bindTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_BIND_DURATION, collectionInterval); compareTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_COMPARE_DURATION, collectionInterval); deleteTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_DELETE_DURATION, collectionInterval); modifyTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_MODIFY_DURATION, collectionInterval); searchTimer = new TimeTracker(clientID, threadID, STAT_TRACKER_SEARCH_DURATION, collectionInterval); resultCodes = new CategoricalTracker(clientID, threadID, STAT_TRACKER_RESULT_CODES, collectionInterval); opRatios = new CategoricalTracker(clientID, threadID, STAT_TRACKER_OPERATION_RATIOS, collectionInterval); // Initialize and establish the connections to the directory server. if (useSSL) { try { SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); bindConnection = new LDAPConnection(sslUtil.createSSLSocketFactory()); opConnection = new LDAPConnection(sslUtil.createSSLSocketFactory()); } catch (Exception e) { throw new UnableToRunException("Unable to perform SSL initialization " + "for LDAP connections: " + e, e); } } else { bindConnection = new LDAPConnection(); opConnection = new LDAPConnection(); } try { if (doBind) { opConnection.connect(address, port, 10000); opConnection.bind(bindDN, bindPassword); } else { opConnection.connect(address, port); } bindConnection.connect(address, port); } catch (Exception e) { throw new UnableToRunException("Unable to establish the connections " + "to the directory server: " + e, e); } } /** * {@inheritDoc} */ @Override() public void runJob() { // Initialize the housekeeping variables. boolean needReconnect = false; int opsPlayed = 0; jobThread = currentThread(); // Start all the stat trackers. opRatios.startTracker(); resultCodes.startTracker(); addsReplayed.startTracker(); bindsReplayed.startTracker(); comparesReplayed.startTracker(); deletesReplayed.startTracker(); modifiesReplayed.startTracker(); searchesReplayed.startTracker(); totalReplayed.startTracker(); addTimer.startTracker(); bindTimer.startTracker(); compareTimer.startTracker(); deleteTimer.startTracker(); modifyTimer.startTracker(); searchTimer.startTracker(); totalTimer.startTracker(); // Loop until it is determined that the job should stop. while (! shouldStop()) { long startTime = System.currentTimeMillis(); // See if we need to reconnect to the server. if (needReconnect) { try { opConnection.close(); } catch (Exception e) {} try { if (doBind) { opConnection.connect(address, port, 10000); opConnection.bind(bindDN, bindPassword); } else { opConnection.connect(address, port); } } catch (Exception e) { logMessage("Unable to re-establish the connection to the " + "directory server: " + e); indicateStoppedDueToError(); break; } } // Get the next operation to perform and replay it. LogOperation op = getNextOperation(); if (op == null) { break; } op.replayOperation(this); // Increment the counter to see if we need to reconnect the next time // through. if (opsBetweenDisconnects > 0) { opsPlayed++; if (opsPlayed > opsBetweenDisconnects) { needReconnect = true; opsPlayed = 0; } } // See if we need to sleep before sending the next request. if (timeBetweenRequests > 0) { long elapsedTime = System.currentTimeMillis() - startTime; long sleepTime = timeBetweenRequests - elapsedTime; if (sleepTime > 0) { try { Thread.sleep(sleepTime); } catch (Exception e) {} } } } // Stop all the stat trackers. opRatios.stopTracker(); resultCodes.stopTracker(); addsReplayed.stopTracker(); bindsReplayed.stopTracker(); comparesReplayed.stopTracker(); deletesReplayed.stopTracker(); modifiesReplayed.stopTracker(); searchesReplayed.stopTracker(); totalReplayed.stopTracker(); addTimer.stopTracker(); bindTimer.stopTracker(); compareTimer.stopTracker(); deleteTimer.stopTracker(); modifyTimer.stopTracker(); searchTimer.stopTracker(); totalTimer.stopTracker(); // Close the connections to the directory server. try { opConnection.close(); } catch (Exception e) {} try { bindConnection.close(); } catch (Exception e) {} } /** * {@inheritDoc} */ @Override() public void destroyThread() { // Close the connections to the directory server. try { bindConnection.close(); } catch (Exception e) {} try { opConnection.close(); } catch (Exception e) {} // Interrupt the job thread. try { jobThread.interrupt(); } catch (Exception e) {} } /** * Retrieves a string containing the specified number of randomly-chosen * characters. * * @param length The number of characters to include in the generated * string. * * @return The generated string. */ public String getRandomString(int length) { Random r; if (random == null) { if (parentRandom == null) { parentRandom = new Random(); } r = parentRandom; } else { r = random; } char[] chars = new char[length]; for (int i=0; i < length; i++) { chars[i] = ALPHABET[r.nextInt(ALPHABET.length)]; } return new String(chars); } /** * Retrieves the next log operation that should be replayed against the * directory server. * * @return The next log operation that should be replayed against the * directory server, or <CODE>null</CODE> if there are no more * operations to replay. */ public LogOperation getNextOperation() { if (randomOrder) { return operationsToReplay[random.nextInt(operationsToReplay.length)]; } else { synchronized (operationsToReplay) { int opPos = ++nextOperation; if (opPos >= operationsToReplay.length) { if (repeatOrder) { opPos = 0; nextOperation = 0; } else { return null; } } return operationsToReplay[opPos]; } } } }