From e1b8b0d8a080991befc174db259f485273de98e7 Mon Sep 17 00:00:00 2001 From: No Author Date: Fri, 19 Aug 2005 20:24:19 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'rel-2_1'. git-svn-id: https://svn.apache.org/repos/asf/jakarta/jmeter/tags/rel-2_1@325652 13f79535-47bb-0310-9956-ffa450edef68 --- KEYS.txt | 17 +++ build.xml | 2 +- eclipse.classpath | 2 +- .../org/apache/jmeter/config/CSVDataSet.java | 2 +- .../org/apache/jmeter/config/Argument.java | 3 + .../org/apache/jmeter/config/Arguments.java | 3 + .../org/apache/jmeter/engine/PreCompiler.java | 20 +-- .../jmeter/engine/StandardJMeterEngine.java | 23 ++-- .../org/apache/jmeter/gui/GuiPackage.java | 6 + .../org/apache/jmeter/junit/JMeterTest.java | 15 +- .../org/apache/jmeter/save/SaveService.java | 22 +-- .../apache/jmeter/services/FileServer.java | 6 +- .../apache/jmeter/testelement/TestPlan.java | 5 + .../jmeter/testelement/ThreadListener.java | 43 ++++++ .../property/FunctionProperty.java | 4 +- .../org/apache/jmeter/util/JMeterVersion.java | 2 +- .../org/apache/jorphan/util/JOrphanUtils.java | 74 +++------- .../http/control/gui/HttpTestSampleGui2.java | 7 +- .../control/gui/WebServiceSamplerGui.java | 9 +- .../http/parser/HtmlParserHTMLParser.java | 9 +- .../protocol/http/parser/JTidyHTMLParser.java | 2 +- .../http/sampler/AccessLogSampler.java | 23 +++- .../protocol/http/sampler/HTTPSampler2.java | 4 +- .../protocol/http/util/accesslog/Filter.java | 4 +- .../http/util/accesslog/LogFilter.java | 13 +- .../accesslog/OrderPreservingLogParser.java | 27 ++++ .../http/util/accesslog/SessionFilter.java | 130 +++++++++++++----- .../util/accesslog/SharedTCLogParser.java | 93 +++++++++++++ .../http/util/accesslog/TCLogParser.java | 2 +- .../protocol/jdbc/sampler/JDBCSampler.java | 18 +-- .../jdbc/sampler/JDBCSamplerBeanInfo.java | 8 +- .../sampler/JDBCSamplerResources.properties | 4 +- .../JDBCSamplerResources_es.properties | 4 +- .../protocol/tcp/sampler/TCPSampler.java | 119 +++++----------- .../apache/jmeter/save/TestSaveService.java | 14 +- .../apache/jorphan/util/TestJorphanUtils.java | 81 +++++++++++ xdocs/changes.xml | 4 + xdocs/usermanual/component_reference.xml | 14 +- ...meter_distributed_testing_step_by_step.pdf | Bin 123784 -> 133695 bytes ...meter_distributed_testing_step_by_step.sxw | Bin 39165 -> 49185 bytes 40 files changed, 577 insertions(+), 261 deletions(-) create mode 100644 src/core/org/apache/jmeter/testelement/ThreadListener.java create mode 100644 src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java create mode 100644 src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java create mode 100644 test/src/org/apache/jorphan/util/TestJorphanUtils.java diff --git a/KEYS.txt b/KEYS.txt index a2d7bead7d6..f188ea33e8f 100644 --- a/KEYS.txt +++ b/KEYS.txt @@ -113,3 +113,20 @@ LY8FCQXcYNsACgkQP+DBYbypc6yBbwCeOvnv0jFl9vF0s8xhSAoL//sVh+AAni2E MqhkiC8tJZdEK9EAd9T0HkeT =Y6NQ -----END PGP PUBLIC KEY BLOCK----- + +key for: Peter Lin + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.2 (MingW32) + +mIsEQvwEgAEEALtlIFI5TYGenY7iQhhoU26lca2z7PTIGlCZ3eAPxjQNek/oCEPR +nl39NtNKjEo4V2eJPrZz4nlA2pUxwkzVWGe9yULQT73mB0pkQxr40iH2vIdpkW3y +CV3NymyH77XrvELhQrnLpK1pbzMdPTnpr04Wq4BCysEcGzJJT7mlIDiBAAYptDtQ +ZXRlciBMaW4gKFBldGVyIExpbiBKTWV0ZXIgY29tbWl0dGVyKSA8d29vbGZlbEBh +cGFjaGUub3JnPoi8BBMBAgAmBQJC/ASAAhsDBQkDwmcABgsJCAcDAgQVAggDBBYC +AwECHgECF4AACgkQlell0l24rA970wP/WhOKLRrpF8IrjgpZsJ4X7CpdOhRQRfXd +8u7TxhdxTRom6XFW14LyUH9i7teRB3RC8F809jFwfmR2oa4fhNhv36QkXQI9npmS +gebbL9RMq3mjdXwtpDYbcA4r2pbz+Wf2m+BV38Gz8k+WOm5X8QW3sRbZ1QnxUjTu +NGHyeqz14hs= +=rMW+ +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/build.xml b/build.xml index 97d82951977..c899389f43c 100644 --- a/build.xml +++ b/build.xml @@ -298,7 +298,7 @@ - + diff --git a/eclipse.classpath b/eclipse.classpath index d5e31a04375..7a7b4d7d110 100644 --- a/eclipse.classpath +++ b/eclipse.classpath @@ -35,7 +35,7 @@ - + diff --git a/src/components/org/apache/jmeter/config/CSVDataSet.java b/src/components/org/apache/jmeter/config/CSVDataSet.java index aad80920113..0895f9c9d81 100644 --- a/src/components/org/apache/jmeter/config/CSVDataSet.java +++ b/src/components/org/apache/jmeter/config/CSVDataSet.java @@ -62,7 +62,7 @@ public void iterationStart(LoopIterationEvent iterEvent) { String delim = getDelimiter(); if (delim.equals("\\t")) delim = "\t";// Make it easier to enter a Tab - String[] lineValues = JOrphanUtils.split(server.readLine(getFilename()), delim); + String[] lineValues = JOrphanUtils.split(server.readLine(getFilename()), delim,false); for (int a = 0; a < vars.length && a < lineValues.length; a++) { this.getThreadContext().getVariables().put(vars[a], lineValues[a]); } diff --git a/src/core/org/apache/jmeter/config/Argument.java b/src/core/org/apache/jmeter/config/Argument.java index d45706a8a3b..02190d2d3a2 100644 --- a/src/core/org/apache/jmeter/config/Argument.java +++ b/src/core/org/apache/jmeter/config/Argument.java @@ -22,6 +22,8 @@ import org.apache.jmeter.testelement.AbstractTestElement; import org.apache.jmeter.testelement.property.StringProperty; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.log.Logger; // Mark Walsh, 2002-08-03, add metadata attribute // add constructor Argument(String name, Object value, Object metadata) @@ -36,6 +38,7 @@ * @version $Revision$ */ public class Argument extends AbstractTestElement implements Serializable { + private static Logger log = LoggingManager.getLoggerForClass(); /** Name used to store the argument's name. */ public static final String ARG_NAME = "Argument.name"; diff --git a/src/core/org/apache/jmeter/config/Arguments.java b/src/core/org/apache/jmeter/config/Arguments.java index 94044d813e9..fac807eba38 100644 --- a/src/core/org/apache/jmeter/config/Arguments.java +++ b/src/core/org/apache/jmeter/config/Arguments.java @@ -27,6 +27,8 @@ import org.apache.jmeter.testelement.property.CollectionProperty; import org.apache.jmeter.testelement.property.PropertyIterator; import org.apache.jmeter.testelement.property.TestElementProperty; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.log.Logger; // Mark Walsh, 2002-08-03 add method: // addArgument(String name, Object value, Object metadata) @@ -41,6 +43,7 @@ * @version $Revision$ */ public class Arguments extends ConfigTestElement implements Serializable { + private static Logger log = LoggingManager.getLoggerForClass(); /** The name of the property used to store the arguments. */ public static final String ARGUMENTS = "Arguments.arguments"; diff --git a/src/core/org/apache/jmeter/engine/PreCompiler.java b/src/core/org/apache/jmeter/engine/PreCompiler.java index a7af74218e0..7eba7eb7457 100644 --- a/src/core/org/apache/jmeter/engine/PreCompiler.java +++ b/src/core/org/apache/jmeter/engine/PreCompiler.java @@ -52,21 +52,25 @@ public PreCompiler() { * @see HashTreeTraverser#addNode(Object, HashTree) */ public void addNode(Object node, HashTree subTree) { + if(node instanceof TestElement) + { + try { + replacer.replaceValues((TestElement) node); + } catch (InvalidVariableException e) { + log.error("invalid variables", e); + } + } if (node instanceof TestPlan) { - Map args = ((TestPlan) node).getUserDefinedVariables(); + ((TestPlan)node).prepareForPreCompile(); //A hack to make user-defined variables in the testplan element more dynamic + Map args = ((TestPlan) node).getUserDefinedVariables(); replacer.setUserDefinedVariables(args); JMeterVariables vars = new JMeterVariables(); vars.putAll(args); JMeterContextService.getContext().setVariables(vars); - } else if (node instanceof TestElement) { - try { - replacer.replaceValues((TestElement) node); - } catch (InvalidVariableException e) { - log.error("invalid variables", e); - } - } + } if (node instanceof Arguments) { + ((Arguments)node).setRunningVersion(true); Map args = ((Arguments) node).getArgumentsAsMap(); replacer.addVariables(args); JMeterContextService.getContext().getVariables().putAll(args); diff --git a/src/core/org/apache/jmeter/engine/StandardJMeterEngine.java b/src/core/org/apache/jmeter/engine/StandardJMeterEngine.java index 9b61f5fb415..929a93eb15d 100644 --- a/src/core/org/apache/jmeter/engine/StandardJMeterEngine.java +++ b/src/core/org/apache/jmeter/engine/StandardJMeterEngine.java @@ -204,6 +204,7 @@ protected void notifyTestListenersOfStart() { } protected void notifyTestListenersOfEnd() { + log.info("Notifying test listeners of end of test"); Iterator iter = testListeners.getSearchResults().iterator(); while (iter.hasNext()) { TestListener tl = (TestListener) iter.next(); @@ -231,12 +232,16 @@ public void reset() { } public synchronized void threadFinished(JMeterThread thread) { - allThreads.remove(thread); - log.info("Ending thread " + thread.getThreadNum()); - if (!serialized && allThreads.size() == 0 && !schcdule_run) { - log.info("Stopping test"); - stopTest(); - } + try { + allThreads.remove(thread); + log.info("Ending thread " + thread.getThreadNum()); + if (!serialized && allThreads.size() == 0 && !schcdule_run) { + log.info("Stopping test"); + stopTest(); + } + } catch (Throwable e) { + log.fatalError("Call to threadFinished should never throw an exception - this can deadlock JMeter",e); + } } public synchronized void stopTest() { @@ -296,7 +301,7 @@ public void run() { if (((TestPlan) plan[0]).isSerialized()) { serialized = true; } - JMeterContextService.startTest(); + JMeterContextService.startTest(); compileTree(); /** * Notification of test listeners needs to happen after function @@ -354,9 +359,9 @@ public void run() { log.info("Continue on error"); } + ListedHashTree threadGroupTree = (ListedHashTree) searcher.getSubTree(group); + threadGroupTree.add(group, testLevelElements); for (int i = 0; running && i < threads.length; i++) { - ListedHashTree threadGroupTree = (ListedHashTree) searcher.getSubTree(group); - threadGroupTree.add(group, testLevelElements); threads[i] = new JMeterThread(cloneTree(threadGroupTree), this, notifier); threads[i].setThreadNum(i); threads[i].setThreadGroup(group); diff --git a/src/core/org/apache/jmeter/gui/GuiPackage.java b/src/core/org/apache/jmeter/gui/GuiPackage.java index cc2a5ae21a2..6abe3456bc2 100644 --- a/src/core/org/apache/jmeter/gui/GuiPackage.java +++ b/src/core/org/apache/jmeter/gui/GuiPackage.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Map; +import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import org.apache.jmeter.engine.util.ValueReplacer; @@ -306,6 +307,11 @@ public TestElement createTestElement(String objClass) { return node; } catch (NoClassDefFoundError e) { log.error("Problem retrieving gui for " + objClass, e); + String msg="Cannot find class: "+e.getMessage(); + JOptionPane.showMessageDialog(null, + msg, + "Missing jar? See log file." , + JOptionPane.ERROR_MESSAGE); throw new RuntimeException(e.toString()); // Probably a missing // jar } catch (ClassNotFoundException e) { diff --git a/src/core/org/apache/jmeter/junit/JMeterTest.java b/src/core/org/apache/jmeter/junit/JMeterTest.java index ff042f1dd17..3e0b1ec75ee 100644 --- a/src/core/org/apache/jmeter/junit/JMeterTest.java +++ b/src/core/org/apache/jmeter/junit/JMeterTest.java @@ -173,11 +173,14 @@ public void createTitleSet() throws Exception { List components = ((Element) sections.get(i)).getChildren("component"); for (int j = 0; j < components.size(); j++) { Element comp = (Element) components.get(j); - guiTitles.put(comp.getAttributeValue("name"), Boolean.FALSE); + String nm=comp.getAttributeValue("name"); + if (!nm.equals("SSL Manager")){// Not a true GUI component + guiTitles.put(nm.replace(' ','_'), Boolean.FALSE); + } } } // Add titles that don't need to be documented - guiTitles.put("Root", Boolean.FALSE); + //guiTitles.put("Root", Boolean.FALSE); guiTitles.put("Example Sampler", Boolean.FALSE); } @@ -235,10 +238,6 @@ private int scanprintMap(Map m, String t) { while (i.hasNext()) { Object key = i.next(); if (!m.get(key).equals(Boolean.TRUE)) { - if (key.equals("SSL Manager"))// Not a true GUI component - { - continue; - } if (unseen == 0)// first time { System.out.println("\nNames remaining in " + t + " Map:"); @@ -332,13 +331,13 @@ private static Test suiteBeanComponents() throws Exception { */ public void runGUITitle() throws Exception { if (guiTitles.size() > 0) { - String title = guiItem.getStaticLabel(); + String title = guiItem.getDocAnchor(); boolean ct = guiTitles.containsKey(title); if (ct) guiTitles.put(title, Boolean.TRUE);// So we can detect extra // entries if (// Is this a work in progress or an internal GUI component? - (title.length() > 0) // Will be "" for internal components + (title != null && title.length() > 0) // Will be "" for internal components && (title.toUpperCase().indexOf("(ALPHA") == -1) && (title.toUpperCase().indexOf("(BETA") == -1) && (!title.equals("Example1")) // Skip the example samplers // ... diff --git a/src/core/org/apache/jmeter/save/SaveService.java b/src/core/org/apache/jmeter/save/SaveService.java index fb3cbb23b57..0caf7ea5275 100644 --- a/src/core/org/apache/jmeter/save/SaveService.java +++ b/src/core/org/apache/jmeter/save/SaveService.java @@ -220,23 +220,23 @@ private static void checkVersion(Class clazz, String expected) { private static void checkVersions() { versionsOK = true; - checkVersion(BooleanPropertyConverter.class, "1.4"); - checkVersion(HashTreeConverter.class, "1.2"); - checkVersion(IntegerPropertyConverter.class, "1.3"); - checkVersion(LongPropertyConverter.class, "1.3"); - checkVersion(MultiPropertyConverter.class, "1.3"); - checkVersion(SampleResultConverter.class, "1.8"); + checkVersion(BooleanPropertyConverter.class, "1.5"); + checkVersion(HashTreeConverter.class, "1.3"); + checkVersion(IntegerPropertyConverter.class, "1.4"); + checkVersion(LongPropertyConverter.class, "1.4"); + checkVersion(MultiPropertyConverter.class, "1.4"); + checkVersion(SampleResultConverter.class, "1.9"); /* * Should check this, but tricky to do, because not built until later. * * checkVersion(HTTPResultConverter.class, "1.6"); * */ - checkVersion(StringPropertyConverter.class, "1.6"); - checkVersion(TestElementConverter.class, "1.3"); - checkVersion(TestElementPropertyConverter.class, "1.6"); - checkVersion(ScriptWrapperConverter.class, "1.5"); - checkVersion(TestResultWrapperConverter.class, "1.4"); + checkVersion(StringPropertyConverter.class, "1.7"); + checkVersion(TestElementConverter.class, "1.4"); + checkVersion(TestElementPropertyConverter.class, "1.7"); + checkVersion(ScriptWrapperConverter.class, "1.6"); + checkVersion(TestResultWrapperConverter.class, "1.5"); if (!PROPVERSION.equalsIgnoreCase(propertiesVersion)) { log.warn("Property file - expected " + PROPVERSION + ", found " + propertiesVersion); } diff --git a/src/core/org/apache/jmeter/services/FileServer.java b/src/core/org/apache/jmeter/services/FileServer.java index a18d0d576a1..a8e6a0663ae 100644 --- a/src/core/org/apache/jmeter/services/FileServer.java +++ b/src/core/org/apache/jmeter/services/FileServer.java @@ -72,7 +72,6 @@ public static FileServer getFileServer() { } public void setBasedir(String basedir) throws IOException { - log.info("Setting basedir to: " + basedir); if (filesOpen()) { throw new IOException("Files are still open, cannot change base directory"); } @@ -90,7 +89,6 @@ public String getBaseDir() { } public synchronized void reserveFile(String filename) { - log.info("filename = " + filename + " base = " + base); if (!files.containsKey(filename)) { Object[] file = new Object[] { new File(base, filename), null }; files.put(filename, file); @@ -151,7 +149,7 @@ public void closeFiles() throws IOException { */ public synchronized void closeFile(String name) throws IOException { Object[] file = (Object[]) files.get(name); - if (file[1] != null) { + if (file != null && file.length == 2 && file[1] != null) { ((Reader) file[1]).close(); file[1] = null; } @@ -171,6 +169,8 @@ protected boolean filesOpen() { /** * Method will get a random file in a base directory + * TODO hey, not sure this method belongs here. FileServer is for threadsafe + * File access relative to current test's base directory. * * @param basedir * @return diff --git a/src/core/org/apache/jmeter/testelement/TestPlan.java b/src/core/org/apache/jmeter/testelement/TestPlan.java index 5bf023d9ba9..1a769e79cd9 100644 --- a/src/core/org/apache/jmeter/testelement/TestPlan.java +++ b/src/core/org/apache/jmeter/testelement/TestPlan.java @@ -88,6 +88,11 @@ public TestPlan(String name) { // setSerialized(false); setProperty(new CollectionProperty(THREAD_GROUPS, threadGroups)); } + + public void prepareForPreCompile() + { + getVariables().setRunningVersion(true); + } /** * Fetches the functional mode property diff --git a/src/core/org/apache/jmeter/testelement/ThreadListener.java b/src/core/org/apache/jmeter/testelement/ThreadListener.java new file mode 100644 index 00000000000..69655897f18 --- /dev/null +++ b/src/core/org/apache/jmeter/testelement/ThreadListener.java @@ -0,0 +1,43 @@ +// $Header$ +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.testelement; + +/** + * Allow threads to perform startup and closedown if necessary + * + * @version $Revision$ on $Date$ + */ +public interface ThreadListener { + /** + * Called just before the start of the thread + * + * @see org.apache.jmeter.threads.JMeterThread#threadStarted() + * + */ + public void threadStarted(); + + /** + * Called once for each thread at the end of a test + * + * @see org.apache.jmeter.threads.JMeterThread#threadFinished() + * + */ + public void threadFinished(); + +} \ No newline at end of file diff --git a/src/core/org/apache/jmeter/testelement/property/FunctionProperty.java b/src/core/org/apache/jmeter/testelement/property/FunctionProperty.java index 2b567e7fe18..42a5d869804 100644 --- a/src/core/org/apache/jmeter/testelement/property/FunctionProperty.java +++ b/src/core/org/apache/jmeter/testelement/property/FunctionProperty.java @@ -70,12 +70,12 @@ public String getStringValue() { JMeterContext ctx = JMeterContextService.getContext();// Expensive, so // do // once - if (!isRunningVersion() || !ctx.isSamplingStarted()) { + if (!isRunningVersion() /*|| !ctx.isSamplingStarted()*/) { log.debug("Not running version, return raw function string"); return function.getRawParameters(); } else { log.debug("Running version, executing function"); - int iter = ctx.getVariables().getIteration(); + int iter = ctx.getVariables() != null ? ctx.getVariables().getIteration() : -1; if (iter < testIteration) { testIteration = -1; } diff --git a/src/core/org/apache/jmeter/util/JMeterVersion.java b/src/core/org/apache/jmeter/util/JMeterVersion.java index 9319062b7bc..841d3c6209c 100644 --- a/src/core/org/apache/jmeter/util/JMeterVersion.java +++ b/src/core/org/apache/jmeter/util/JMeterVersion.java @@ -41,7 +41,7 @@ public class JMeterVersion { * JMeterUtils This ensures that JMeterUtils always gets the correct * version, even if it is not re-compiled during the build. */ - private static final String VERSION = "2.1.20050630"; + private static final String VERSION = "2.1"; static final String COPYRIGHT = "Copyright (c) 1998-2005 The Apache Software Foundation"; diff --git a/src/jorphan/org/apache/jorphan/util/JOrphanUtils.java b/src/jorphan/org/apache/jorphan/util/JOrphanUtils.java index 68e6d53fac4..3759d5311a3 100644 --- a/src/jorphan/org/apache/jorphan/util/JOrphanUtils.java +++ b/src/jorphan/org/apache/jorphan/util/JOrphanUtils.java @@ -61,15 +61,18 @@ private JOrphanUtils() { * Character to split the string on * @return Array of all the tokens. */ - public static String[] split(String splittee, String splitChar) { + public static String[] split(String splittee, String splitChar,boolean truncate) { if (splittee == null || splitChar == null) { return new String[0]; } int spot; - while ((spot = splittee.indexOf(splitChar + splitChar)) != -1) { - splittee = splittee.substring(0, spot + splitChar.length()) - + splittee.substring(spot + 2 * splitChar.length(), splittee.length()); - } + if(truncate) { + while ((spot = splittee.indexOf(splitChar + splitChar)) != -1) { + splittee = splittee.substring(0, spot + splitChar.length()) + + splittee.substring(spot + 2 * splitChar.length(), splittee.length()); + } + if(splittee.startsWith(splitChar)) splittee = splittee.substring(splitChar.length()); + } Vector returns = new Vector(); int start = 0; int length = splittee.length(); @@ -78,6 +81,10 @@ public static String[] split(String splittee, String splitChar) { if (spot > 0) { returns.addElement(splittee.substring(start, spot)); } + else + { + returns.addElement(""); + } start = spot + splitChar.length(); } if (start < length) { @@ -87,6 +94,11 @@ public static String[] split(String splittee, String splitChar) { returns.copyInto(values); return values; } + + public static String[] split(String splittee,String splitChar) + { + return split(splittee,splitChar,true); + } private static final String SPACES = " "; @@ -280,56 +292,4 @@ public static byte[] getByteArraySlice(byte[] array, int begin, int end) { return slice; } - - public static class Test extends TestCase { - public void testReplace1() { - assertEquals("xyzdef", replaceFirst("abcdef", "abc", "xyz")); - } - - public void testReplace2() { - assertEquals("axyzdef", replaceFirst("abcdef", "bc", "xyz")); - } - - public void testReplace3() { - assertEquals("abcxyz", replaceFirst("abcdef", "def", "xyz")); - } - - public void testReplace4() { - assertEquals("abcdef", replaceFirst("abcdef", "bce", "xyz")); - } - - public void testReplace5() { - assertEquals("abcdef", replaceFirst("abcdef", "alt=\"\" ", "")); - } - - public void testReplace6() { - assertEquals("abcdef", replaceFirst("abcdef", "alt=\"\" ", "")); - } - - public void testReplace7() { - assertEquals("alt=\"\"", replaceFirst("alt=\"\"", "alt=\"\" ", "")); - } - - public void testReplace8() { - assertEquals("img src=xyz ", replaceFirst("img src=xyz alt=\"\" ", "alt=\"\" ", "")); - } - - public void testSplit1() { - String in = "a,bc,,"; // Test ignore trailing split characters - String out[] = split(in, ","); - assertEquals(2, out.length); - assertEquals("a", out[0]); - assertEquals("bc", out[1]); - } - - public void testSplit2() { - String in = ",,a,bc"; // Test leading split characters - String out[] = split(in, ","); - assertEquals("Should detect the leading split chars; ", 2, out.length - 2); - assertEquals("", out[0]); - assertEquals("", out[1]); - assertEquals("a", out[2]); - assertEquals("bc", out[3]); - } - } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui2.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui2.java index 7e9ddea9fe0..078a1ceb731 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui2.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui2.java @@ -38,6 +38,11 @@ public TestElement createTestElement() { } public String getStaticLabel() { - return super.getStaticLabel() + " HTTPCLient (ALPHA)"; + return super.getStaticLabel() + " HTTPClient"; } + + public String getDocAnchor() { + return super.getStaticLabel().replace(' ', '_'); + } + } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/WebServiceSamplerGui.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/WebServiceSamplerGui.java index 3e5c42ccb15..5bc6ebd6538 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/WebServiceSamplerGui.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/gui/WebServiceSamplerGui.java @@ -170,7 +170,7 @@ public void modifyTestElement(TestElement s) { WebServiceSampler sampler = (WebServiceSampler) s; this.configureTestElement(sampler); sampler.setDomain(domain.getText()); - sampler.setPort(80); + sampler.setProperty(HTTPSamplerBase.PORT,port.getText()); sampler.setPath(path.getText()); sampler.setWsdlURL(wsdlField.getText()); sampler.setMethod(HTTPSamplerBase.POST); @@ -280,12 +280,7 @@ public void configure(TestElement el) { WebServiceSampler sampler = (WebServiceSampler) el; wsdlField.setText(sampler.getWsdlURL()); domain.setText(sampler.getDomain()); - String portstring = sampler.getPropertyAsString(HTTPSamplerBase.PORT); - if (portstring.equals("" + HTTPSamplerBase.UNSPECIFIED_PORT)) { - port.setText(""); - } else { - port.setText(portstring); - } + port.setText(sampler.getPropertyAsString(HTTPSamplerBase.PORT)); path.setText(sampler.getPath()); soapAction.setText(sampler.getSoapAction()); soapXml.setText(sampler.getXmlData()); diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/parser/HtmlParserHTMLParser.java b/src/protocol/http/org/apache/jmeter/protocol/http/parser/HtmlParserHTMLParser.java index 07585d22803..86f3ce424f6 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/parser/HtmlParserHTMLParser.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/parser/HtmlParserHTMLParser.java @@ -48,6 +48,7 @@ import org.htmlparser.tags.LinkTag; import org.htmlparser.tags.LinkTagTag; import org.htmlparser.tags.ScriptTag; +import org.htmlparser.tags.Tag; import org.htmlparser.util.DefaultParserFeedback; import org.htmlparser.util.NodeIterator; import org.htmlparser.util.ParserException; @@ -165,7 +166,13 @@ public Iterator getEmbeddedResourceURLs(byte[] html, URL baseUrl, URLCollection } else if (node instanceof BgSoundTag) { BgSoundTag script = (BgSoundTag) node; binUrlStr = script.getAttribute("src"); - } + } else if (node instanceof Tag) { + Tag tag = (Tag) node; + String tagname=tag.getTagName(); + if (tagname.equalsIgnoreCase("EMBED")){ + binUrlStr = tag.getAttribute("src"); + } + } if (binUrlStr == null) { continue; diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/parser/JTidyHTMLParser.java b/src/protocol/http/org/apache/jmeter/protocol/http/parser/JTidyHTMLParser.java index bcfe943b31e..cb8937a965a 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/parser/JTidyHTMLParser.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/parser/JTidyHTMLParser.java @@ -111,7 +111,7 @@ private URL scanNodes(Node node, URLCollection urls, URL baseUrl) throws HTMLPar break; } - if (name.equalsIgnoreCase("img")) { + if (name.equalsIgnoreCase("img") || name.equalsIgnoreCase("embed")) { urls.addURL(getValue(attrs, "src"), baseUrl); break; } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/AccessLogSampler.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/AccessLogSampler.java index d842293a6c4..87da4e7bf60 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/AccessLogSampler.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/AccessLogSampler.java @@ -25,6 +25,7 @@ import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.testbeans.TestBean; import org.apache.jmeter.testelement.TestCloneable; +import org.apache.jmeter.testelement.ThreadListener; import org.apache.jmeter.threads.JMeterContextService; import org.apache.jorphan.logging.LoggingManager; import org.apache.jorphan.util.JMeterException; @@ -65,7 +66,7 @@ * @author Peter Lin * @version $Revision$ last updated $Date$ */ -public class AccessLogSampler extends HTTPSampler implements TestBean { +public class AccessLogSampler extends HTTPSampler implements TestBean,ThreadListener { private static Logger log = LoggingManager.getLoggerForClass(); public static final String DEFAULT_CLASS = "org.apache.jmeter.protocol.http.util.accesslog.TCLogParser"; @@ -296,6 +297,15 @@ public Object clone() { initFilter(); s.filter = (Filter) ((TestCloneable) filter).clone(); } + if(TestCloneable.class.isAssignableFrom(Class.forName(parserClassName))) + { + instantiateParser(); + s.PARSER = (LogParser)((TestCloneable)PARSER).clone(); + if(filter != null) + { + s.PARSER.setFilter(s.filter); + } + } } catch (Exception e) { log.warn("Could not clone cloneable filter", e); } @@ -313,6 +323,7 @@ public void testEnded() { if (PARSER != null) { PARSER.close(); } + filter = null; started = false; super.testEnded(); } @@ -326,4 +337,14 @@ public void testStarted() { started = true; super.testStarted(); } + + /* (non-Javadoc) + * @see org.apache.jmeter.testelement.AbstractTestElement#threadFinished() + */ + public void threadFinished() { + if(PARSER instanceof ThreadListener) + ((ThreadListener)PARSER).threadFinished(); + if(filter instanceof ThreadListener) + ((ThreadListener)filter).threadFinished(); + } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java index e460c5e6f8b..07daee11a72 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java @@ -536,11 +536,11 @@ private void saveConnectionCookies(HttpState state, CookieManager cookieManager) } public void threadStarted() { - log.info("Thread Started"); + log.debug("Thread Started"); } public void threadFinished() { - log.info("Thread Finished"); + log.debug("Thread Finished"); if (httpConn != null) httpConn.close(); } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/Filter.java b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/Filter.java index 6659e3b0804..985b6fa3ebe 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/Filter.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/Filter.java @@ -18,6 +18,8 @@ package org.apache.jmeter.protocol.http.util.accesslog; +import org.apache.jmeter.testelement.TestElement; + /** * Description:
*
@@ -82,7 +84,7 @@ public interface Filter { * @param path * @return boolean */ - public boolean isFiltered(String path); + public boolean isFiltered(String path,TestElement sampler); /** * In case the user wants to replace the file extension, log parsers should diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java index e0430cdaa62..1a86f52afd9 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import org.apache.jmeter.junit.JMeterTestCase; +import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.util.JMeterUtils; import org.apache.oro.text.regex.Pattern; import org.apache.oro.text.regex.Perl5Compiler; @@ -207,7 +208,7 @@ public void excludePattern(String[] regexp) { * @param path * @return boolean */ - public boolean isFiltered(String path) { + public boolean isFiltered(String path,TestElement el) { // we do a quick check to see if any // filters are set. If not we just // return false to be efficient. @@ -474,7 +475,7 @@ public void setUp() { public void testReplaceExtension() { testf.setReplaceExtension("html", "jsp"); - testf.isFiltered(TESTSTR);// set the required variables + testf.isFiltered(TESTSTR,null);// set the required variables assertEquals(TESTSTROUT, testf.filter(TESTSTR)); } @@ -485,7 +486,7 @@ public void testExcludeFiles() { String theFile = td.file; boolean expect = td.exclfile; - testf.isFiltered(theFile); + testf.isFiltered(theFile,null); String line = testf.filter(theFile); if (line != null) { assertTrue("Expect to accept " + theFile, expect); @@ -502,7 +503,7 @@ public void testIncludeFiles() { String theFile = td.file; boolean expect = td.inclfile; - testf.isFiltered(theFile); + testf.isFiltered(theFile,null); String line = testf.filter(theFile); if (line != null) { assertTrue("Expect to accept " + theFile, expect); @@ -520,7 +521,7 @@ public void testExcludePattern() { String theFile = td.file; boolean expect = td.exclpatt; - assertEquals(!expect, testf.isFiltered(theFile)); + assertEquals(!expect, testf.isFiltered(theFile,null)); String line = testf.filter(theFile); if (line != null) { assertTrue("Expect to accept " + theFile, expect); @@ -537,7 +538,7 @@ public void testIncludePattern() { String theFile = td.file; boolean expect = td.inclpatt; - assertEquals(!expect, testf.isFiltered(theFile)); + assertEquals(!expect, testf.isFiltered(theFile,null)); String line = testf.filter(theFile); if (line != null) { assertTrue("Expect to accept " + theFile, expect); diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java new file mode 100644 index 00000000000..4095fbfb527 --- /dev/null +++ b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java @@ -0,0 +1,27 @@ +package org.apache.jmeter.protocol.http.util.accesslog; + +import org.apache.jmeter.testelement.TestElement; + +public class OrderPreservingLogParser extends SharedTCLogParser { + + public OrderPreservingLogParser() { + super(); + } + + public OrderPreservingLogParser(String source) { + super(source); + } + + /** + * parse a set number of lines from the access log. Keep in mind the number + * of lines parsed will depend the filter and number of lines in the log. + * The method returns the actual lines parsed. + * + * @param count + * @return lines parsed + */ + public synchronized int parseAndConfigure(int count, TestElement el) { + return this.parse(el, count); + } + +} diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java index 7d298fc9e39..932ec133f70 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java @@ -21,11 +21,17 @@ package org.apache.jmeter.protocol.http.util.accesslog; import java.io.Serializable; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.jmeter.protocol.http.control.CookieManager; +import org.apache.jmeter.protocol.http.sampler.HTTPSampler; import org.apache.jmeter.testelement.TestCloneable; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.ThreadListener; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; @@ -37,37 +43,26 @@ * @author mstover * */ -public class SessionFilter implements Filter, Serializable, TestCloneable { +public class SessionFilter implements Filter, Serializable, TestCloneable,ThreadListener { + private static final long serialVersionUID = 1; static Logger log = LoggingManager.getLoggerForClass(); /** - * This object is static across multiple threads in a test, via clone() + * These objects are static across multiple threads in a test, via clone() * method. */ - protected List excludedIps; - - String ipAddress; + protected Map cookieManagers; + protected Set managersInUse; + + protected CookieManager lastUsed; - /* + /* * (non-Javadoc) * * @see org.apache.jmeter.protocol.http.util.accesslog.LogFilter#excPattern(java.lang.String) */ protected boolean hasExcPattern(String text) { - synchronized (excludedIps) { - boolean exclude = false; - for (Iterator x = excludedIps.iterator(); x.hasNext();) { - if (text.indexOf((String) x.next()) > -1) { - exclude = true; - break; - } - } - if (!exclude) { - ipAddress = getIpAddress(text); - excludedIps.add(ipAddress); - } - return exclude; - } + return false; } protected String getIpAddress(String logLine) { @@ -84,12 +79,21 @@ protected String getIpAddress(String logLine) { * @see org.apache.jmeter.protocol.http.util.accesslog.Filter#reset() */ public void reset() { - ipAddress = null; + cookieManagers.clear(); } public Object clone() { + if(cookieManagers == null) + { + cookieManagers = Collections.synchronizedMap(new HashMap()); + } + if(managersInUse == null) + { + managersInUse = Collections.synchronizedSet(new HashSet()); + } SessionFilter f = new SessionFilter(); - f.excludedIps = excludedIps; + f.cookieManagers = cookieManagers; + f.managersInUse = managersInUse; return f; } @@ -97,7 +101,6 @@ public Object clone() { * */ public SessionFilter() { - excludedIps = new LinkedList(); } /* @@ -146,13 +149,55 @@ public void includePattern(String[] regexp) { * * @see org.apache.jmeter.protocol.http.util.accesslog.Filter#isFiltered(java.lang.String) */ - public boolean isFiltered(String path) { - if (ipAddress != null) { - log.debug("looking for ip address: " + ipAddress + " in line: " + path); - return !(path.indexOf(ipAddress) > -1); - } else - return hasExcPattern(path); + public boolean isFiltered(String path,TestElement sampler) { + String ipAddr = getIpAddress(path); + CookieManager cm = getCookieManager(ipAddr); + ((HTTPSampler)sampler).setCookieManager(cm); + return false; } + + protected CookieManager getCookieManager(String ipAddr) + { + CookieManager cm = null; + // First have to release the cookie we were using so other + // threads stuck in wait can move on + synchronized(managersInUse) + { + if(lastUsed != null) + { + managersInUse.remove(lastUsed); + managersInUse.notify(); + } + } + // let notified threads move on and get lock on managersInUse + if(lastUsed != null) + { + Thread.yield(); + } + // here is the core routine to find appropriate cookie manager and + // check it's not being used. If used, wait until whoever's using it gives + // it up + synchronized(managersInUse) + { + cm = (CookieManager)cookieManagers.get(ipAddr); + if(cm == null) + { + cm = new CookieManager(); + cookieManagers.put(ipAddr,cm); + } + while(managersInUse.contains(cm)) + { + try { + managersInUse.wait(); + } catch (InterruptedException e) { + log.info("SessionFilter wait interrupted"); + } + } + managersInUse.add(cm); + lastUsed = cm; + } + return cm; + } /* * (non-Javadoc) @@ -162,4 +207,23 @@ public boolean isFiltered(String path) { */ public void setReplaceExtension(String oldextension, String newextension) { } + + /* (non-Javadoc) + * @see org.apache.jmeter.testelement.ThreadListener#threadFinished() + */ + public void threadFinished() { + synchronized(managersInUse) + { + managersInUse.remove(lastUsed); + managersInUse.notify(); + } + } + + /* (non-Javadoc) + * @see org.apache.jmeter.testelement.ThreadListener#threadStarted() + */ + public void threadStarted() { + // TODO Auto-generated method stub + + } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java new file mode 100644 index 00000000000..6dc502bf266 --- /dev/null +++ b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java @@ -0,0 +1,93 @@ +package org.apache.jmeter.protocol.http.util.accesslog; + +import java.io.IOException; + +import org.apache.jmeter.services.FileServer; +import org.apache.jmeter.testelement.TestCloneable; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.ThreadListener; + +public class SharedTCLogParser extends TCLogParser implements TestCloneable { + + public SharedTCLogParser() { + super(); + } + + public SharedTCLogParser(String source) { + super(source); + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + public Object clone() { + SharedTCLogParser parser = new SharedTCLogParser(); + parser.FILENAME = FILENAME; + parser.FILTER = FILTER; + return parser; + } + + /* (non-Javadoc) + * @see org.apache.jmeter.protocol.http.util.accesslog.TCLogParser#parse(org.apache.jmeter.testelement.TestElement, int) + */ + public int parse(TestElement el, int parseCount) { + FileServer fileServer = FileServer.getFileServer(); + fileServer.reserveFile(FILENAME); + try { + return parse(fileServer, el, parseCount); + } catch (Exception exception) { + log.error("Problem creating samples", exception); + } + return -1;// indicate that an error occured + } + + /** + * The method is responsible for reading each line, and breaking out of the + * while loop if a set number of lines is given. + * + * @param breader + */ + protected int parse(FileServer breader, TestElement el, int parseCount) { + int actualCount = 0; + String line = null; + try { + // read one line at a time using + // BufferedReader + line = breader.readLine(FILENAME); + while (line != null) { + if (line.length() > 0) { + actualCount += this.parseLine(line, el); + } + // we check the count to see if we have exceeded + // the number of lines to parse. There's no way + // to know where to stop in the file. Therefore + // we use break to escape the while loop when + // we've reached the count. + if (parseCount != -1 && actualCount >= parseCount) { + break; + } + line = breader.readLine(FILENAME); + } + if (line == null) { + breader.closeFile(FILENAME); + // this.READER = new BufferedReader(new + // FileReader(this.SOURCE)); + // parse(this.READER,el); + } + } catch (IOException ioe) { + log.error("Error reading log file", ioe); + } + return actualCount; + } + + public void close() { + try { + FileServer.getFileServer().closeFile(FILENAME); + } catch (IOException e) { + // do nothing + } + } + + + +} diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java index 1a055e4c4ae..dab041e7b1b 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java @@ -244,7 +244,7 @@ protected int parseLine(String line, TestElement el) { el.setProperty(HTTPSamplerBase.METHOD, RMETHOD); if (FILTER != null) { log.debug("filter is not null"); - if (!FILTER.isFiltered(line)) { + if (!FILTER.isFiltered(line,el)) { log.debug("line was not filtered"); // increment the current count count++; diff --git a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java index a7d89eeda79..057739a1af9 100644 --- a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java +++ b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java @@ -44,12 +44,13 @@ public class JDBCSampler extends AbstractSampler implements TestBean { private static Logger log = LoggingManager.getLoggerForClass(); public static final String QUERY = "query"; + public static final String SELECT = "Select Statement"; public String query = ""; public String dataSource = ""; - public boolean queryOnly = true; + public String queryType = SELECT; /** * Creates a JDBCSampler. @@ -80,7 +81,7 @@ public SampleResult sample(Entry e) { stmt = conn.createStatement(); // Based on query return value, get results - if (isQueryOnly()) { + if (SELECT.equals(getQueryType())) { ResultSet rs = null; try { rs = stmt.executeQuery(getQuery()); @@ -195,17 +196,16 @@ public void setDataSource(String dataSource) { } /** - * @return Returns the queryOnly. + * @return Returns the queryType. */ - public boolean isQueryOnly() { - return queryOnly; + public String getQueryType() { + return queryType; } /** - * @param queryOnly - * The queryOnly to set. + * @param queryType The queryType to set. */ - public void setQueryOnly(boolean queryOnly) { - this.queryOnly = queryOnly; + public void setQueryType(String queryType) { + this.queryType = queryType; } } diff --git a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java index c5400e67918..148f5e689f4 100644 --- a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java +++ b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java @@ -40,15 +40,17 @@ public JDBCSamplerBeanInfo() { createPropertyGroup("varName", new String[] { "dataSource" }); - createPropertyGroup("sql", new String[] { "queryOnly", "query" }); + createPropertyGroup("sql", new String[] { "queryType", "query" }); PropertyDescriptor p = property("dataSource"); p.setValue(NOT_UNDEFINED, Boolean.TRUE); p.setValue(DEFAULT, ""); - p = property("queryOnly"); + p = property("queryType"); p.setValue(NOT_UNDEFINED, Boolean.TRUE); - p.setValue(DEFAULT, new Boolean(true)); + p.setValue(DEFAULT, JDBCSampler.SELECT); + p.setValue(NOT_OTHER,Boolean.TRUE); + p.setValue(TAGS,new String[]{JDBCSampler.SELECT,"Update Statement"}); p = property("query"); p.setValue(NOT_UNDEFINED, Boolean.TRUE); diff --git a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties index 1b90853a521..6ce82079548 100644 --- a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties +++ b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties @@ -3,7 +3,7 @@ varName.displayName=Variable Name Bound to Pool sql.displayName=SQL Query query.displayName=Query query.shortDescription=SQL Query to send to database -queryOnly.displayName=Query Only -queryOnly.shortDescription=If true, will run as a query and not as an update/insert. Otherwise, run as update. +queryType.displayName=Query Type +queryType.shortDescription=Determines if the SQL statement should be run as a select statement or an update statement. dataSource.displayName=Variable Name dataSource.shortDescription=Name of the JMeter variable that the connection pool is bound to. \ No newline at end of file diff --git a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties index 80d12658274..f9522694520 100644 --- a/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties +++ b/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties @@ -4,7 +4,7 @@ dataSource.shortDescription=Nombre de la variable JMeter a la cual est\u00E1 lig displayName=Petici\u00F3n JDBC query.displayName=Query query.shortDescription=Query SQL a enviar a la base de datos -queryOnly.displayName=Solo Query -queryOnly.shortDescription=is true, se lanzar\u00E1 como una query y no como un update/inser. Si no, se lanza como update. +queryType.displayName=Solo Query +queryType.shortDescription=is true, se lanzar\u00E1 como una query y no como un update/inser. Si no, se lanza como update. sql.displayName=Query SQL varName.displayName=Nombre de Variable Ligada al Pool diff --git a/src/protocol/tcp/org/apache/jmeter/protocol/tcp/sampler/TCPSampler.java b/src/protocol/tcp/org/apache/jmeter/protocol/tcp/sampler/TCPSampler.java index bf2fa933312..df4c0a2cfd1 100644 --- a/src/protocol/tcp/org/apache/jmeter/protocol/tcp/sampler/TCPSampler.java +++ b/src/protocol/tcp/org/apache/jmeter/protocol/tcp/sampler/TCPSampler.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2004 The Apache Software Foundation. + * Copyright 2003-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,19 +26,15 @@ import java.net.Socket; import java.net.UnknownHostException; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Properties; -import java.util.Set; import org.apache.jmeter.config.ConfigTestElement; import org.apache.jmeter.util.JMeterUtils; -import org.apache.jmeter.engine.event.LoopIterationEvent; import org.apache.jmeter.samplers.AbstractSampler; import org.apache.jmeter.samplers.Entry; import org.apache.jmeter.samplers.SampleResult; -import org.apache.jmeter.testelement.TestListener; +import org.apache.jmeter.testelement.ThreadListener; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; @@ -46,7 +42,7 @@ * A sampler which understands Tcp requests. * */ -public class TCPSampler extends AbstractSampler implements TestListener { +public class TCPSampler extends AbstractSampler implements ThreadListener { private static final Logger log = LoggingManager.getLoggerForClass(); public final static String SERVER = "TCPSampler.server"; //$NON-NLS-1$ @@ -67,9 +63,6 @@ public class TCPSampler extends AbstractSampler implements TestListener { private final static String ERRKEY = "ERR"; //$NON-NLS-1$ key for HashMap - private static Set allSockets = new HashSet();// Keep track of connections - // to allow close - // If set, this is the regex that is used to extract the status from the // response // NOT implemented yet private final static String STATUS_REGEX = @@ -142,7 +135,6 @@ private Socket getSocket() { log.debug(this + " Timeout " + getTimeout() + " NoDelay " + getNoDelay()); //$NON-NLS-1$ log.debug("Created new connection " + con); //$NON-NLS-1$ cp.put(TCPKEY, con); - allSockets.add(con);// Save so can be closed } catch (UnknownHostException e) { log.warn("Unknown host for " + getLabel(), e);//$NON-NLS-1$ cp.put(ERRKEY, e.toString()); @@ -302,22 +294,23 @@ public SampleResult sample(Entry e)// Entry tends to be ignored ... } } } - } catch (Exception ex) { + } catch (IOException ex) { log.debug("", ex); res.setResponseCode("500"); res.setResponseMessage(ex.toString()); - } - - // Calculate response time - res.sampleEnd(); - - // Set if we were successful or not - res.setSuccessful(isSuccessful); + closeSocket(); + } finally { + // Calculate response time + res.sampleEnd(); + + // Set if we were successful or not + res.setSuccessful(isSuccessful); + } return res; } - /** + /** * @param rc * response code * @return whether this represents success or not @@ -332,69 +325,25 @@ private boolean checkResponseCode(String rc) { return true; } - private void disconnectAll() { - synchronized (allSockets) { - Iterator i = allSockets.iterator(); - while (i.hasNext()) { - Socket socket = (Socket) i.next(); - try { - socket.close(); - } catch (IOException e) { - log.warn("Error closing socket ", e); - } finally { - i.remove(); - } - } - } - } - - /* - * (non-Javadoc) - * - * @see org.apache.jmeter.testelement.TestListener#testStarted() - */ - public void testStarted() // Only called once per class? - { - log.debug(this + " test started"); - } - - /* - * (non-Javadoc) - * - * @see org.apache.jmeter.testelement.TestListener#testEnded() - */ - public void testEnded() // Only called once per class? - { - log.debug(this + " test ended"); - disconnectAll(); - } - - /* - * (non-Javadoc) - * - * @see org.apache.jmeter.testelement.TestListener#testStarted(java.lang.String) - */ - public void testStarted(String host) { - log.debug(this + " test started on " + host); - } - - /* - * (non-Javadoc) - * - * @see org.apache.jmeter.testelement.TestListener#testEnded(java.lang.String) - */ - public void testEnded(String host) { - log.debug(this + " test ended on " + host); - disconnectAll(); - - } - - /* - * (non-Javadoc) - * - * @see org.apache.jmeter.testelement.TestListener#testIterationStart(org.apache.jmeter.engine.event.LoopIterationEvent) - */ - public void testIterationStart(LoopIterationEvent event) { - log.debug(this + " test iteration start on " + event.getIteration()); - } + public void threadStarted() { + log.debug("Thread Started"); + } + + private void closeSocket() { + Map cp = (Map) tp.get(); + Socket con = (Socket) cp.remove(TCPKEY); + if (con != null) { + log.debug(this + " Closing connection " + con); //$NON-NLS-1$ + try { + con.close(); + } catch (IOException e) { + log.warn("Error closing socket "+e); + } + } + } + + public void threadFinished() { + log.debug("Thread Finished"); + closeSocket(); + } } diff --git a/test/src/org/apache/jmeter/save/TestSaveService.java b/test/src/org/apache/jmeter/save/TestSaveService.java index d237617f931..8719343e43a 100644 --- a/test/src/org/apache/jmeter/save/TestSaveService.java +++ b/test/src/org/apache/jmeter/save/TestSaveService.java @@ -67,11 +67,21 @@ public void testLoadAndSave() throws Exception { // fail, because the order of the properties within each // test element may change. Comparing the lengths should be // enough to detect most problem cases... - if (len != out.size()) { + int outsz=out.size(); + // Allow for input in CRLF and output in LF only + int lines=0; + byte ba[]=out.toByteArray(); + for(int j=0;jHow-to for JMS samplers
  • Bug 35525 - Added Spanish localisation
  • Bug 30379 - allow server.rmi.port to be overridden
  • +
  • enhanced the monitor listener to save the calculated stats
  • +
  • Functions and variables now work at top level of test plan
  • Bug fixes:

      @@ -81,6 +83,8 @@
    • Fixed webservice sampler so it works with user defined variables
    • Fixed screen borders for LDAP config GUI elements
    • Bug 31184 - make sure encoding is specified in JDBC sampler
    • +
    • TCP sampler - only share sockets with same host:port details; correct the manual
    • +
    • Extract src attribute for embed tags in JTidy and Html Parsers

    Version 2.0.3

    diff --git a/xdocs/usermanual/component_reference.xml b/xdocs/usermanual/component_reference.xml index 9f699c46941..df2d6aedfa8 100644 --- a/xdocs/usermanual/component_reference.xml +++ b/xdocs/usermanual/component_reference.xml @@ -80,6 +80,9 @@ JMeter assumes the FTP server is listening on the default port.
    them. This can save you time if you have a lot of HTTP requests or requests with many parameters.

    +

    There are two versions of the sampler + - one uses the default Java HTTP implementation, the other uses Commons HttpClient

    +

    If the request requires a login authorization, you will also have to add an Configuration Element. And, if the request uses cookies, then you will also need an @@ -614,8 +617,15 @@ TBC

    The TCP Sampler opens a TCP/IP connection to the specified server. It then sends the text, and waits for a response. - Once established, the same connection is re-used by the Sampler that created it. - Connections are not shared between Samplers, even in the same thread. +

    + Connections are shared between Samplers in the same thread, provided that the exact + same host name string and port are used. + To force a different socket to be used, change the hostname by changing the case + of one of the letters, e.g. www.apache.org and wWw.apache.org will use different + sockets. +

    + If an error is detected, the socket is closed. + Another socket will be reopened on the next sample.

    The following properties can be used to control its operation:

    diff --git a/xdocs/usermanual/jmeter_distributed_testing_step_by_step.pdf b/xdocs/usermanual/jmeter_distributed_testing_step_by_step.pdf index 73505b2864c8d500471407c7c2126f1f2b349daa..e338dd92a5240033882b5f47931450854a6115e9 100644 GIT binary patch delta 19853 zcmY(LbzD?W`0qs;=`LaEj@_l3rMpW~x{+><(yf4ilynFp5`wfeNH@|Y-QC>9?~UI* z?>}~5Gjk@M&ok#t?MC*XwthtWpaaZBuk+ysQhx%D+(9psE&KS`-yJME_pY@|h=KUg z9NQ0+g2WjNJrfi!1m0O~`5wunmTDENChvsE?l&-qWb?IFG+{SggC9 zY4dwj2L@DLJArSSbk#`u8}f|MpZ%@uY(!Fff80_rmmHRgjc_Lhn_wm!wgMyF|O*S^)~X|9C)#`}D~%PgONs>=AIA+jr7 zyGr(45Zm%)mlwe>`n#MT%$<42-^$8I-F~w6;;GAKbnf!kHdy4Z`isN>5DaWGyPX-% z4@^acNbWMqA4z(ikxr)DUH!?4s4#Q<_3~0HxQDG6^+yIXE)8{51XAG0-Wu*HOUTc* z0Gm;?dU^E~g?=r`%nRIgj#$Fyb`Z8CEF%Ad1vTHGT9<~BKoShik0;WO^KO}h4nq+m zbi1rmKl?a8p!RrCL|QziBp41khlTpM_0$iS zjUR9PI)3>4P_9Bjl10t^oEp&WD>ghWFr4jfvN@Rv5|9zdqOH+m0{Ya@#4t>8dj>2G z34F}=<<@Yp{jzS+(35qx+y4GarVQNo5K`FRM|XXj7Ug`cy#V>tnERnGh)V6c-)hSC zj%et*>~zQZyFx>`@@7o!TJLy$kJEN*m6E#k!;+WpcTx?!4$_skvVLdF7U5hw2`2=4 z-3oHLK|Fs^eZT|7guojJWVmbRooOa7=t>Y5t##RQh3eseGO(VI7xEO_fo zsIOeM`8tpC=jgB#k6XXfGs4Fz(j-Y$hH=?!frkYD@}FHTW*{49>19Q%za-ENi1HB1*vEZbww zBDwWzb_l)|Y4N0P;7HoAThbseW>al`9*^O2bkJTC^EOHG**rnI)s*k%qCnW~w<>&(uPU9DE^55+vPHkYMFyiI+B%1>tevb4#xZlGxM9HCXt8te+ zOT>Xed|7l{=o0M_8OOYir)|3_<@CQc^&bwIJ%Kr&^e&N7!I;CAi zJ1v1X6sJLs6IQ2$Vbr}P?50$r?fI3lEfp&tiO%KK$=kY}uZS3U#3?uCEJFq3%=Aq} z`=#SKo$`ICnv-ie<9L`$Bn=oTcf=G%(hV9cjJ>l!g1b5eDgoiNw5c;{5FIbw84Qb* zhII+$)zFu-JcY`FXZ@yz_sYT}Cj;36^pTv?zE~R-poPb4b3=ww>sSURPkSeat3U(O z7vT+h%a*+nl6_5H+b`aq^#<1U(r?gwHvtzZ4V&7>d`XGE{u+#Rzm)ft$#Wuia?}@w ztUd|>UT!`Yo*uqN>rzu7EzfLLArgl`vf0_Rr;@+Ymaxhe4QBuP^gdol{&OLgIwiNz z)w2QXEWW6>tGTx7X;+;i83a-ZMYfS)@iW+ZhaoISTb3PERcJ(> z+(;-iyEBNnHQP>@y{LwW@kP|tRhoed3Uwq2t#4{>?YsSlDy>f4jmx_xk!#sIf>|=e z&%*hB4_=taqqtVhIP81a<+z(qc>PpE3u^W2vPRH>zSoh`{o+{iy{V$=u++oYe%y(E z2vDGp?HQ^W6WPqM(~(;^XlEDy*gh1ZVc ztChGlLdNMTY#LK#WLym{Z@;ArNDOxIr%F_E;Do5Cj4NSIc71 zkpNk*=1Lc2F$2ZlJhTmmeU9DOAVUiaJPsE_!9GLu3xT~#OnnIAwb(W9pL11w&6E}+ zOIMzdvfi+=G@Z&7LJ{QQ!fknUB|LR;dw#i+N*&(1`R8J3THMt1+hGOQk8(@~?88

    0FUYH@XJ;cQ^-&Q-{t5+W;W2XV^~lrT>hH}GDx>lKGxe|Y)g2OQM^*t zm>^)Xqp%#B9_Ux(NPb*b_M+e$>d%)4NF>Kw3+4R$^?S6^B*Y6LwrR#$s6BPxW82XF zoSHL>j1o)bVEIfZFuvh6dQi17T2mMx8S%0cubhSs1Ovd^!sJ zr3ZD58?LH+FwiF?v7Xnt8Dcw(QdAL&)G_^NM9*R>Xr6 zUwJbs3Uk&EOQbKmR$a@qn18v-t>#iI5XZH7mPy69AZd$%$+tL#n9Zh$K5Kuj#4Bj% zBN0NW3YVjId{SSvlvxJ5a+d{e zAgCBNd%=(?O3}u8<#%3_YOucP2?4Bxa|7ZLEEnie4Uyc2SgUp<^}g$yFfcbD^-jIn z(7Bjzi0U8^vp=H{mToIG8;jfeNQ~cQ`c(nHDNisT;p@_t*GD9ZsEQ zNLNfz#H7M#yhc|LjWu`k#!5z_!_C~(2l;qZ5bKqGW*D}?ZA?o~kj8>|A z#Ycq>=y%E!rlp;sf1^e*WxWCR?uGl23Qk>tT_;u27?pD!UFiw3Q#5fgDXB0u^9y{G ze2jS8DskbQahrInweBylhMdwK`uZE+2BMZX64i#Un!pIZg%#+?K{tas@y0@YFZqLx zH;o#qeW}!R4mh@7XnQyGxOZ}%-!@!1RgOU8li~_D@(KgeB%M5R-86=}V zvUe@*gZ_ABJU}b_Jt`n{aR!;gf9I& z{Q9GBA54QLgOzrX&(Uh$xUd@${TXPk$+KZr4lHJ^M!Jw3;*X66LI@TkU|%d{8P&oU z*uuFDGNon)P&@U$nn6bhN9ip*`sC)^&Z*W;&Sp^RhnUgLjhVLu4PB zl4H7W#Foe81-O;0&?ect)*15~1?w#L2($Vt;$rN&5!cE3F3q=LR17R^VLwmUNCI9@ z4&@3wV46(%)p=nc0`Y6@l?V;nIgDDuwLTv09{z?n_?bs&LAb8&;JGOY6Plyf{Q4=* zCSMB1uS9biOAZz^CjPLJzcfa&8o@)r3>SPGReIw#S-R#O&glkVz3LCd^ zV@NrfHwuT+P)GAOivgk$v03H&L;@W{pD)85EjYf<=_ODW0v9v2q^dx9N%>g(J12&) z?73|cSK(61qX65qn8XGxYRQ7suHwB;x1q(V9ais2mD&52ERT-vVtpgIFI}ub;{&wc zOBtJsEeBLia!x`5-7~(uTSuGh`inx)nZKEVH}M6Gv7xjYLTX`-n}pf+d z^PIx(8~qP7^L?9|1<25dPS24+0&DDqeW^2rq-LkWBW-3h;}=bDW^5}}=yCw6PrG>a zZlKhiFvM(~RSy&G@*;ug*xNQn`ex|qP4HvG9x%MJ0_P}Z%zxp~N%JoUbnrj9 zV98ZTH%GFX#@~;U%oN5K9mar;zd5JpWQTKoqMbA0@yM^J1ml{{NohY?z$iEu_MA#f zID)Ru2~T(XxdX1QPX*>5$r=V8pDAKDc0awXi75I0-|HGI2;s-VuyVHi_xi@d;7>ZV z7s7||J-y07T=G_SHntuhsGvYnHU>kI8Jh|qE5I$wEeL`MJ^=#rLwF!kAgCm-Bp(a} z<$D4o$o~Y07b*Z2gn?wFpTNT41@p_mL!~620D&bTl29;+U-k(Q515aSUlJrK@B~Pj zmm7Y8V7yOYVekm@Lj(jsd{9A2m;eBRNJ&Da1V9i$9&R3~r?Pkir9skCPk^`uo&Z6( zpTNT4gGusB!7IZ31c+aTo1dEp1m)&|@k)ZAuqQxZ2v`aN2JylKq$LGG{CrPfVaP~5 z0pfR-00xqld;%mR$SwU;mcSDr8Q4=purLI{Pk`X%3Bau+C?E}$ z;R8XyP+q7kNSYsh2R{g23pj9_aYMNwASrkarQj0qNk0LS;^UU$<^#dFxh1)IL6XoX z;PrR{e!nyy0BMjy1LBo>0-h9Z4=_lIM}|)p z3<66(frSBvJpq!1+XKc6fr+<+iA zToP%BjFc=7h#&l4B0d2)a1+3h2Fb|E3dl-A16Hn^H0YU#=|G5+Z_bFap2rv9j zc*lbS;+1*g$&egSw#gl8iIHx~Tyz{>+S9^45aP$>vJqbKj-kp=zB9Q^du;eq#LxIN*3pfXQ9 z^<*&o!NSiAo1dCFBQYtpHR`j^oxJA`S{mnC7ShVN1jp>%&zEaN=&6RaKY#pqLd z+}HtM>y0$I;Q=%D_zODfS(3}ilhROg(%JV9B^w&z^2}8q)iI^eySV@R0RDXr5xS$x zn7S1)V=)rm_ol6y$t2PzQ?bhTOXR6nJO(y?q*LE?jwIt1!e?+a5}*g$Yy^*b+6?C8=9R zk8*C%NAbn@xs+c{qXRNUqz`%NF8Y6!VWT=jggrGou?P@TMf;kvPY$UVJb;ta`uZuDq6SjxXAIufL-Npa}mw!*bNLAdm zrR5c1YThEk+o)5Jc)- zq7CwMT@OD2t~Bv;L}|TfUzrp(dYuSO5!_2&_u`;4i-DpN@`F4cEyT-4v$N4JNclSV zTb?J&DXnOM9zUiA$dpQJWbU_c13h9qH(3*l`U?G&Z3JA&R1_a&Betk^hlmKRtVgzt z-DDV_AF2zB$;m2AAwM~gtU#-HR zvCu8@LZ`uTh1djU&m%cTcel8)+VyREPC78a=UAgC$2Go}>8aOi-t)Z_mhd@)g9BD!%XvcP*Ya zMf|}v;B8Q^7-cl5&hjg_QofJGN)XH#%N=8MfXv?m-Zjbe<)X4t>_4hBeszD`m zGLJQ0_lvV0-rFx^-G4rskXKrYLA27F`Dae)^X2fYlt;m*ehtHqsO8m|d-XLyvqdb2 zhC$kV4d?ZS z5?~MX)jefZ>wW=Yh4>97CRgIMYsl1#s#5_d_01J6I3^mAsn7c(9=NCKnGpEzx*Winat zDGKdXPUrojUdN_kzWHs*+!`yX!S%^>$;glsX6Ndj{&|NchqR^h(*(-TXF7r*OjSS8 z2>X4YWh7sdbH7HWZlmjG2T+HEa?9pKk}xtB9p6dg&2|lJkS`Y?j}s8QNGi^mWQUQg zfpdPM;t&0ykv14(TIvC4XkteT_y|Q&2rps5KVzvpKw^;#1lDJi>hlCqx`kxs#lEt% zk|Hi7X^cxf`Oe(kX!%uN*>KxWo!R==Z$HsDfnUM1p}$xj^l?0u$Tkb?AWEaJ+*RnKTxIHiPLDNlFX+b5WL2Mc)knODVr?8!^@SL9ku zHfqbahcwwrLBzsRrRBX=`#IOKtQ8slAlz*O@Gltz6uz>)1SCccV7`kisx%mr5ZR24 zZKYM8#eH}s*fn0snmmsl!tXlWAkSmwSThWeBFtB$zEKf6P_pI16LI+lk6nFuzNUD3 z#&96&!j*IgtdVT$q}P?OO9l8Zc2gHE z_cUo^Tg^yN*j2d&M@B@)4j~VoH=(u1tc3!htNULd7>O{etG|HnU`*sb3g-nbQ_dDU zXJ(cF(^3Q-c5FgFj~WoYWEJas6384aI>7u2-gvOywwFIUdV!CqOZ>RVJ&UTvy3pWw zYWq=Bb(`vnB|PQM3~4n1^9YMM+;}?^r=N}Fm<>7AY#`%SB57H}){L%#Nh~&fJH>yL z0Y*ddt_N8Y#-)gS>^QJg%2!kiM3rAjgIdq_2q~p#OqdFHyIE>4qsWu3N~BW3hSJQ? zG!{-ZM3&_rEwOR=j=GO&(`ut?tu}YX^@o-8DMIH>??}EIfu`hVlFt*rK75sbwo|NL z826#}C)^E$D#k($y1z>_Te=wlDSaPOfJ%kqZqXZ%YE9op|P&Zhb5StmBG$eMYZSOG4CYwAWozUceCVnBnpV=Vcjh2VXt51Ht$!` zrwcW;sb!|Sc~u#3n+^;h{_?#qykS{CKEWUsXYJSQJ&*Ab8mSa$s`4AVrBZ5V0ro!e zAGf8x{H?XT*h!eY|E7XQExM%g&@_r;u(~6#LWG4D$H%58@^=yEq4a4KmS-($2EPo! zscP;LLFAW*mo;vXXdIp|)%VXtSrb_UpE*$}zZ9qs_R8}nq01#z7`SbHxEy}8M=rnU z!Ap+|H7De-*lERU@D?ok<;X)A;sY!KduqTv7x3sJriyc;l1YTs1>#s!0&bu{G3optB#|`hCaHkvDndS)JHw+0yqo1Qf2}}I4j}0G!IOFJ@Xn^I z*mms;!G5Uu4)^lSNd{wEgP31-SLF+4;tyZr5<-mZ7?Gk;e_;=|Ob36k-@zwckDC%R z3W&CsOzg4VdfhVN8mF3tBz)JKM)Mc#;;OOc-gs5(BMO6HMd$=)QI3}H?5os4zeA-7 zw|<3y==#JwoxMd>iD+P($RkT5O=aT>)BeYK;HuWwzfXgstV_;k$00KOkvU3e2Km9O zT9n$SSY>-(y~$7__Z{4gO+g+rcdExM@phABh?X|ZFKVKwXf$ItYBd6)kt|ta7p+i@ zFEJ`B4q0wE7Xr$z2v2vEb&@iM8CXcW`q6yt2zG^HbTfkTuK+@ZK^n@H?A0UhEedMf z+oEq7LOYx}q^im`s9EQTc-g#HRA`kkJxh;m^tmZ=1@nRR)W@svg4PM;D~~&0x{t z^Y8gVTg-rxeX+&=H`3h7SQVT5S*TEXIN#kJ_NUxEFowc?gmadq#YSM!U+MT{NpPe7 zp)+B6H{KD4XXWw3Xk$qj`M&WlQ}#Z|yRMF{FLCDC{DXPP%#L^s$uC27xrze=n2JaP zYyQU|6#$OE|8V!MS^)%M*2;ym4$;!6s*6&W0sL`EyNk>lKVT}*yc$R>s?}q2KeoO5 zbVd2Md~Iz)@Nu0U-fSgxRtYr@UFW$mChTBD1qQFt(%=z66!&nTjb$&MfCoR_gXKr0$khO-L_D zHYZc7uu4^1(Ms}J$oW~HY23f8HU5`fq>r#oc7dfdypefu;@3Ur)nPt*R~N@yx7QBN z`7ZYU7XI;xet`7Ny4)NEMWA=>GRbIn_teW1fd*bVY)awgFB_@f8!WVZ6lex&cUzF&MV)&P|=>MsA& zBIkV&t9gmru!AM=Y97V1+THt%_J}%_d5B4CopiR#;{_lSD}Imr+a!=^?U*t0SXU@l z14;WY=I37Xocd>zX_TD9Yd;{AgvV~-nai+BvfUvadX*%^vr+z$!arg#JeBh-x(ogv zDm(k^$2fAsqJC8cIQdcNFogvv>cI6GIqIIPnK^YYe@gil5!J7WZ?Dewk8xHePt9_U z=9$G@S37{?x|QkvH6~TcI>MX0|1)rMrG?fT7;z;^*^(49F%mPXaeR!C*)lg`PB&ku z56&CFyg4*3X_a3VNf)le$}HmJ+{#4jDi^3Wgsa zpYpFvW^8)m9-;YEEIV*zbWI;sMqKfITmou{{fcdM8P|e_KL6#S0$Gqex3NEaKC_yK zpiwNK*mTs8z{e7gUp(fzcf!I##?<<)Sp-ESekJ1B>dbM5#4c%zm-$`ivGwtw-f$}h z5;ZKYj!;&mlB^I*865X*_swT^nDOV?Z#Gdp^~lo+Acv4Q_TzfMc1Rem^eQ=3VY?<8l2F0@}A�r8gU00_+#k>TD<37{Y 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 delta 10746 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 P4q#(2Gs|lzU}O9rww&Gd diff --git a/xdocs/usermanual/jmeter_distributed_testing_step_by_step.sxw b/xdocs/usermanual/jmeter_distributed_testing_step_by_step.sxw index 40a3840893871237a29f4e3f5659b38f431331c9..755c61c11bfc544cb4c5a5edab9757ab3ab090a9 100644 GIT binary patch delta 16508 zcmajG2Ut^0(=ePy5<&|-^b$&_hTatjgen38QYBPDdJzyyfY6(CLFpYtM4A+pjz|*_ z5fGFvs30mR*84xkqLbN1})?Ci|!?Ci|u_kBoN6qLc-1PWsVQ7;F^ z-Ao1@C~&)PvB6Hgx2STI@g6@c$#3x=KMYyGgcyM;dGF7aMmRAL2q%_3Wx>lTr>Kml zUUI7Xx{7*gI*Ri0*?(DZFoB665I7wP1@JF5+w){LsgS{c%uTKIzq2v)^z`saD-zr< zojP@@q@=_tx#Hu;j}8eX&z?OKl2=bmOf(6k+`D&g0R-yq?ml}p)35xoV*P}ikxfZq z)tfhO=;-Lg#l8W{GNpl zpl&xYv4C7qfS_Y2eYq`h((n~C3b+U~(DCu{@bK_4H0=XMWPN=-6BE;`#W!)$87EGh zu(q}?DXevNc4lT~_Vn~T0fju>{wyLQA}lO?*xjuGgD`T>6FgP3`Dk=(v!Vm~#G&!D?h3(8~ zYYhzzJ3BiF1aeACkDi|1z`%ffCGk4BYaURBzyA8``u(56AP~06NKeNqV$s!BEfzF} zVgX^boZx6}`iee71`t}CAdOkbW2MdC=1B#C6}eNb!2g#UeT+pQ8iv&dF;GbE9GD{# z3|Z=ePK6``BGx2{y+n~^lQwxB515EiAZ~cyNFO~Nk-u?MvzZ0TOzonf9^>grG9P8s z!$V+Gfk@&GJ8ysRN@yLj!N1Z2A&3D(yf85A4Ml~7Lgm<}(5>gEsIP2iXUBP29H9

    CqI(K-Hg9}=xyfz>?b$3tR7KKr?b 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 Cj^OJ6