From f93174a06f157fb10b49bb0bf179617943d54710 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Wed, 23 Jul 2014 14:40:02 +0000
Subject: [PATCH] OPENDJ-1074 (CR-4030) Implement combined add/del rate tool Adding an add/del rate tool named addrate. -opendj-core ** EntryGenerator.java: * Changing the subtemplate parsing behavior, add the "generated branches" options ** TemplateFile.java * Adding the "generated branches" options ** addrate.template * New template used to generate entries
---
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java | 45 +
opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/MakeLDIFTestCase.java | 2
opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java | 5
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java | 86 ++-
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java | 6
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java | 6
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java | 10
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunnerOptions.java | 90 +++++
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java | 86 +---
opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java | 429 +++++++++++++++++++++++
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java | 22 +
opendj-sdk/opendj-core/src/main/resources/org/forgerock/opendj/ldif/addrate.template | 32 +
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java | 2
opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bat/addrate | 33 +
opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java | 115 ++++++
opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bin/addrate | 36 ++
opendj-sdk/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties | 45 ++
17 files changed, 924 insertions(+), 126 deletions(-)
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java
index e5d636e..898712e 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java
@@ -111,6 +111,12 @@
*/
private InputStream templateStream;
+ /** Indicates whether branch entries should be generated.
+ *
+ * Default is {@code true}
+ */
+ private boolean generateBranches = true;
+
/** Dictionary of constants to use in the template file. */
private Map<String, String> constants = new HashMap<String, String>();
@@ -222,6 +228,20 @@
}
/**
+ * Sets the flag which indicates whether branch entries should be generated.
+ *
+ * The default is {@code true}.
+ *
+ * @param generateBranches
+ * Indicates whether or not the branches DN entries has to be generated.
+ * @return A reference to this {@code EntryGenerator}.
+ */
+ public EntryGenerator setGenerateBranches(boolean generateBranches) {
+ this.generateBranches = generateBranches;
+ return this;
+ }
+
+ /**
* Checks if there are some warning(s) after parsing the template file.
* <p>
* Warnings are available only after the first call to {@code hasNext()} or
@@ -286,7 +306,7 @@
if (schema == null) {
schema = Schema.getDefaultSchema();
}
- templateFile = new TemplateFile(schema, constants, resourcePath, new Random(randomSeed));
+ templateFile = new TemplateFile(schema, constants, resourcePath, new Random(randomSeed), generateBranches);
try {
if (templatePath != null) {
templateFile.parse(templatePath, warnings);
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java
index 70b743c..cc20e2d 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java
@@ -104,6 +104,9 @@
/** The name of the file holding the list of last names. */
private static final String LAST_NAME_FILE = "last.names";
+ /** Default value for infinite number of entries. */
+ private static final int INFINITE_ENTRIES = -1;
+
/**
* A map of the contents of various text files used during the parsing
* process, mapped from absolute path to the array of lines in the file.
@@ -149,6 +152,9 @@
/** The next last name that should be used. */
private String lastName;
+ /** Indicates whether branch entries should be generated. */
+ private boolean generateBranches;
+
/**
* The resource path to use for filesystem elements that cannot be found
* anywhere else.
@@ -180,7 +186,7 @@
* if a problem occurs when initializing
*/
TemplateFile(Schema schema, Map<String, String> constants, String resourcePath) throws IOException {
- this(schema, constants, resourcePath, new Random());
+ this(schema, constants, resourcePath, new Random(), true);
}
/**
@@ -197,12 +203,16 @@
* {@code null}.
* @param random
* The random number generator for this template file.
+ * @param generateBranches
+ * Indicates whether branch entries should be generated.
* @throws IOException
* if a problem occurs when initializing
*/
- TemplateFile(Schema schema, Map<String, String> constants, String resourcePath, Random random)
+ TemplateFile(Schema schema, Map<String, String> constants, String resourcePath,
+ Random random, boolean generateBranches)
throws IOException {
Reject.ifNull(schema, random);
+ this.generateBranches = generateBranches;
this.schema = schema;
this.constants = constants != null ? constants : new HashMap<String, String>();
this.resourcePath = resourcePath;
@@ -466,7 +476,7 @@
// Finalize the branch and template definitions
// and then update the template file variables.
for (Branch b : templateData.branches.values()) {
- b.completeBranchInitialization(templateData.templates);
+ b.completeBranchInitialization(templateData.templates, generateBranches);
}
for (Template t : templateData.templates.values()) {
@@ -835,30 +845,31 @@
private Pair<String, Integer> parseSubordinateTemplate(final int lineNumber, final String line,
final Element element, final String elementName, final List<LocalizableMessage> warnings)
throws DecodeException {
- // It's a subordinate template, so we'll want to parse
- // the template name and the number of entries.
+ // It's a subordinate template, so we'll want to parse the template name
+ // and the number of entries if it is provided.
final int colonPos = line.indexOf(':', SUBORDINATE_TEMPLATE_LABEL.length());
+ final String templateName;
+ int numEntries = INFINITE_ENTRIES;
+
if (colonPos <= SUBORDINATE_TEMPLATE_LABEL.length()) {
- throw DecodeException.fatalError(ERR_ENTRY_GENERATOR_SUBORDINATE_TEMPLATE_NO_COLON.get(
- lineNumber, element.getLabel(), elementName));
- }
+ //No number of entries provided, generator will provides an infinite number of entries
+ templateName = line.substring(SUBORDINATE_TEMPLATE_LABEL.length(), line.length()).trim();
+ } else {
+ templateName = line.substring(SUBORDINATE_TEMPLATE_LABEL.length(), colonPos).trim();
- final String templateName = line.substring(SUBORDINATE_TEMPLATE_LABEL.length(), colonPos).trim();
-
- try {
- final int numEntries = Integer.parseInt(line.substring(colonPos + 1).trim());
- if (numEntries < 0) {
- throw DecodeException.fatalError(ERR_ENTRY_GENERATOR_SUBORDINATE_INVALID_NUM_ENTRIES.get(
- lineNumber, element.getLabel(), elementName, numEntries, templateName));
- } else if (numEntries == 0) {
- warnings.add(WARN_ENTRY_GENERATOR_SUBORDINATE_ZERO_ENTRIES.get(
- lineNumber, element.getLabel(), elementName, templateName));
+ try {
+ numEntries = Integer.parseInt(line.substring(colonPos + 1).trim());
+ if (numEntries == 0) {
+ warnings.add(WARN_ENTRY_GENERATOR_SUBORDINATE_ZERO_ENTRIES.get(
+ lineNumber, element.getLabel(), elementName, templateName));
+ }
+ } catch (NumberFormatException nfe) {
+ throw DecodeException.fatalError(ERR_ENTRY_GENERATOR_SUBORDINATE_CANT_PARSE_NUMENTRIES.get(
+ templateName, lineNumber, element.getLabel(), elementName));
}
- return Pair.of(templateName, numEntries);
- } catch (NumberFormatException nfe) {
- throw DecodeException.fatalError(ERR_ENTRY_GENERATOR_SUBORDINATE_CANT_PARSE_NUMENTRIES.get(
- templateName, lineNumber, element.getLabel(), elementName));
}
+
+ return Pair.of(templateName, numEntries);
}
private static final int PARSING_STATIC_TEXT = 0;
@@ -1417,14 +1428,9 @@
* initialization is completed. In particular, it should make sure that
* all referenced subordinate templates actually exist in the template
* file.
- *
- * @param templates
- * The set of templates defined in the template file.
- * @throws DecodeException
- * If any of the subordinate templates are not defined in
- * the template file.
*/
- private void completeBranchInitialization(final Map<String, Template> templates) throws DecodeException {
+ private void completeBranchInitialization(final Map<String, Template> templates,
+ boolean generateBranches) throws DecodeException {
subordinateTemplates = new ArrayList<Template>();
for (int i = 0; i < subordinateTemplateNames.size(); i++) {
subordinateTemplates.add(templates.get(subordinateTemplateNames.get(i).toLowerCase()));
@@ -1434,7 +1440,7 @@
}
}
- nextEntry = buildBranchEntry();
+ nextEntry = buildBranchEntry(generateBranches);
}
DN getBranchDN() {
@@ -1491,10 +1497,8 @@
/**
* Returns the entry corresponding to this branch.
- *
- * @return the entry, or null if it can't be generated
*/
- private TemplateEntry buildBranchEntry() {
+ private TemplateEntry buildBranchEntry(boolean generateBranches) {
final TemplateEntry entry = new TemplateEntry(this);
final List<TemplateLine> lines = new ArrayList<TemplateLine>(rdnLines);
lines.addAll(extraLines);
@@ -1504,6 +1508,11 @@
for (int i = 0; i < subordinateTemplates.size(); i++) {
subordinateTemplates.get(i).reset(entry.getDN(), numEntriesPerTemplate.get(i));
}
+
+ if (!generateBranches) {
+ return null;
+ }
+
return entry;
}
@@ -1685,7 +1694,10 @@
/** parent DN of entries to generate for this template. */
private DN parentDN;
- /** Number of entries to generate for this template. */
+ /**
+ * Number of entries to generate for this template.
+ * Negative number means infinite generation.
+ */
private int numberOfEntries;
/** Current count of generated entries for this template. */
@@ -1747,7 +1759,7 @@
if (nextEntry != null) {
return true;
}
- while (entriesCount < numberOfEntries) {
+ while ((entriesCount < numberOfEntries) || generateForever()) {
// get the template entry
if (!currentEntryIsInitialized) {
nextEntry = buildTemplateEntry();
@@ -1773,6 +1785,10 @@
return false;
}
+ private boolean generateForever() {
+ return numberOfEntries < 0;
+ }
+
/**
* Returns the next generated entry.
*
diff --git a/opendj-sdk/opendj-core/src/main/resources/org/forgerock/opendj/ldif/addrate.template b/opendj-sdk/opendj-core/src/main/resources/org/forgerock/opendj/ldif/addrate.template
new file mode 100644
index 0000000..09085e4
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/main/resources/org/forgerock/opendj/ldif/addrate.template
@@ -0,0 +1,32 @@
+define suffix=dc=example,dc=com
+define maildomain=example.com
+
+branch: [suffix]
+
+branch: ou=People,[suffix]
+subordinateTemplate: person
+
+template: person
+rdnAttr: uid
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: <first>
+sn: <last>
+cn: {givenName} {sn}
+initials: {givenName:1}<random:chars:ABCDEFGHIJKLMNOPQRSTUVWXYZ:1>{sn:1}
+employeeNumber: <sequential:0>
+uid: user.{employeeNumber}
+mail: {uid}@[maildomain]
+userPassword: password
+telephoneNumber: <random:telephone>
+homePhone: <random:telephone>
+pager: <random:telephone>
+mobile: <random:telephone>
+street: <random:numeric:5> <file:streets> Street
+l: <file:cities>
+st: <file:states>
+postalCode: <random:numeric:5>
+postalAddress: {cn}${street}${l}, {st} {postalCode}
+description: This is the description for {cn}.
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
index 2b2ecbe..c212fca 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
@@ -410,6 +410,8 @@
// Remove this from the map.
entryMap.remove(dn);
requestsInProgress.remove(context);
+ result = Responses.newResult(ResultCode.SUCCESS);
+ handler.handleResult(result);
}
public <R extends ExtendedResult> void handleExtendedRequest(final Integer context,
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bat/addrate b/opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bat/addrate
new file mode 100644
index 0000000..d6e8d42
--- /dev/null
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bat/addrate
@@ -0,0 +1,33 @@
+
+@echo off
+rem CDDL HEADER START
+rem
+rem The contents of this file are subject to the terms of the
+rem Common Development and Distribution License, Version 1.0 only
+rem (the "License"). You may not use this file except in compliance
+rem with the License.
+rem
+rem You can obtain a copy of the license at
+rem legal-notices/CDDLv1_0.txt
+rem or http://forgerock.org/license/CDDLv1.0.html.
+rem See the License for the specific language governing permissions
+rem and limitations under the License.
+rem
+rem When distributing Covered Code, include this CDDL HEADER in each
+rem file and include the License file at legal-notices/CDDLv1_0.txt.
+rem legal-notices/CDDLv1_0.txt. If applicable,
+rem add the following below this CDDL HEADER, with the fields enclosed
+rem by brackets "[]" replaced with your own identifying information:
+rem Portions Copyright [yyyy] [name of copyright owner]
+rem
+rem CDDL HEADER END
+rem
+rem
+rem Copyright 2014 ForgeRock AS
+
+setlocal
+
+set OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.AddRate"
+set SCRIPT_NAME=addrate
+for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %*
+
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bin/addrate b/opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bin/addrate
new file mode 100644
index 0000000..7975cc1
--- /dev/null
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/assembly/bin/addrate
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at
+# trunk/opendj3/legal-notices/CDDLv1_0.txt
+# or http://forgerock.org/license/CDDLv1.0.html.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at
+# trunk/opendj3/legal-notices/CDDLv1_0.txt. If applicable,
+# add the following below this CDDL HEADER, with the fields enclosed
+# by brackets "[]" replaced with your own identifying information:
+# Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2014 ForgeRock AS
+
+
+OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.AddRate"
+export OPENDJ_INVOKE_CLASS
+
+SCRIPT_NAME="addrate"
+export SCRIPT_NAME
+
+SCRIPT_DIR=`dirname "${0}"`
+"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
\ No newline at end of file
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
new file mode 100644
index 0000000..b7ea17c
--- /dev/null
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
@@ -0,0 +1,429 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2014 ForgeRock AS
+ */
+
+package com.forgerock.opendj.ldap.tools;
+
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.filterExitCode;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.FutureResult;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.DeleteRequest;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.opendj.ldif.EntryGenerator;
+
+import com.forgerock.opendj.cli.ArgumentException;
+import com.forgerock.opendj.cli.ArgumentParser;
+import com.forgerock.opendj.cli.BooleanArgument;
+import com.forgerock.opendj.cli.CommonArguments;
+import com.forgerock.opendj.cli.ConnectionFactoryProvider;
+import com.forgerock.opendj.cli.ConsoleApplication;
+import com.forgerock.opendj.cli.IntegerArgument;
+import com.forgerock.opendj.cli.MultiChoiceArgument;
+import com.forgerock.opendj.cli.StringArgument;
+
+/**
+ * A load generation tool that can be used to load a Directory Server with Add
+ * and Delete requests using one or more LDAP connections.
+ */
+public class AddRate extends ConsoleApplication {
+
+ private static final class AddPerformanceRunner extends PerformanceRunner {
+ private final class AddStatsHandler extends UpdateStatsResultHandler<Result> {
+ private String entryDN;
+
+ private AddStatsHandler(final long currentTime, String entryDN) {
+ super(currentTime);
+ this.entryDN = entryDN;
+ }
+
+ @Override
+ public void handleResult(final Result result) {
+ super.handleResult(result);
+
+ switch (delStrategy) {
+ case RANDOM:
+ long newKey;
+ do {
+ newKey = randomSeq.get().nextInt();
+ } while (dnEntriesAdded.putIfAbsent(newKey, this.entryDN) != null);
+ break;
+ case FIFO:
+ long uniqueTime = currentTime;
+ while (dnEntriesAdded.putIfAbsent(uniqueTime, this.entryDN) != null) {
+ uniqueTime++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ nbAdd.getAndIncrement();
+ }
+ }
+
+ private final class DeleteStatsHandler extends UpdateStatsResultHandler<Result> {
+
+ private DeleteStatsHandler(final long startTime) {
+ super(startTime);
+ }
+
+ @Override
+ public void handleResult(final Result result) {
+ super.handleResult(result);
+ nbDelete.getAndIncrement();
+ }
+ }
+
+ private final class AddRateStatsThread extends StatsThread {
+ private final String[] extraColumn = new String[1];
+
+ private AddRateStatsThread() {
+ super(new String[] { "Add%" });
+ }
+
+ @Override
+ String[] getAdditionalColumns() {
+ final int nbAddStat = nbAdd.getAndSet(0);
+ final int nbDelStat = nbDelete.getAndSet(0);
+ final int total = nbAddStat + nbDelStat;
+
+ extraColumn[0] = String.format("%.2f", total > 0 ? ((double) nbAddStat / total) * 100 : 0.0);
+
+ return extraColumn;
+ }
+ }
+
+ private final class AddWorkerThread extends WorkerThread {
+
+ AddWorkerThread(Connection connection, ConnectionFactory connectionFactory) {
+ super(connection, connectionFactory);
+ }
+
+ @Override
+ public FutureResult<?> performOperation(Connection connection,
+ DataSource[] dataSources, long currentTime) {
+ if (needsDelete(currentTime)) {
+ DeleteRequest dr = Requests.newDeleteRequest(getDNEntryToRemove());
+ return connection.deleteAsync(dr, null, new DeleteStatsHandler(currentTime));
+ } else {
+ return performAddOperation(connection, currentTime);
+ }
+ }
+
+ private FutureResult<?> performAddOperation(Connection connection, long currentTime) {
+ try {
+ Entry entry;
+ synchronized (generator) {
+ entry = generator.readEntry();
+ }
+
+ AddRequest ar = Requests.newAddRequest(entry);
+ return connection
+ .addAsync(ar, null, new AddStatsHandler(currentTime, entry.getName().toString()));
+ } catch (IOException e) {
+ // faking an error result by notifying the Handler
+ UpdateStatsResultHandler<Result> resHandler = new UpdateStatsResultHandler<Result>(currentTime);
+ resHandler.handleErrorResult(ErrorResultException.newErrorResult(ResultCode.OTHER, e));
+ return null;
+ }
+ }
+
+ private boolean needsDelete(final long currentTime) {
+ if (dnEntriesAdded.isEmpty() || delStrategy == DeleteStrategy.OFF) {
+ return false;
+ }
+
+ switch (delThreshold) {
+ case SIZE_THRESHOLD:
+ return dnEntriesAdded.size() > sizeThreshold;
+ case AGE_THRESHOLD:
+ long olderEntryTimestamp = (Long) dnEntriesAdded.firstKey();
+ return (olderEntryTimestamp + timeToWait) < currentTime;
+ default:
+ return false;
+ }
+ }
+
+ private String getDNEntryToRemove() {
+ String removedEntry = null;
+
+ while (removedEntry == null) {
+ long minKey = dnEntriesAdded.firstKey();
+ long maxKey = dnEntriesAdded.lastKey();
+ long randomIndex = Math.round(Math.random() * (maxKey - minKey) + minKey);
+ Long key = dnEntriesAdded.ceilingKey(randomIndex);
+
+ if (key != null) {
+ removedEntry = dnEntriesAdded.remove(key);
+ }
+ }
+
+ return removedEntry;
+ }
+
+ }
+
+ private final ConcurrentSkipListMap<Long, String> dnEntriesAdded =
+ new ConcurrentSkipListMap<Long, String>();
+ private final ThreadLocal<Random> randomSeq = new ThreadLocal<Random>() {
+ @Override
+ protected Random initialValue() {
+ return new Random();
+ }
+ };
+
+ private EntryGenerator generator;
+ private DeleteStrategy delStrategy;
+ private DeleteThreshold delThreshold;
+ private Integer sizeThreshold;
+ private long timeToWait;
+ private final AtomicInteger nbAdd = new AtomicInteger();
+ private final AtomicInteger nbDelete = new AtomicInteger();
+
+ private AddPerformanceRunner(final PerformanceRunnerOptions options) throws ArgumentException {
+ super(options);
+ }
+
+ @Override
+ WorkerThread newWorkerThread(Connection connection, ConnectionFactory connectionFactory) {
+ return new AddWorkerThread(connection, connectionFactory);
+ }
+
+ @Override
+ StatsThread newStatsThread() {
+ return new AddRateStatsThread();
+ }
+
+ public void validate(MultiChoiceArgument<DeleteStrategy> delModeArg, IntegerArgument delSizeThresholdArg,
+ IntegerArgument delAgeThresholdArg) throws ArgumentException {
+ super.validate();
+ delStrategy = delModeArg.getTypedValue();
+ // Check for inconsistent use cases
+ if (delSizeThresholdArg.isPresent() && delAgeThresholdArg.isPresent()) {
+ throw new ArgumentException(ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE.get());
+ } else if (delStrategy == DeleteStrategy.OFF
+ && (delSizeThresholdArg.isPresent() || delAgeThresholdArg.isPresent())) {
+ throw new ArgumentException(ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get());
+ } else if (delStrategy == DeleteStrategy.RANDOM && delAgeThresholdArg.isPresent()) {
+ throw new ArgumentException(ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE.get());
+ }
+
+ if (delStrategy != DeleteStrategy.OFF) {
+ delThreshold =
+ delAgeThresholdArg.isPresent() ? DeleteThreshold.AGE_THRESHOLD : DeleteThreshold.SIZE_THRESHOLD;
+ if (delThreshold == DeleteThreshold.SIZE_THRESHOLD) {
+ sizeThreshold = delSizeThresholdArg.getIntValue();
+ } else {
+ timeToWait = delAgeThresholdArg.getIntValue() * 1000000000L;
+ }
+ }
+ }
+ }
+
+ private enum DeleteStrategy {
+ OFF, RANDOM, FIFO;
+ }
+
+ private enum DeleteThreshold {
+ SIZE_THRESHOLD, AGE_THRESHOLD, OFF;
+ }
+
+ private static final int EXIT_CODE_SUCCESS = 0;
+
+ /**
+ * The main method for AddRate tool.
+ *
+ * @param args
+ * The command-line arguments provided to this program.
+ */
+ public static void main(final String[] args) {
+ final int retCode = new AddRate().run(args);
+ System.exit(filterExitCode(retCode));
+ }
+
+ private BooleanArgument verbose;
+
+ private BooleanArgument scriptFriendly;
+
+ private AddRate() {
+ // Nothing to do
+ }
+
+ AddRate(PrintStream out, PrintStream err) {
+ super(out, err);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isInteractive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isScriptFriendly() {
+ return scriptFriendly.isPresent();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isVerbose() {
+ return verbose.isPresent();
+ }
+
+ int run(final String[] args) {
+ // Create the command-line argument parser for use with this program.
+ final LocalizableMessage toolDescription = INFO_ADDRATE_TOOL_DESCRIPTION.get();
+ final ArgumentParser argParser =
+ new ArgumentParser(AddRate.class.getName(), toolDescription, false, true, 1, 1, "template-file-path");
+
+ ConnectionFactoryProvider connectionFactoryProvider;
+ ConnectionFactory connectionFactory;
+ AddPerformanceRunner runner;
+
+ /* Entries generation parameters */
+ IntegerArgument randomSeedArg;
+ StringArgument resourcePathArg;
+ StringArgument constantsArg;
+
+ /* addrate specifics arguments */
+ MultiChoiceArgument<DeleteStrategy> deleteMode;
+ IntegerArgument deleteSizeThreshold;
+ IntegerArgument deleteAgeThreshold;
+
+ try {
+ Utils.setDefaultPerfToolProperties();
+ PerformanceRunnerOptions options = new PerformanceRunnerOptions(argParser, this);
+ options.setSupportsGeneratorArgument(false);
+
+ connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
+ runner = new AddPerformanceRunner(options);
+
+ addCommonArguments(argParser);
+
+ /* Entries generation parameters */
+ resourcePathArg =
+ new StringArgument("resourcepath", 'r', MakeLDIF.OPTION_LONG_RESOURCE_PATH, false, false, true,
+ INFO_PATH_PLACEHOLDER.get(), null, null, INFO_ADDRATE_DESCRIPTION_RESOURCE_PATH.get());
+ argParser.addArgument(resourcePathArg);
+
+ randomSeedArg =
+ new IntegerArgument("randomseed", 'R', OPTION_LONG_RANDOM_SEED, false, false, true,
+ INFO_SEED_PLACEHOLDER.get(), 0, null, INFO_ADDRATE_DESCRIPTION_SEED.get());
+ argParser.addArgument(randomSeedArg);
+
+ constantsArg =
+ new StringArgument("constant", 'g', MakeLDIF.OPTION_LONG_CONSTANT, false, true, true,
+ INFO_CONSTANT_PLACEHOLDER.get(), null, null, INFO_ADDRATE_DESCRIPTION_CONSTANT.get());
+ argParser.addArgument(constantsArg);
+
+ /* addrate specifics arguments */
+ deleteMode =
+ new MultiChoiceArgument<DeleteStrategy>("deletemode", 'C', "deleteMode", false, true,
+ INFO_DELETEMODE_PLACEHOLDER.get(), Arrays.asList(DeleteStrategy.values()), false,
+ INFO_ADDRATE_DESCRIPTION_DELETEMODE.get());
+ deleteMode.setDefaultValue(DeleteStrategy.FIFO.toString());
+ argParser.addArgument(deleteMode);
+
+ deleteSizeThreshold =
+ new IntegerArgument("deletesizethreshold", 's', "deleteSizeThreshold", false, true,
+ INFO_DELETESIZETHRESHOLD_PLACEHOLDER.get(), INFO_ADDRATE_DESCRIPTION_DELETESIZETHRESHOLD.get());
+ deleteSizeThreshold.setDefaultValue(String.valueOf(10000));
+ argParser.addArgument(deleteSizeThreshold);
+
+ deleteAgeThreshold =
+ new IntegerArgument("deleteagethreshold", 'a', "deleteAgeThreshold", false, true,
+ INFO_DELETEAGETHRESHOLD_PLACEHOLDER.get(), INFO_ADDRATE_DESCRIPTION_DELETEAGETHRESHOLD.get());
+ argParser.addArgument(deleteAgeThreshold);
+ } catch (final ArgumentException ae) {
+ errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
+ return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+ }
+
+ // Parse the command-line arguments provided to this program.
+ try {
+ argParser.parseArguments(args);
+
+ if (argParser.usageOrVersionDisplayed()) {
+ return EXIT_CODE_SUCCESS;
+ }
+
+ connectionFactory = connectionFactoryProvider.getAuthenticatedConnectionFactory();
+ runner.validate(deleteMode, deleteSizeThreshold, deleteAgeThreshold);
+ } catch (final ArgumentException ae) {
+ final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
+ errPrintln(message);
+ errPrintln(argParser.getUsageMessage());
+ return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
+ }
+
+ final String templatePath = argParser.getTrailingArguments().get(0);
+
+ runner.generator =
+ MakeLDIF.createGenerator(templatePath, resourcePathArg, randomSeedArg, constantsArg, false, this);
+
+ return runner.run(connectionFactory);
+ }
+
+ private void addCommonArguments(ArgumentParser argParser) throws ArgumentException {
+ StringArgument propertiesFileArgument = CommonArguments.getPropertiesFile();
+ argParser.addArgument(propertiesFileArgument);
+ argParser.setFilePropertiesArgument(propertiesFileArgument);
+
+ BooleanArgument noPropertiesFileArgument = CommonArguments.getNoPropertiesFile();
+ argParser.addArgument(noPropertiesFileArgument);
+ argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
+
+ BooleanArgument showUsage = CommonArguments.getShowUsage();
+ argParser.addArgument(showUsage);
+ argParser.setUsageArgument(showUsage, getOutputStream());
+
+ verbose = CommonArguments.getVerbose();
+ argParser.addArgument(verbose);
+
+ scriptFriendly =
+ new BooleanArgument("scriptFriendly", 'S', "scriptFriendly", INFO_DESCRIPTION_SCRIPT_FRIENDLY.get());
+ scriptFriendly.setPropertyName("scriptFriendly");
+ argParser.addArgument(scriptFriendly);
+
+ }
+}
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
index 3e81eec..6ba512a 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
@@ -321,9 +321,9 @@
private BindRequest bindRequest;
private int invalidCredPercent;
- private BindPerformanceRunner(final ArgumentParser argParser, final ConsoleApplication app)
+ private BindPerformanceRunner(final PerformanceRunnerOptions options)
throws ArgumentException {
- super(argParser, app, true, true, true);
+ super(options);
}
@Override
@@ -408,9 +408,13 @@
try {
setDefaultPerfToolProperties();
+ PerformanceRunnerOptions options = new PerformanceRunnerOptions(argParser, this);
+ options.setSupportsRebind(false);
+ options.setSupportsAsynchronousRequests(false);
+ options.setSupportsMultipleThreadsPerConnection(false);
connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
- runner = new BindPerformanceRunner(argParser, this);
+ runner = new BindPerformanceRunner(options);
propertiesFileArgument = CommonArguments.getPropertiesFile();
argParser.addArgument(propertiesFileArgument);
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
index 66fccf2..c5b98e3 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
@@ -55,6 +55,16 @@
*/
public final class MakeLDIF extends ConsoleApplication {
+ /**
+ * The value for the constant option in LDIF generator tools.
+ */
+ public static final String OPTION_LONG_CONSTANT = "constant";
+
+ /**
+ * The value for the path to look for LDIF resources (e.g data files).
+ */
+ public static final String OPTION_LONG_RESOURCE_PATH = "resourcePath";
+
private static final int EXIT_CODE_SUCCESS = 0;
private static final int EXIT_CODE_FAILURE = 1;
@@ -84,7 +94,7 @@
StringArgument resourcePath;
StringArgument constants;
try {
- resourcePath = new StringArgument("resourcepath", 'r', "resourcePath", false, false, true,
+ resourcePath = new StringArgument("resourcepath", 'r', OPTION_LONG_RESOURCE_PATH, false, false, true,
INFO_PATH_PLACEHOLDER.get(), null, null, INFO_MAKELDIF_DESCRIPTION_RESOURCE_PATH.get());
argParser.addArgument(resourcePath);
@@ -97,7 +107,7 @@
false, true, INFO_SEED_PLACEHOLDER.get(), 0, null, INFO_MAKELDIF_DESCRIPTION_SEED.get());
argParser.addArgument(randomSeed);
- constants = new StringArgument("constant", 'c', "constant", false, true, true,
+ constants = new StringArgument("constant", 'c', OPTION_LONG_CONSTANT, false, true, true,
INFO_CONSTANT_PLACEHOLDER.get(),
null, null, INFO_MAKELDIF_DESCRIPTION_CONSTANT.get());
argParser.addArgument(constants);
@@ -168,17 +178,15 @@
return EXIT_CODE_SUCCESS;
}
- /**
- * Returns the initialized generator, or null if a problem occurs.
- */
- private EntryGenerator createGenerator(final String templatePath, final StringArgument resourcePath,
- final IntegerArgument randomSeedArg, final StringArgument constants) {
- final EntryGenerator generator = new EntryGenerator(templatePath);
+ static EntryGenerator createGenerator(final String templatePath, final StringArgument resourcePath,
+ final IntegerArgument randomSeedArg, final StringArgument constants,
+ final boolean generateBranches, final ConsoleApplication app) {
+ final EntryGenerator generator = new EntryGenerator(templatePath).setGenerateBranches(generateBranches);
if (resourcePath.isPresent()) {
final File resourceDir = new File(resourcePath.getValue());
if (!resourceDir.exists()) {
- errPrintln(ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY.get(resourcePath.getValue()));
+ app.errPrintln(ERR_LDIF_GEN_TOOL_NO_SUCH_RESOURCE_DIRECTORY.get(resourcePath.getValue()));
generator.close();
return null;
}
@@ -189,14 +197,14 @@
try {
generator.setRandomSeed(randomSeedArg.getIntValue());
} catch (ArgumentException ae) {
- errPrintln(ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
+ app.errPrintln(ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
generator.close();
return null;
}
}
if (constants.isPresent()) {
- if (!addConstantsToGenerator(constants, generator)) {
+ if (!addConstantsToGenerator(constants, generator, app)) {
generator.close();
return null;
}
@@ -206,7 +214,7 @@
try {
generator.hasNext();
} catch (IOException e) {
- errPrintln(ERR_MAKELDIF_EXCEPTION_DURING_PARSE.get(e.getMessage()));
+ app.errPrintln(ERR_LDIF_GEN_TOOL_EXCEPTION_DURING_PARSE.get(e.getMessage()));
generator.close();
return null;
}
@@ -217,11 +225,12 @@
/**
* Returns true if all constants are added to generator, false otherwise.
*/
- private boolean addConstantsToGenerator(StringArgument constants, EntryGenerator generator) {
+ private static boolean addConstantsToGenerator(StringArgument constants, EntryGenerator generator,
+ final ConsoleApplication app) {
for (final String constant : constants.getValues()) {
final String[] chunks = constant.split("=");
if (chunks.length != 2) {
- errPrintln(ERR_CONSTANT_ARG_CANNOT_DECODE.get(constant));
+ app.errPrintln(ERR_CONSTANT_ARG_CANNOT_DECODE.get(constant));
return false;
}
generator.setConstant(chunks[0], chunks[1]);
@@ -229,8 +238,14 @@
return true;
}
+
+ private EntryGenerator createGenerator(final String templatePath, final StringArgument resourcePath,
+ final IntegerArgument randomSeedArg, final StringArgument constants) {
+ return createGenerator(templatePath, resourcePath, randomSeedArg, constants, true, this);
+ }
+
/**
- * Returns true if generation is successfull, false otherwise.
+ * Returns true if generation is successful, false otherwise.
*/
private boolean generateEntries(final EntryGenerator generator, final LDIFEntryWriter writer,
final StringArgument ldifFile) {
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
index 419a736..bea207d 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
@@ -102,9 +102,9 @@
private String baseDN;
private String[] modStrings;
- private ModifyPerformanceRunner(final ArgumentParser argParser, final ConsoleApplication app)
+ private ModifyPerformanceRunner(final PerformanceRunnerOptions options)
throws ArgumentException {
- super(argParser, app, false, false, false);
+ super(options);
}
@Override
@@ -175,7 +175,7 @@
Utils.setDefaultPerfToolProperties();
connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
- runner = new ModifyPerformanceRunner(argParser, this);
+ runner = new ModifyPerformanceRunner(new PerformanceRunnerOptions(argParser, this));
propertiesFileArgument = CommonArguments.getPropertiesFile();
argParser.addArgument(propertiesFileArgument);
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
index 3fd70e3..8398959 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
@@ -28,6 +28,7 @@
package com.forgerock.opendj.ldap.tools;
import static org.forgerock.util.Utils.closeSilently;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
import java.io.IOException;
import java.lang.management.GarbageCollectorMXBean;
@@ -70,39 +71,22 @@
*/
class StatsThread extends Thread {
private final String[] additionalColumns;
-
private final List<GarbageCollectorMXBean> beans;
-
private final Set<Double> percentiles;
-
private final int numColumns;
-
private ReversableArray etimes = new ReversableArray(100000);
-
private final ReversableArray array = new ReversableArray(200000);
-
protected long totalSuccessCount;
-
protected long totalOperationCount;
-
protected long totalFailedCount;
-
protected long totalWaitTime;
-
protected int successCount;
-
protected int operationCount;
-
protected int failedCount;
-
protected long waitTime;
-
protected long lastStatTime;
-
protected long lastGCDuration;
-
protected double recentDuration;
-
protected double averageDuration;
public StatsThread(final String[] additionalColumns) {
@@ -351,10 +335,10 @@
* The type of expected result.
*/
class UpdateStatsResultHandler<S extends Result> implements ResultHandler<S> {
- private final long startTime;
+ protected final long currentTime;
- UpdateStatsResultHandler(final long startTime) {
- this.startTime = startTime;
+ UpdateStatsResultHandler(final long currentTime) {
+ this.currentTime = currentTime;
}
@Override
@@ -371,7 +355,7 @@
}
private void updateStats() {
- final long eTime = System.nanoTime() - startTime;
+ final long eTime = System.nanoTime() - currentTime;
waitRecentTime.getAndAdd(eTime);
synchronized (this) {
final ReversableArray array = eTimeBuffer.get();
@@ -451,7 +435,9 @@
count++;
if (!isAsync) {
try {
- future.get();
+ if (future != null) {
+ future.get();
+ }
} catch (final InterruptedException e) {
// Ignore and check stop requested
continue;
@@ -603,20 +589,15 @@
}
private static final String[] EMPTY_STRINGS = new String[0];
-
private final AtomicInteger operationRecentCount = new AtomicInteger();
-
protected final AtomicInteger successRecentCount = new AtomicInteger();
-
protected final AtomicInteger failedRecentCount = new AtomicInteger();
-
private final AtomicLong waitRecentTime = new AtomicLong();
private final AtomicReference<ReversableArray> eTimeBuffer =
new AtomicReference<ReversableArray>(new ReversableArray(100000));
private final ConsoleApplication app;
-
private DataSource[] dataSourcePrototypes;
// Thread local copies of the data sources
@@ -638,51 +619,34 @@
};
private volatile boolean stopRequested;
-
private int numThreads;
-
private int numConnections;
-
private int targetThroughput;
-
private int maxIterations;
-
private boolean isAsync;
-
private boolean noRebind;
-
private int statsInterval;
-
private final IntegerArgument numThreadsArgument;
-
private final IntegerArgument maxIterationsArgument;
-
private final IntegerArgument statsIntervalArgument;
-
private final IntegerArgument targetThroughputArgument;
-
private final IntegerArgument numConnectionsArgument;
-
private final IntegerArgument percentilesArgument;
-
private final BooleanArgument keepConnectionsOpen;
-
private final BooleanArgument noRebindArgument;
-
private final BooleanArgument asyncArgument;
-
private final StringArgument arguments;
- PerformanceRunner(final ArgumentParser argParser, final ConsoleApplication app,
- final boolean neverRebind, final boolean neverAsynchronous,
- final boolean alwaysSingleThreaded) throws ArgumentException {
- this.app = app;
+ PerformanceRunner(final PerformanceRunnerOptions options) throws ArgumentException {
+ ArgumentParser argParser = options.getArgumentParser();
+
+ this.app = options.getConsoleApplication();
numThreadsArgument =
new IntegerArgument("numThreads", 't', "numThreads", false, false, true,
LocalizableMessage.raw("{numThreads}"), 1, null, true, 1, false, 0,
LocalizableMessage.raw("Number of worker threads per connection"));
numThreadsArgument.setPropertyName("numThreads");
- if (!alwaysSingleThreaded) {
+ if (options.supportsMultipleThreadsPerConnection()) {
argParser.addArgument(numThreadsArgument);
} else {
numThreadsArgument.addValue("1");
@@ -735,7 +699,7 @@
new BooleanArgument("noRebind", 'F', "noRebind", LocalizableMessage
.raw("Keep connections open and don't rebind"));
noRebindArgument.setPropertyName("noRebind");
- if (!neverRebind) {
+ if (options.supportsRebind()) {
argParser.addArgument(noRebindArgument);
}
@@ -744,7 +708,7 @@
.raw("Use asynchronous mode and don't "
+ "wait for results before sending the next request"));
asyncArgument.setPropertyName("asynchronous");
- if (!neverAsynchronous) {
+ if (options.supportsAsynchronousRequests()) {
argParser.addArgument(asyncArgument);
}
@@ -767,7 +731,9 @@
+ "arguments, they can be generated per iteration with the "
+ "following functions: " + StaticUtils.EOL
+ DataSource.getUsage()));
- argParser.addArgument(arguments);
+ if (options.supportsGeneratorArgument()) {
+ argParser.addArgument(arguments);
+ }
}
@Override
@@ -804,15 +770,19 @@
noRebind = noRebindArgument.isPresent();
if (!noRebindArgument.isPresent() && this.numThreads > 1) {
- throw new ArgumentException(LocalizableMessage.raw("--"
- + noRebindArgument.getLongIdentifier() + " must be used if --"
- + numThreadsArgument.getLongIdentifier() + " is > 1"));
+ throw new ArgumentException(ERR_TOOL_ARG_MUST_BE_USED_WHEN_ARG_CONDITION.get(
+ "--" + noRebindArgument.getLongIdentifier(), "--" + numThreadsArgument.getLongIdentifier(), "> 1"));
}
if (!noRebindArgument.isPresent() && asyncArgument.isPresent()) {
- throw new ArgumentException(LocalizableMessage.raw("--"
- + noRebindArgument.getLongIdentifier() + " must be used when using --"
- + asyncArgument.getLongIdentifier()));
+ throw new ArgumentException(ERR_TOOL_ARG_NEEDED_WHEN_USING_ARG.get(
+ "--" + noRebindArgument.getLongIdentifier(), asyncArgument.getLongIdentifier()));
+ }
+
+ if (maxIterationsArgument.isPresent() && maxIterations <= 0) {
+ throw new ArgumentException(ERR_TOOL_NOT_ENOUGH_ITERATIONS.get(
+ "--" + maxIterationsArgument.getLongIdentifier(), numConnections * numThreads,
+ numConnectionsArgument.getLongIdentifier(), numThreadsArgument.getLongIdentifier()));
}
dataSourcePrototypes = DataSource.parse(arguments.getValues());
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunnerOptions.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunnerOptions.java
new file mode 100644
index 0000000..9497c26
--- /dev/null
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunnerOptions.java
@@ -0,0 +1,90 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2014 ForgeRock AS
+ */
+
+package com.forgerock.opendj.ldap.tools;
+
+import com.forgerock.opendj.cli.ArgumentParser;
+import com.forgerock.opendj.cli.ConsoleApplication;
+
+/**
+ * SDK Performance Runner options wrapper.
+ */
+class PerformanceRunnerOptions {
+ private ArgumentParser argParser;
+ private ConsoleApplication app;
+
+ private boolean supportsRebind = true;
+ private boolean supportAsynchronousRequests = true;
+ private boolean supportsMultipleThreadsPerConnection = true;
+ private boolean supportsGeneratorArgument = true;
+
+ PerformanceRunnerOptions(ArgumentParser argParser, ConsoleApplication app) {
+ super();
+ this.argParser = argParser;
+ this.app = app;
+ }
+
+ boolean supportsRebind() {
+ return supportsRebind;
+ }
+
+ void setSupportsRebind(boolean supportsRebind) {
+ this.supportsRebind = !supportsRebind;
+ }
+
+ boolean supportsAsynchronousRequests() {
+ return supportAsynchronousRequests;
+ }
+
+ void setSupportsAsynchronousRequests(boolean supportAsynchronousRequests) {
+ this.supportAsynchronousRequests = supportAsynchronousRequests;
+ }
+
+ boolean supportsMultipleThreadsPerConnection() {
+ return supportsMultipleThreadsPerConnection;
+ }
+
+ void setSupportsMultipleThreadsPerConnection(boolean supportsMultipleThreadsPerConnection) {
+ this.supportsMultipleThreadsPerConnection = supportsMultipleThreadsPerConnection;
+ }
+
+ boolean supportsGeneratorArgument() {
+ return supportsGeneratorArgument;
+ }
+
+ void setSupportsGeneratorArgument(boolean supportsGeneratorArgument) {
+ this.supportsGeneratorArgument = supportsGeneratorArgument;
+ }
+
+ ArgumentParser getArgumentParser() {
+ return argParser;
+ }
+
+ ConsoleApplication getConsoleApplication() {
+ return app;
+ }
+
+}
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
index 66a312b..8318c0c 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
@@ -139,9 +139,9 @@
private DereferenceAliasesPolicy dereferencesAliasesPolicy;
private String[] attributes;
- private SearchPerformanceRunner(final ArgumentParser argParser, final ConsoleApplication app)
+ private SearchPerformanceRunner(final PerformanceRunnerOptions options)
throws ArgumentException {
- super(argParser, app, false, false, false);
+ super(options);
}
@Override
@@ -225,7 +225,7 @@
Utils.setDefaultPerfToolProperties();
connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
- runner = new SearchPerformanceRunner(argParser, this);
+ runner = new SearchPerformanceRunner(new PerformanceRunnerOptions(argParser, this));
propertiesFileArgument = CommonArguments.getPropertiesFile();
argParser.addArgument(propertiesFileArgument);
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties b/opendj-sdk/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
index 02ecc68..41bcb5a 100755
--- a/opendj-sdk/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
+++ b/opendj-sdk/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
@@ -22,7 +22,7 @@
#
#
# Copyright 2010 Sun Microsystems, Inc.
-# Portions copyright 2012 ForgeRock AS.
+# Portions copyright 2012-2014 ForgeRock AS.
#
#
# Utility messages
@@ -429,6 +429,15 @@
user-defined searches.\n\n\
Example:\n\n\ \ searchrate -p 1389 -D "cn=directory manager" -w password \\\n\
\ \ \ \ -F -c 4 -t 4 -b "dc=example,dc=com" -g "rand(0,2000)" "(uid=user.%%d)"
+INFO_ADDRATE_TOOL_DESCRIPTION=This utility can be used to measure \
+ add and optionally delete throughput and response time of a directory server using \
+ user-defined entries.\n\
+ \nExamples:\n \ This example is adding entries and randomly deleting them while \
+ the number of entries added is greater than 10,000: \n \
+ addrate -p 1389 -f -c 10 -d rand -s 10000 addrate.template \n \
+ This example adds entries and starts to delete them in the same \
+ order if their age is greater than a certain time: \n \
+ addrate -p 1389 -f -c 10 -d fifo -a 2 addrate.template
INFO_SEARCHRATE_TOOL_DESCRIPTION_BASEDN=Base DN format string.
INFO_MODRATE_TOOL_DESCRIPTION=This utility can be used to measure \
modify throughput and response time of a directory service using \
@@ -471,6 +480,14 @@
files and report the differences in LDIF format
INFO_LDIFSEARCH_TOOL_DESCRIPTION=This utility can be used to perform search \
operations against entries contained in an LDIF file
+ERR_LDIF_GEN_TOOL_EXCEPTION_DURING_PARSE=An error occurred while \
+ parsing template file: %s
+ERR_LDIF_GEN_TOOL_NO_SUCH_RESOURCE_DIRECTORY=The specified resource \
+ directory %s does not exist
+ERR_TOOL_NOT_ENOUGH_ITERATIONS=%s argument must be greater than or equal to %s \
+ (%s per %s)
+ERR_TOOL_ARG_NEEDED_WHEN_USING_ARG=%s must be used when using %s
+ERR_TOOL_ARG_MUST_BE_USED_WHEN_ARG_CONDITION=%s must be used if %s is %s
#
# MakeLDIF tool
#
@@ -487,18 +504,34 @@
INFO_MAKELDIF_DESCRIPTION_HELP=Show this usage information
INFO_MAKELDIF_DESCRIPTION_RESOURCE_PATH=Path to look for \
MakeLDIF resources (e.g., data files)
-ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY=The specified resource \
- directory %s could not be found
INFO_MAKELDIF_PROCESSED_N_ENTRIES=Processed %d entries
INFO_MAKELDIF_PROCESSING_COMPLETE=LDIF processing complete. %d entries \
written
-ERR_MAKELDIF_EXCEPTION_DURING_PARSE=An error occurred while \
- parsing template file : %s
ERR_MAKELDIF_UNABLE_TO_CREATE_LDIF=An error occurred while \
attempting to open LDIF file %s for writing: %s
ERR_MAKELDIF_ERROR_WRITING_LDIF=An error occurred while writing data \
to LDIF file %s: %s
ERR_MAKELDIF_EXCEPTION_DURING_PROCESSING=An error occurred while \
processing : %s
-ERR_CONSTANT_ARG_CANNOT_DECODE=Unable to parse a constant argument, \
+ERR_CONSTANT_ARG_CANNOT_DECODE=Unable to parse a constant argument \
expecting name=value but got %s
+#
+# AddRate Tool
+#
+INFO_ADDRATE_DESCRIPTION_RESOURCE_PATH=Path to look for template resources (e.g. data files)
+INFO_ADDRATE_DESCRIPTION_SEED=The seed to use for initializing the random number generator
+INFO_ADDRATE_DESCRIPTION_CONSTANT=A constant that overrides the value set in the template file
+INFO_ADDRATE_DESCRIPTION_DELETEMODE=The algorithm used for selecting entries to be deleted which \
+ must be one of "fifo", "random", or "off".
+INFO_ADDRATE_DESCRIPTION_DELETESIZETHRESHOLD=Specifies the number of entries \
+ to be added before deletion begins
+INFO_ADDRATE_DESCRIPTION_DELETEAGETHRESHOLD=Specifies the age at which added entries \
+ will become candidates for deletion
+INFO_DELETEMODE_PLACEHOLDER={fifo | random | off}
+INFO_DELETESIZETHRESHOLD_PLACEHOLDER={count}
+INFO_DELETEAGETHRESHOLD_PLACEHOLDER={seconds}
+ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON=A deletion threshold should not be specified when deletion is disabled
+ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE=Size and age based deletion thresholds were both \
+ specified, but only only one at a time is supported
+ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE=A age based deletion threshold should not be used \
+ with a random deletion mode
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java b/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java
new file mode 100644
index 0000000..cabd601
--- /dev/null
+++ b/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2014 ForgeRock AS.
+ */
+package com.forgerock.opendj.ldap.tools;
+
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.INFO_ADDRATE_TOOL_DESCRIPTION;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.forgerock.util.Utils.closeSilently;
+
+import java.io.PrintStream;
+
+import org.forgerock.opendj.ldap.ByteStringBuilder;
+import org.forgerock.opendj.ldap.TestCaseUtils;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class AddRateITCase extends ToolsITCase {
+ private static final String TEMPLATE_NAME = "addrate.template";
+ private static final String ADD_PERCENT_TEXT = "Add%";
+ private static final int THROUGHPUT_COLUMN = 1;
+ private static final int ERR_PER_SEC_COLUMN_NUMBER = 8;
+
+ @DataProvider
+ public Object[][] addRateArgs() throws Exception {
+ String[] commonArgs =
+ args("-h", TestCaseUtils.getServerSocketAddress().getHostName(),
+ "-p", Integer.toString(TestCaseUtils.getServerSocketAddress().getPort()),
+ "-c", "1", "-t", "1", "-i", "1", "-m", "1000", "-S");
+
+ return new Object[][] {
+ // Check if the help message is correctly prompted
+ { args("-H"), INFO_ADDRATE_TOOL_DESCRIPTION.get(), "" },
+
+ // Should report inconsistent use of options
+ { args(commonArgs, "-C", "off", "-a", "3", TEMPLATE_NAME), "",
+ ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get() },
+ { args(commonArgs, "-C", "off", "-s", "30000", TEMPLATE_NAME), "",
+ ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get() },
+ { args(commonArgs, "-C", "fifo", "-a", "3", "-s", "20000", TEMPLATE_NAME), "",
+ ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE.get() },
+ { args(commonArgs, "-C", "random", "-a", "3", TEMPLATE_NAME), "",
+ ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE.get() },
+
+ // Correct test case
+ { args(commonArgs, "-s", "10", TEMPLATE_NAME), ADD_PERCENT_TEXT, "" },
+
+ };
+ }
+
+ public String[] args(String[] startLine, String... args) {
+ String[] res = new String[startLine.length + args.length];
+
+ System.arraycopy(startLine, 0, res, 0, startLine.length);
+ System.arraycopy(args, 0, res, startLine.length, args.length);
+
+ return res;
+ }
+
+ @Test(dataProvider = "addRateArgs")
+ public void testITAddRate(String[] arguments, Object expectedOut, Object expectedErr) throws Exception {
+ ByteStringBuilder out = new ByteStringBuilder();
+ ByteStringBuilder err = new ByteStringBuilder();
+
+ PrintStream outStream = new PrintStream(out.asOutputStream());
+ PrintStream errStream = new PrintStream(err.asOutputStream());
+
+ try {
+ AddRate addRate = new AddRate(outStream, errStream);
+ addRate.run(arguments);
+
+ checkOuputStreams(out, err, expectedOut, expectedErr);
+ String outContent = out.toString();
+
+ if (outContent.contains(ADD_PERCENT_TEXT)) {
+ // Check that there was no error in search
+ String[] addRateResLines = outContent.split(System.getProperty("line.separator"));
+ // Skip header line
+ for (int i = 1; i < addRateResLines.length; i++) {
+ String[] addhRateLineData = addRateResLines[i].split(",");
+ assertThat(addhRateLineData[ERR_PER_SEC_COLUMN_NUMBER].trim()).isEqualTo("0.0");
+ assertThat(addhRateLineData[THROUGHPUT_COLUMN].trim()).isNotEqualTo("0.0");
+ }
+
+ }
+ } finally {
+ closeSilently(outStream, errStream);
+ }
+ }
+
+}
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/MakeLDIFTestCase.java b/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/MakeLDIFTestCase.java
index bd33975..3672dd1 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/MakeLDIFTestCase.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/MakeLDIFTestCase.java
@@ -68,7 +68,7 @@
expectedErrOutput(ERR_ERROR_PARSING_ARGS.get("")) },
{ args("-r", "unknown/path" , "example.template"),
- expectedErrOutput(ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY.get("unknown/path")) },
+ expectedErrOutput(ERR_LDIF_GEN_TOOL_NO_SUCH_RESOURCE_DIRECTORY.get("unknown/path")) },
{ args("-o", "unknown/path" , "example.template"),
expectedErrOutput(ERR_MAKELDIF_UNABLE_TO_CREATE_LDIF.get("unknown/path", "")) },
diff --git a/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java b/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java
index 3297650..ec20d54 100644
--- a/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java
+++ b/opendj-sdk/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java
@@ -74,16 +74,19 @@
}
private void checkOutputStream(ByteStringBuilder out, Object expectedOutput) {
+ String lineSeparator = System.getProperty("line.separator");
String toCompare = expectedOutput.toString();
if (expectedOutput instanceof LocalizableMessage) {
toCompare = wrapText((LocalizableMessage) expectedOutput, MAX_LINE_WIDTH);
}
+ toCompare = toCompare.replace(lineSeparator, " ");
+
if (toCompare.isEmpty()) {
assertThat(out.toString().length()).isEqualTo(0);
} else {
- assertThat(out.toString()).contains(toCompare);
+ assertThat(out.toString().replace(lineSeparator, " ")).contains(toCompare);
}
}
--
Gitblit v1.10.0