diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index be85ffc28d..83c7f53618 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,10 +22,12 @@ jobs: uses: actions/setup-java@v1 with: java-version: 14 - - name: 'Test' - shell: bash - run: | - ./gradlew --no-parallel build -x distTar -x distTarSource + - uses: burrunan/gradle-cache-action@v1 + name: Test + with: + job-id: jdk14 + multi-cache-enabled: false + arguments: --scan --no-parallel build -x distTar -x distTarSource mac: name: 'macOS (JDK 14)' @@ -38,6 +40,9 @@ jobs: uses: actions/setup-java@v1 with: java-version: 14 - - name: 'Test' - run: | - ./gradlew --no-parallel build -x distTar -x distTarSource -Dskip.test_TestDNSCacheManager.testWithCustomResolverAnd1Server=true + - uses: burrunan/gradle-cache-action@v1 + name: Test + with: + job-id: jdk14 + multi-cache-enabled: false + arguments: --scan --no-parallel build -x distTar -x distTarSource -Dskip.test_TestDNSCacheManager.testWithCustomResolverAnd1Server=true diff --git a/README.md b/README.md index cce7bfa8d8..f921538287 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ The following requirements exist for running Apache JMeter: - Java Compiler (*OPTIONAL*): - A Java compiler is not needed since the distribution cludes a + A Java compiler is not needed since the distribution includes a precompiled Java binary archive. > **Note** that a compiler is required to build plugins for Apache JMeter. @@ -126,7 +126,7 @@ The following requirements exist for running Apache JMeter: ### Windows -For Windows there are also some other scripts which you can drag-and-drop +For Windows, there are also some other scripts which you can drag-and-drop a JMX file onto: - `jmeter-n.cmd` - runs the file as a non-GUI test @@ -160,7 +160,7 @@ _This is useful for testing what happens if the optional jars are not downloaded by other JMeter users._ If you are behind a proxy, you can set a few build properties in -`~/.gradle/gradle.properties` for gradle to use the proxy: +`~/.gradle/gradle.properties` for Gradle to use the proxy: ```properties systemProp.http.proxyHost=proxy.example.invalid @@ -193,14 +193,16 @@ The output artifacts (jars, reports) are placed in the `build` folder. For instance, binary artifacts can be found under `src/dist/build/distributions`. The following command would compile the application and enable you to run `jmeter` -from the `bin` directory. Note: it completely refreshes `lib/` contents, -so it would remove clustom plugins should you have them installed. +from the `bin` directory. + +> **Note** that it completely refreshes `lib/` contents, +so it would remove custom plugins should you have them installed. ```sh ./gradlew createDist ``` -Alternatively you could get Gradle to start the GUI: +Alternatively, you could get Gradle to start the GUI: ```sh ./gradlew runGui diff --git a/bin/jmeter.properties b/bin/jmeter.properties index 96efa711f1..c3e78c3512 100644 --- a/bin/jmeter.properties +++ b/bin/jmeter.properties @@ -140,6 +140,14 @@ # See https://bz.apache.org/bugzilla/show_bug.cgi?id=52026 for details # N.B. the LAF can be defined in user.properties. +# Enable custom window chrome for Darklaf Look and Feels. +# defaults to false +# darklaf.decorations=false + +# Enables the unified menubar for Darklaf Look and Feels. +# defaults to true +# darklaf.unifiedMenuBar=true + # LoggerPanel display # default to false #jmeter.loggerpanel.display=false diff --git a/build.gradle.kts b/build.gradle.kts index 4f0c6059b0..2bbc5f45b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,8 +15,7 @@ * limitations under the License. */ -import com.github.spotbugs.SpotBugsPlugin -import com.github.spotbugs.SpotBugsTask +import com.github.spotbugs.snom.SpotBugsTask import com.github.vlsi.gradle.crlf.CrLfSpec import com.github.vlsi.gradle.crlf.LineEndings import com.github.vlsi.gradle.crlf.filter @@ -188,11 +187,15 @@ sonarqube { } } -fun SonarQubeProperties.add(name: String, value: String) { - properties.getOrPut(name) { mutableSetOf() } +fun SonarQubeProperties.add(name: String, valueProvider: () -> String) { + properties.getOrPut(name) { mutableSetOf() } .also { @Suppress("UNCHECKED_CAST") - (it as MutableCollection).add(value) + (it as MutableCollection).add(object { + // SonarQube calls toString when converting properties to values + // (see SonarQubeProperties), so we use that to emulate "lazy properties" + override fun toString() = valueProvider() + }) } } @@ -239,7 +242,11 @@ if (enableSpotBugs) { sonarqube { properties { spotBugTasks.configureEach { - add("sonar.java.spotbugs.reportPaths", reports.xml.destination.toString()) + add("sonar.java.spotbugs.reportPaths") { + // Note: report is created with lower-case xml, and then + // the created entry MUST be retrieved as upper-case XML + reports.named("XML").get().destination.toString() + } } } } @@ -321,8 +328,14 @@ allprojects { } val sourceSets: SourceSetContainer by project if (sourceSets.isNotEmpty()) { + val checkstyleTasks = tasks.withType() + checkstyleTasks.configureEach { + // Checkstyle 8.26 does not need classpath, see https://github.com/gradle/gradle/issues/14227 + classpath = files() + } + tasks.register("checkstyleAll") { - dependsOn(tasks.withType()) + dependsOn(checkstyleTasks) } tasks.register("checkstyle") { group = LifecycleBasePlugin.VERIFICATION_GROUP @@ -341,11 +354,11 @@ allprojects { } } } - apply() + apply(plugin = "com.github.spotbugs") spotbugs { - toolVersion = "spotbugs".v - isIgnoreFailures = ignoreSpotBugsFailures + toolVersion.set("spotbugs".v) + ignoreFailures.set(ignoreSpotBugsFailures) } if (!skipAutostyle) { @@ -457,21 +470,17 @@ allprojects { options.encoding = "UTF-8" } withType().configureEach { - from(source) { - include("**/*.properties") - filteringCharset = "UTF-8" - // apply native2ascii conversion since Java 8 expects properties to have ascii symbols only - filter(org.apache.tools.ant.filters.EscapeUnicode::class) - filter(LineEndings.LF) - } - // Text-like resources are normalized to LF (just for consistency purposes) - // This makes to produce exactly the same jar files no matter which OS is used for the build - from(source) { - include("**/*.dtd") - include("**/*.svg") - include("**/*.txt") - filteringCharset = "UTF-8" - filter(LineEndings.LF) + filteringCharset = "UTF-8" + eachFile { + if (name.endsWith(".properties")) { + filteringCharset = "UTF-8" + // apply native2ascii conversion since Java 8 expects properties to have ascii symbols only + filter(org.apache.tools.ant.filters.EscapeUnicode::class) + filter(LineEndings.LF) + } else if (name.endsWith(".dtd") || name.endsWith(".svg") || + name.endsWith(".txt")) { + filter(LineEndings.LF) + } } } afterEvaluate { @@ -506,6 +515,11 @@ allprojects { exceptionFormat = TestExceptionFormat.FULL showStandardStreams = true } + + outputs.cacheIf("test outcomes sometimes depends on third-party systems, so we should not cache it for now") { + false + } + // Pass the property to tests fun passProperty(name: String, default: String? = null) { val value = System.getProperty(name) ?: default @@ -522,10 +536,11 @@ allprojects { description = "$description (skipped by default, to enable it add -Dspotbugs)" } reports { - html.isEnabled = reportsForHumans() - xml.isEnabled = !reportsForHumans() - // This is for Sonar - xml.isWithMessages = true + // xml goes for SonarQube, so we always create it just in case + create("xml") + if (reportsForHumans()) { + create("html") + } } enabled = enableSpotBugs } diff --git a/buildSrc/checksum.xml b/buildSrc/checksum.xml index df0e465cad..d2f817f881 100644 --- a/buildSrc/checksum.xml +++ b/buildSrc/checksum.xml @@ -54,6 +54,9 @@ B6F05B16B1826C3FD50D80AFD14965566B27313691A111EC8589867678D6B92AF827E5E098BECCE8F911D8B479AF5303322A6B2ECA971D1A2D985F42BD1AAA87 + + FFC641492549525D06D6788A3D708C7108AFA06C35796EF0B3A71541672162A7BAFDE6DC8F90F63D524BA33D1447C172C589491C16CF3B91C2C3E150AAB71B46 + 3BA508C96EE0985865C9E86EBE803D1903835F7C198EB15723CDC86B028D6C82DBB7E96ECB01CEFAD7CC6EC7023155676D034B6B730A911D0FFE9F91FE532592 diff --git a/buildSrc/subprojects/batchtest/batchtest.gradle.kts b/buildSrc/subprojects/batchtest/batchtest.gradle.kts index 83ad498635..3c8b01eeef 100644 --- a/buildSrc/subprojects/batchtest/batchtest.gradle.kts +++ b/buildSrc/subprojects/batchtest/batchtest.gradle.kts @@ -17,7 +17,7 @@ */ dependencies { - compile("org.ajoberstar.grgit:grgit-gradle:3.1.1") + implementation("org.ajoberstar.grgit:grgit-gradle:3.1.1") } gradlePlugin { diff --git a/checksum.xml b/checksum.xml index 82223079cb..c04d627275 100644 --- a/checksum.xml +++ b/checksum.xml @@ -14,11 +14,13 @@ + + @@ -39,6 +41,7 @@ + @@ -77,8 +80,8 @@ - + @@ -130,6 +133,7 @@ + @@ -169,6 +173,9 @@ 37EC6DFBDDD0458A2A341E371D038B3B0A25AB2F9006B295EBCF3BDE873A8D02C8CDAA6C69D875500F67593C618E8EF62C0269FB2F7F527A1367A7D6B8EA3CBE + + 36557C04B8FC7DC446B5FBDD6383A1B7281708B424C3039E7EC11807F3BB9A5E2764BE4BEC1D717C38595B12D8B291CB2A002D85B5E79DC9A9A4CBD27C58C7BB + E7486B32EF6C9C14FE879814DA5F06CA6ECABF47195063A93E6FC8CD10119244C5A7BC3C71A4760CCE3AFFA9E9736336D345D8ED84EB65153C15683FA6529D92 @@ -238,6 +245,9 @@ 89F8559C281F6BCEA4EB193C49AA8C960A5FE6762DCA0198A7BB04B9D8A846AC52231F77EC16B2E9C0B48D0F6011517B641B5B93279B24EEF65EE22E2C1167F3 + + AFC41AFD611214C500CACA4D826652E38D5DDD1BCF182D935AFFD2B5B8DF26650DFEB76E4C06C2D46593E1FB86BB4D093438E8E862666E64AB32FB4B6EFEA30D + 9A98E493C4D771322B1331EC05AB0E363A83D8AC2AF8018D96A44DF2BF5BFC97D33EBE6F6F93E46AB10BF1536F0C29E9D9569318ED49BC18B4E96B1A8B476D37 diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index e82b6bba94..75128be884 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -68,6 +68,12 @@ + + + + + + @@ -178,10 +184,6 @@ - - - - diff --git a/gradle.properties b/gradle.properties index 39a5394056..4744760eb4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,6 +16,10 @@ # org.gradle.parallel=true +# Build cache can be disabled with --no-build-cache option +org.gradle.caching=true +#org.gradle.caching.debug=true + # See https://github.com/gradle/gradle/pull/11358 , https://issues.apache.org/jira/browse/INFRA-14923 # repository.apache.org does not yet support .sha256 and .sha512 checksums systemProp.org.gradle.internal.publish.checksums.insecure=true @@ -28,28 +32,28 @@ kotlin.parallel.tasks.in.project=true jmeter.version=5.3.1 # Tools -checkstyle.version=8.22 +checkstyle.version=8.35 jacoco.version=0.8.5 -spotbugs.version=3.1.12 +spotbugs.version=4.1.2 velocity.version=1.7 # Plugins com.github.autostyle.version=3.0 -com.github.spotbugs.version=2.0.0 +com.github.spotbugs.version=4.5.0 com.github.vlsi.checksum-dependency.sha512=4D1A76F38F327CEA0C723D9BDD9ABFE16933769052F47BCECD555DDD1A6CD0A9C21E3CC8F9E1B92780F9B443070D4844889EE9ECB0690D30E50AAB085096D8E1 com.github.vlsi.checksum-dependency.version=1.70 com.github.vlsi.vlsi-release-plugins.version=1.70 org.jetbrains.gradle.plugin.idea-ext.version=0.5 org.nosphere.apache.rat.version=0.5.3 -org.sonarqube.version=2.7.1 +org.sonarqube.version=3.0 # Dependencies accessors-smart.version=1.2 -activemq.version=5.15.11 +activemq.version=5.16.0 apache-rat.version=0.13 apiguardian-api.version=1.1.0 asm.version=7.3.1 -bouncycastle.version=1.64 +bouncycastle.version=1.66 bsf.version=2.4.0 bsh.version=2.0b6 caffeine.version=2.8.0 @@ -58,20 +62,19 @@ commons-codec.version=1.14 commons-collections.version=3.2.2 commons-collections4.version=4.4 commons-dbcp2.version=2.7.0 -commons-io.version=2.6 +commons-io.version=2.7 commons-jexl.version=2.1.1 commons-jexl3.version=3.1 commons-lang.version=2.6 -commons-lang3.version=3.10 +commons-lang3.version=3.11 commons-math3.version=3.6.1 -commons-net.version=3.6 -commons-pool2.version=2.8.0 -commons-text.version=1.8 -darklaf.version=2.4.2 -#darklaf.version=latest.integration +commons-net.version=3.7 +commons-pool2.version=2.8.1 +commons-text.version=1.9 +darklaf.version=2.4.5 dec.version=0.1.2 dnsjava.version=2.1.9 -equalsverifier.version=3.1.13 +equalsverifier.version=3.4.2 freemarker.version=2.3.30 ftplet-api.version=1.1.1 ftpserver-core.version=1.1.1 @@ -98,8 +101,8 @@ json-smart.version=2.3 jsoup.version=1.13.1 jtidy.version=r938 junit4.version=4.13 -junit5.version=5.6.0 -log4j.version=2.13.1 +junit5.version=5.6.2 +log4j.version=2.13.3 mail.version=1.5.0-b01 miglayout.version=5.2 mina-core.version=2.0.19 @@ -110,7 +113,7 @@ oro.version=2.0.8 ph-commons.version=9.4.1 ph-css.version=6.2.2 rhino.version=1.7.12 -rsyntaxtextarea.version=3.1.0 +rsyntaxtextarea.version=3.1.1 Saxon-HE.version=9.9.1-7 slf4j.version=1.7.30 spock-core.version=2.0-M2-groovy-3.0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 490fda8577..e708b1c023 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2ae6d63624..0194212dc8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -17,7 +17,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=0f316a67b971b7b571dac7215dcf2591a30994b3450e0629925ffcfe2c68cc5c -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionSha256Sum=e6f83508f0970452f56197f610d13c5f593baaf43c0e3c6a571e5967be754025 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7d95..4f906e0c81 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 62bd9b9cce..107acd32c4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle.kts b/settings.gradle.kts index 77f8f5b237..2e178c0c07 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,6 +32,10 @@ pluginManagement { } } +plugins { + `gradle-enterprise` +} + // This is the name of a current project // Note: it cannot be inferred from the directory name as developer might clone JMeter to jmeter_tmp folder rootProject.name = "jmeter" @@ -92,6 +96,18 @@ if (property("localReleasePlugins").toBool(nullAs = false, blankAs = true, defau includeBuild("../vlsi-release-plugins") } +val isCiServer = System.getenv().containsKey("CI") + +if (isCiServer) { + gradleEnterprise { + buildScan { + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + tag("CI") + } + } +} + // Checksum plugin sources can be validated at https://github.com/vlsi/vlsi-release-plugins buildscript { dependencies { @@ -107,6 +123,8 @@ buildscript { // Note: we need to verify the checksum for checksum-dependency-plugin itself val expectedSha512 = mapOf( + "F7040C571C2A2727F2EED4EA772F5A7C5D9CB393828B7A2331F7167E467429486F5F3E9423883FE9A6D652FFB0484EAE722CDFB46D97180209BCBEEBF9C25DE3" + to "gradle-enterprise-gradle-plugin-3.4.jar", "43BC9061DFDECA0C421EDF4A76E380413920E788EF01751C81BDC004BD28761FBD4A3F23EA9146ECEDF10C0F85B7BE9A857E9D489A95476525565152E0314B5B" to "bcpg-jdk15on-1.62.jar", "2BA6A5DEC9C8DAC2EB427A65815EB3A9ADAF4D42D476B136F37CD57E6D013BF4E9140394ABEEA81E42FBDB8FC59228C7B85C549ED294123BF898A7D048B3BD95" @@ -137,7 +155,7 @@ val violations = .joinToString("\n ") { (file, sha512) -> "SHA-512(${file.name}) = $sha512 ($file)" } if (violations.isNotBlank()) { - throw GradleException("Buildscript classpath has permitted files that were not explicitly permitted:\n $violations") + throw GradleException("Buildscript classpath has files that were not explicitly permitted:\n $violations") } apply(plugin = "com.github.vlsi.checksum-dependency") diff --git a/src/components/build.gradle.kts b/src/components/build.gradle.kts index 51a8b127a3..c183f1bf9a 100644 --- a/src/components/build.gradle.kts +++ b/src/components/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { api(project(":src:core")) - testCompile(project(":src:core", "testClasses")) + testImplementation(project(":src:core", "testClasses")) api("org.apache-extras.beanshell:bsh") { because(""" diff --git a/src/components/src/main/java/org/apache/jmeter/assertions/HTMLAssertion.java b/src/components/src/main/java/org/apache/jmeter/assertions/HTMLAssertion.java index ca2f5d62ef..d9c1a143d1 100644 --- a/src/components/src/main/java/org/apache/jmeter/assertions/HTMLAssertion.java +++ b/src/components/src/main/java/org/apache/jmeter/assertions/HTMLAssertion.java @@ -121,9 +121,9 @@ public class HTMLAssertion extends AbstractTestElement implements Serializable, || (!isErrorsOnly() && warningsAboveThreshold)) { log.debug("Errors/warnings detected while parsing with tidy: {}", errbuf); result.setFailure(true); - result.setFailureMessage(MessageFormat.format("Tidy Parser errors: " + tidy.getParseErrors() - + " (allowed " + getErrorThreshold() + ") " + "Tidy Parser warnings: " - + tidy.getParseWarnings() + " (allowed " + getWarningThreshold() + ")", new Object[0])); + result.setFailureMessage(MessageFormat.format( + "Tidy Parser errors: {} (allowed {}) Tidy Parser warnings: {} (allowed {})", + tidy.getParseErrors(), getErrorThreshold(), tidy.getParseWarnings(), getWarningThreshold())); // return with an error } else if (tidy.getParseErrors() > 0 || tidy.getParseWarnings() > 0) { diff --git a/src/components/src/main/java/org/apache/jmeter/extractor/BoundaryExtractor.java b/src/components/src/main/java/org/apache/jmeter/extractor/BoundaryExtractor.java index 26f6c9a7e6..f11334ed9d 100644 --- a/src/components/src/main/java/org/apache/jmeter/extractor/BoundaryExtractor.java +++ b/src/components/src/main/java/org/apache/jmeter/extractor/BoundaryExtractor.java @@ -203,19 +203,36 @@ public class BoundaryExtractor extends AbstractScopedTestElement implements Post } private String getInputString(SampleResult result) { - String inputString = useUrl() ? result.getUrlAsString() // Bug 39707 - : useHeaders() ? result.getResponseHeaders() - : useRequestHeaders() ? result.getRequestHeaders() - : useCode() ? result.getResponseCode() // Bug 43451 - : useMessage() ? result.getResponseMessage() // Bug 43451 - : useUnescapedBody() ? StringEscapeUtils.unescapeHtml4(result.getResponseDataAsString()) - : useBodyAsDocument() ? Document.getTextFromDocument(result.getResponseData()) - : result.getResponseDataAsString() // Bug 36898 - ; + String inputString = chosenInput(result); log.debug("Input = '{}'", inputString); return inputString; } + private String chosenInput(SampleResult result) { + if (useUrl()) { + return result.getUrlAsString(); // Bug 39707; + } + if (useHeaders()) { + return result.getResponseHeaders(); + } + if (useRequestHeaders()) { + return result.getRequestHeaders(); + } + if (useCode()) { + return result.getResponseCode(); // Bug 43451 + } + if (useMessage()) { + return result.getResponseMessage(); // Bug 43451 + } + if (useUnescapedBody()) { + return StringEscapeUtils.unescapeHtml4(result.getResponseDataAsString()); + } + if (useBodyAsDocument()) { + return Document.getTextFromDocument(result.getResponseData()); + } + return result.getResponseDataAsString(); // Bug 36898 + } + private List extract( String leftBoundary, String rightBoundary, int matchNumber, Stream previousResults) { boolean allItems = matchNumber <= 0; diff --git a/src/components/src/main/java/org/apache/jmeter/extractor/json/jsonpath/JSONManager.java b/src/components/src/main/java/org/apache/jmeter/extractor/json/jsonpath/JSONManager.java index 4905d79f71..03a4ee5c09 100644 --- a/src/components/src/main/java/org/apache/jmeter/extractor/json/jsonpath/JSONManager.java +++ b/src/components/src/main/java/org/apache/jmeter/extractor/json/jsonpath/JSONManager.java @@ -53,13 +53,7 @@ public class JSONManager { private final Map expressionToJsonPath = new HashMap<>(2); private JsonPath getJsonPath(String jsonPathExpression) { - JsonPath jsonPath = expressionToJsonPath.get(jsonPathExpression); - if (jsonPath == null) { - jsonPath = JsonPath.compile(jsonPathExpression); - expressionToJsonPath.put(jsonPathExpression, jsonPath); - } - - return jsonPath; + return expressionToJsonPath.computeIfAbsent(jsonPathExpression, JsonPath::compile); } public void reset() { diff --git a/src/core/build.gradle.kts b/src/core/build.gradle.kts index 35b6e3a63c..fdfec49e7f 100644 --- a/src/core/build.gradle.kts +++ b/src/core/build.gradle.kts @@ -22,7 +22,7 @@ plugins { dependencies { api(project(":src:launcher")) api(project(":src:jorphan")) - testCompile(project(":src:jorphan", "testClasses")) + testImplementation(project(":src:jorphan", "testClasses")) api("bsf:bsf") { because("protected BSFManager BSFTestElement#getManager()") diff --git a/src/core/src/main/java/org/apache/jmeter/JMeter.java b/src/core/src/main/java/org/apache/jmeter/JMeter.java index 451d882970..f0dc7e2531 100644 --- a/src/core/src/main/java/org/apache/jmeter/JMeter.java +++ b/src/core/src/main/java/org/apache/jmeter/JMeter.java @@ -385,18 +385,26 @@ public class JMeter implements JMeterPlugin { SplashScreen splash = new SplashScreen(); splash.showScreen(); splash.setProgress(10); + log.debug("Apply HiDPI on fonts"); JMeterUtils.applyHiDPIOnFonts(); + splash.setProgress(20); + log.debug("Configure PluginManager"); PluginManager.install(this, true); - - JMeterTreeModel treeModel = new JMeterTreeModel(); splash.setProgress(30); + log.debug("Setup tree"); + JMeterTreeModel treeModel = new JMeterTreeModel(); JMeterTreeListener treeLis = new JMeterTreeListener(treeModel); final ActionRouter instance = ActionRouter.getInstance(); + splash.setProgress(40); + log.debug("populate command map"); instance.populateCommandMap(); splash.setProgress(60); treeLis.setActionHandler(instance); + log.debug("init instance"); + splash.setProgress(70); GuiPackage.initInstance(treeLis, treeModel); splash.setProgress(80); + log.debug("constructing main frame"); MainFrame main = new MainFrame(treeModel, treeLis); splash.setProgress(100); ComponentUtil.centerComponentInWindow(main, 80); diff --git a/src/core/src/main/java/org/apache/jmeter/SplashScreen.java b/src/core/src/main/java/org/apache/jmeter/SplashScreen.java index 6ed8c82de9..17daf7108c 100644 --- a/src/core/src/main/java/org/apache/jmeter/SplashScreen.java +++ b/src/core/src/main/java/org/apache/jmeter/SplashScreen.java @@ -24,9 +24,9 @@ import java.net.URL; import javax.swing.Icon; import javax.swing.JComponent; +import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JProgressBar; -import javax.swing.JWindow; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; @@ -40,7 +40,7 @@ import com.github.weisj.darklaf.icons.ThemedSVGIcon; * Splash Screen * @since 3.2 */ -public class SplashScreen extends JWindow { +public class SplashScreen extends JDialog { private static final Logger log = LoggerFactory.getLogger(SplashScreen.class); private static final long serialVersionUID = 1L; @@ -53,6 +53,9 @@ public class SplashScreen extends JWindow { setLayout(new BorderLayout()); add(loadLogo(), BorderLayout.CENTER); add(progressBar, BorderLayout.SOUTH); + setModalityType(ModalityType.APPLICATION_MODAL); + setAutoRequestFocus(true); + setUndecorated(true); pack(); setLocationRelativeTo(null); } @@ -89,10 +92,7 @@ public class SplashScreen extends JWindow { * Show screen */ public void showScreen() { - SwingUtilities.invokeLater(() -> { - setVisible(true); - setAlwaysOnTop(true); - }); + SwingUtilities.invokeLater(() -> setVisible(true)); } /** diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java b/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java index 41cf95f834..2b3b5af063 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java @@ -89,13 +89,13 @@ public class HtmlReportGenerator { } } catch (TimeoutException e) { errorMessageList.add(MessageFormat.format(JMeterUtils.getResString("generate_report_ui.html_report_timeout_error"), - COMMAND_TIMEOUT, e.getMessage(), commandExecutionOutput.toString())); - LOGGER.error("Report generation took more time than configured timeout(Property {}={})", - "generate_report_ui.generation_timeout", COMMAND_TIMEOUT, commandExecutionOutput.toString(), e); + COMMAND_TIMEOUT, e.getMessage(), commandExecutionOutput)); + LOGGER.error("Report generation took more time than configured timeout (Property {}={}, command output=[{}])", + "generate_report_ui.generation_timeout", COMMAND_TIMEOUT, commandExecutionOutput, e); } catch (InterruptedException | IOException e) { errorMessageList.add(MessageFormat.format(JMeterUtils.getResString("generate_report_ui.html_report_unknown_error"), - e.getMessage(), commandExecutionOutput.toString())); - LOGGER.error("Error during HTML report generation, executing {}", commandExecutionOutput.toString(), e); + e.getMessage(), commandExecutionOutput)); + LOGGER.error("Error during HTML report generation, executing {}", commandExecutionOutput, e); if (e instanceof InterruptedException) { Thread.currentThread().interrupt(); } diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java index 7b29594b1e..007f97633b 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/action/LookAndFeelCommand.java @@ -18,7 +18,6 @@ package org.apache.jmeter.gui.action; import java.awt.event.ActionEvent; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -41,12 +40,6 @@ import org.slf4j.LoggerFactory; import com.github.weisj.darklaf.LafManager; import com.github.weisj.darklaf.theme.DarculaTheme; -import com.github.weisj.darklaf.theme.HighContrastDarkTheme; -import com.github.weisj.darklaf.theme.HighContrastLightTheme; -import com.github.weisj.darklaf.theme.IntelliJTheme; -import com.github.weisj.darklaf.theme.OneDarkTheme; -import com.github.weisj.darklaf.theme.SolarizedDarkTheme; -import com.github.weisj.darklaf.theme.SolarizedLightTheme; import com.github.weisj.darklaf.theme.Theme; /** @@ -70,9 +63,9 @@ public class LookAndFeelCommand extends AbstractAction { final String title; final String command; final String lafClassName; - final Class lafTheme; + final Theme lafTheme; - private MenuItem(String title, String command, String lafClassName, Class lafTheme) { + private MenuItem(String title, String command, String lafClassName, Theme lafTheme) { this.title = title; this.command = command; this.lafClassName = lafClassName; @@ -88,39 +81,41 @@ public class LookAndFeelCommand extends AbstractAction { } private static MenuItem of(String title, String lafClass) { - return new MenuItem(title,ActionNames.LAF_PREFIX + lafClass, lafClass, null); + return new MenuItem(title, ActionNames.LAF_PREFIX + lafClass, lafClass, null); } - private static MenuItem ofDarklafTheme(Class lafTheme) { - return new MenuItem("Darklaf - " + lafTheme.getSimpleName().replace("Theme", ""), - JMeterMenuBar.DARKLAF_LAF_CLASS + ":" + lafTheme.getName(), + private static MenuItem ofDarklafTheme(Theme theme) { + return new MenuItem("Darklaf - " + theme.getName(), + JMeterMenuBar.DARKLAF_LAF_CLASS + ":" + theme.getThemeClass().getName(), JMeterMenuBar.DARKLAF_LAF_CLASS, - lafTheme); + theme); } } static { if (System.getProperty("darklaf.decorations") == null) { System.setProperty("darklaf.decorations", "false"); + } else if (Boolean.getBoolean("darklaf.allowNativeCode")) { + // darklaf.allowNativeCode=true is required for darklaf.decorations=true to work. + System.setProperty("darklaf.decorations", "true"); } if (System.getProperty("darklaf.allowNativeCode") == null) { System.setProperty("darklaf.allowNativeCode", "false"); } - UIManager.installLookAndFeel(JMeterMenuBar.DARKLAF_LAF, JMeterMenuBar.DARKLAF_LAF_CLASS); + if (System.getProperty("darklaf.unifiedMenuBar") == null) { + System.setProperty("darklaf.unifiedMenuBar", "true"); + } + UIManager.installLookAndFeel(JMeterMenuBar.DARCULA_LAF, JMeterMenuBar.DARCULA_LAF_CLASS); List items = new ArrayList<>(); for (UIManager.LookAndFeelInfo laf : JMeterMenuBar.getAllLAFs()) { - if (!laf.getClassName().equals(JMeterMenuBar.DARKLAF_LAF_CLASS)) { + if (!laf.getClassName().equals(JMeterMenuBar.DARCULA_LAF_CLASS)) { items.add(MenuItem.of(laf.getName(), laf.getClassName())); - continue; + } else { + for (Theme theme : LafManager.getRegisteredThemes()) { + items.add(MenuItem.ofDarklafTheme(theme)); + } } - items.add(MenuItem.ofDarklafTheme(DarculaTheme.class)); - items.add(MenuItem.ofDarklafTheme(IntelliJTheme.class)); - items.add(MenuItem.ofDarklafTheme(OneDarkTheme.class)); - items.add(MenuItem.ofDarklafTheme(SolarizedDarkTheme.class)); - items.add(MenuItem.ofDarklafTheme(SolarizedLightTheme.class)); - items.add(MenuItem.ofDarklafTheme(HighContrastDarkTheme.class)); - items.add(MenuItem.ofDarklafTheme(HighContrastLightTheme.class)); } items.sort(Comparator.comparing(MenuItem::getTitle)); for (MenuItem item : items) { @@ -184,7 +179,7 @@ public class LookAndFeelCommand extends AbstractAction { String jMeterLaf = getJMeterLaf(); if (jMeterLaf.equals(JMeterMenuBar.DARCULA_LAF_CLASS)) { // Convert old Darcula to new Darklaf-Darcula LaF - return MenuItem.ofDarklafTheme(DarculaTheme.class).command; + return MenuItem.ofDarklafTheme(new DarculaTheme()).command; } return MenuItem.of("default", jMeterLaf).command; // $NON-NLS-1$ @@ -208,12 +203,7 @@ public class LookAndFeelCommand extends AbstractAction { public static boolean isDark() { String lookAndFeelID = UIManager.getLookAndFeel().getID(); if (lookAndFeelID.equals("Darklaf")) { // $NON-NLS-1$ - Theme lafTheme = LafManager.getTheme(); - if (lafTheme == null) { - return false; - } - String name = lafTheme.getName(); - return name.equals("darcula") || name.equals("solarized_dark"); // $NON-NLS-1$ + return Theme.isDark(LafManager.getTheme()); } return false; } @@ -221,24 +211,15 @@ public class LookAndFeelCommand extends AbstractAction { public static void activateLookAndFeel(String command) { MenuItem item = items.get(command); String className = item.lafClassName; - try { - if (item.lafTheme != null) { - LafManager.setTheme(item.lafTheme.getConstructor().newInstance()); - } - GuiPackage instance = GuiPackage.getInstance(); - if (instance != null) { - instance.updateUIForHiddenComponents(); - } - JFactory.refreshUI(className); - PREFS.put(USER_PREFS_KEY, item.command); - } catch ( InstantiationException - | NoSuchMethodException - | IllegalAccessException e) { - throw new IllegalArgumentException("Look and Feel unavailable:" + e.toString(), e); - } catch (InvocationTargetException e) { - Throwable c = e.getCause(); - throw new IllegalArgumentException("Look and Feel unavailable:" + c.toString(), c); + if (item.lafTheme != null) { + LafManager.setTheme(item.lafTheme); } + GuiPackage instance = GuiPackage.getInstance(); + if (instance != null) { + instance.updateUIForHiddenComponents(); + } + JFactory.refreshUI(className); + PREFS.put(USER_PREFS_KEY, item.command); } @Override diff --git a/src/core/src/main/java/org/apache/jmeter/gui/menu/StaticJMeterGUIComponent.java b/src/core/src/main/java/org/apache/jmeter/gui/menu/StaticJMeterGUIComponent.java index 6977e48858..0ddd7bb63f 100644 --- a/src/core/src/main/java/org/apache/jmeter/gui/menu/StaticJMeterGUIComponent.java +++ b/src/core/src/main/java/org/apache/jmeter/gui/menu/StaticJMeterGUIComponent.java @@ -41,6 +41,7 @@ import org.apache.jmeter.processor.gui.AbstractPreProcessorGui; import org.apache.jmeter.samplers.Sampler; import org.apache.jmeter.samplers.gui.AbstractSamplerGui; import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.threads.ThreadGroup; import org.apache.jmeter.threads.gui.AbstractThreadGroupGui; import org.apache.jmeter.timers.Timer; import org.apache.jmeter.timers.gui.AbstractTimerGui; @@ -100,7 +101,7 @@ public class StaticJMeterGUIComponent implements JMeterGUIComponent { group = MenuFactory.SAMPLERS; } else if (Timer.class.isAssignableFrom(c) || AbstractTimerGui.class.isAssignableFrom(c)) { group = MenuFactory.TIMERS; - } else if (AbstractThreadGroupGui.class.isAssignableFrom(c) || AbstractThreadGroupGui.class.isAssignableFrom(c)) { + } else if (ThreadGroup.class.isAssignableFrom(c) || AbstractThreadGroupGui.class.isAssignableFrom(c)) { group = MenuFactory.THREADS; } else { throw new IllegalArgumentException("Unknown group for class " + c); diff --git a/src/core/src/main/java/org/apache/jmeter/report/dashboard/JsonizerVisitor.java b/src/core/src/main/java/org/apache/jmeter/report/dashboard/JsonizerVisitor.java index e41f9ecb73..89b723c4f2 100644 --- a/src/core/src/main/java/org/apache/jmeter/report/dashboard/JsonizerVisitor.java +++ b/src/core/src/main/java/org/apache/jmeter/report/dashboard/JsonizerVisitor.java @@ -20,13 +20,14 @@ package org.apache.jmeter.report.dashboard; import java.util.HashMap; import java.util.Map; -import org.apache.commons.text.StringEscapeUtils; import org.apache.jmeter.report.core.JsonUtil; import org.apache.jmeter.report.processor.ListResultData; import org.apache.jmeter.report.processor.MapResultData; import org.apache.jmeter.report.processor.ResultData; import org.apache.jmeter.report.processor.ResultDataVisitor; import org.apache.jmeter.report.processor.ValueResultData; + +import com.fasterxml.jackson.core.io.JsonStringEncoder; /** * The class JsonizerVisitor provides a visitor that can get json-like string * from ResultData. @@ -96,7 +97,7 @@ public class JsonizerVisitor implements ResultDataVisitor { Object value = valueResult.getValue(); result = String.valueOf(value); if (value instanceof String) { - result = '"' + StringEscapeUtils.escapeEcmaScript(result.replace('\"', '\'')) + '"'; + result = '"' + new String(JsonStringEncoder.getInstance().quoteAsString(result)) + '"'; } } return result; diff --git a/src/core/src/main/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumer.java b/src/core/src/main/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumer.java index 2264044187..f0175730aa 100644 --- a/src/core/src/main/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumer.java +++ b/src/core/src/main/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumer.java @@ -24,6 +24,8 @@ import org.apache.jmeter.report.utils.MetricUtils; import org.apache.jmeter.samplers.SampleSaveConfiguration; import org.apache.jmeter.util.JMeterUtils; +import com.fasterxml.jackson.core.io.JsonStringEncoder; + /** *

* The class ErrorSummaryConsumer provides a consumer that calculates error @@ -89,7 +91,7 @@ public class ErrorsSummaryConsumer extends AbstractSummaryConsumer { String responseCode = sample.getResponseCode(); String responseMessage = sample.getResponseMessage(); String key = responseCode + (!StringUtils.isEmpty(responseMessage) ? - "/" + StringEscapeUtils.escapeJson(StringEscapeUtils.escapeHtml4(responseMessage)) : ""); + "/" + escapeJson(responseMessage) : ""); if (MetricUtils.isSuccessCode(responseCode) || (StringUtils.isEmpty(responseCode) && !StringUtils.isEmpty(sample.getFailureMessage()))) { @@ -97,12 +99,17 @@ public class ErrorsSummaryConsumer extends AbstractSummaryConsumer { if (ASSERTION_RESULTS_FAILURE_MESSAGE) { String msg = sample.getFailureMessage(); if (!StringUtils.isEmpty(msg)) { - key = StringEscapeUtils.escapeJson(StringEscapeUtils.escapeHtml4(msg)); + key = escapeJson(msg); } } } return key; } + + private static String escapeJson(String responseMessage) { + return new String(JsonStringEncoder.getInstance().quoteAsString(StringEscapeUtils.escapeHtml4(responseMessage))); + } + /* * (non-Javadoc) * diff --git a/src/core/src/main/java/org/apache/jmeter/util/JsseSSLManager.java b/src/core/src/main/java/org/apache/jmeter/util/JsseSSLManager.java index a80bd7c01f..f4ca272c59 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/JsseSSLManager.java +++ b/src/core/src/main/java/org/apache/jmeter/util/JsseSSLManager.java @@ -188,7 +188,7 @@ public class JsseSSLManager extends SSLManager { public void resetContext() { if (!SHARED_SESSION_CONTEXT) { log.debug("Clearing session context for current thread"); - this.threadlocal.set(null); + this.threadlocal.remove(); } } diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties index f598f6f1f1..9013ed1c06 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties @@ -1016,8 +1016,14 @@ run_threadgroup_no_timers=Start no pauses running_test=Running test runtime_controller_title=Runtime Controller runtime_seconds=Runtime (seconds) +sample_creator_counter_value=Counter start value +sample_creator_set_counter=Set counter +sample_name_formatter=Use format string sample_name_prefix=Prefix +sample_name_suffix=Suffix +sample_naming_scheme=Naming scheme sample_name_transaction=Transaction name +sample_naming_format_help=Format for the names of the samplers.
The counter, path and transaction names can be given with the placeholders #{counter}, #{path} and #{name}. salt_string=Salt to be used for hashing (optional) sample_result_save_configuration=Sample Result Save Configuration sample_scope=Apply to: @@ -1459,6 +1465,7 @@ web_testing_basic=Basic web_testing_advanced=Advanced web_testing_concurrent_download=Parallel downloads. Number: web_testing_embedded_url_pattern=URLs must match\: +web_testing_embedded_url_exclude_pattern=URLs must not match\: web_testing_retrieve_images=Retrieve All Embedded Resources web_testing_retrieve_title=Embedded Resources from HTML Files web_testing_source_ip=Source address diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties index c43d7d7daa..0dc5426d5f 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties @@ -1006,8 +1006,14 @@ running_test=Lancer test runtime_controller_title=Contrôleur Durée d'exécution runtime_seconds=Temps d'exécution (secondes) \: salt_string=Sel à utiliser pour le hash +sample_creator_counter_value=Valeur de départ du compteur +sample_creator_set_counter=Définir le compteur +sample_name_formatter=Utiliser le format sample_name_prefix=Préfixe +sample_name_suffix=Suffixe +sample_naming_scheme=Schéma de dénomination sample_name_transaction=Nom de la transaction +sample_naming_format_help=Format des noms des échantillonneurs.
Les noms de compteur, chemin et transaction peuvent être donnés avec #{counter}, #{path} et #{name}. sample_result_save_configuration=Sauvegarder la configuration de la sauvegarde des échantillons sample_scope=Appliquer sur sample_scope_all=L'échantillon et ses ressources liées @@ -1448,6 +1454,7 @@ web_testing_advanced=Avancée web_testing_basic=Basique web_testing_concurrent_download=Téléchargements en parallèle. Nombre \: web_testing_embedded_url_pattern=Les URL à inclure doivent correspondre à \: +web_testing_embedded_url_exclude_pattern=Les URL à exclure doivent correspondre à \: web_testing_retrieve_images=Récupérer les ressources incluses web_testing_retrieve_title=Ressources incluses dans les pages HTML web_testing_source_ip=Adresse source diff --git a/src/core/src/test/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumerTest.java b/src/core/src/test/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumerTest.java index a75f7408e2..ab43b8e964 100644 --- a/src/core/src/test/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumerTest.java +++ b/src/core/src/test/java/org/apache/jmeter/report/processor/ErrorsSummaryConsumerTest.java @@ -40,12 +40,12 @@ public class ErrorsSummaryConsumerTest { sample = new Sample(0, metadata, new String[] { "false", "200", "", "Test failed: text expected to contain /Some html text/" }); - assertEquals("Test failed: text expected to contain \\/<title>Some html text<\\/title>\\/", + assertEquals("Test failed: text expected to contain /<title>Some html text</title>/", ErrorsSummaryConsumer.getErrorKey(sample)); sample = new Sample(0, metadata, new String[] { "false", "200", "", "Test failed: text expected to contain /{\"glossary\": { \"title\": \"example glossary\"}}/" }); - assertEquals("Test failed: text expected to contain \\/{"glossary": { "title": "example glossary"}}\\/", + assertEquals("Test failed: text expected to contain /{"glossary": { "title": "example glossary"}}/", ErrorsSummaryConsumer.getErrorKey(sample)); sample = new Sample(0, metadata, new String[] { "true", "200", "", "" }); diff --git a/src/dist-check/build.gradle.kts b/src/dist-check/build.gradle.kts index 52bb13e1b8..10abddedb5 100644 --- a/src/dist-check/build.gradle.kts +++ b/src/dist-check/build.gradle.kts @@ -30,7 +30,7 @@ val loggingClasspath by configurations.creating dependencies { api(project(":src:dist")) - testCompile(project(":src:dist", "testClasses")) + testImplementation(project(":src:dist", "allTestClasses")) testImplementation("org.apache.commons:commons-lang3") { because("StringUtils") } diff --git a/src/dist/build.gradle.kts b/src/dist/build.gradle.kts index 0966ad3db6..c395ed6b50 100644 --- a/src/dist/build.gradle.kts +++ b/src/dist/build.gradle.kts @@ -64,12 +64,17 @@ val srcLicense by configurations.creating { isCanBeConsumed = false } +val allTestClasses by configurations.creating { + isCanBeConsumed = true + isCanBeResolved = false +} + // Note: you can inspect final classpath (list of jars in the binary distribution) via // gw dependencies --configuration runtimeClasspath dependencies { for (p in jars) { api(project(p)) - testCompile(project(p, "testClasses")) + allTestClasses(project(p, "testClasses")) } binLicense(project(":src:licenses", "binLicense")) diff --git a/src/functions/build.gradle.kts b/src/functions/build.gradle.kts index 803ac69c14..3a16b5ee38 100644 --- a/src/functions/build.gradle.kts +++ b/src/functions/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { api(project(":src:components")) - testCompile(project(":src:components", "testClasses")) + testImplementation(project(":src:core", "testClasses")) implementation("org.mozilla:rhino") implementation("commons-codec:commons-codec") diff --git a/src/launcher/src/main/java/org/apache/jmeter/NewDriver.java b/src/launcher/src/main/java/org/apache/jmeter/NewDriver.java index d7e62ed4fb..41bd8b7e6c 100644 --- a/src/launcher/src/main/java/org/apache/jmeter/NewDriver.java +++ b/src/launcher/src/main/java/org/apache/jmeter/NewDriver.java @@ -79,12 +79,15 @@ public final class NewDriver { tmpDir = null; } } else {// e.g. started from IDE with full classpath - tmpDir = System.getProperty("jmeter.home","");// Allow override $NON-NLS-1$ $NON-NLS-2$ - if (tmpDir.length() == 0) { + tmpDir = System.getProperty("jmeter.home", System.getenv("JMETER_HOME"));// Allow override $NON-NLS-1$ $NON-NLS-2$ + if (tmpDir == null || tmpDir.length() == 0) { File userDir = new File(System.getProperty("user.dir"));// $NON-NLS-1$ tmpDir = userDir.getAbsoluteFile().getParent(); } } + if (tmpDir == null) { + tmpDir = System.getenv("JMETER_HOME"); + } JMETER_INSTALLATION_DIRECTORY=tmpDir; /* diff --git a/src/protocol/build.gradle.kts b/src/protocol/build.gradle.kts index 81678287dc..aca2843704 100644 --- a/src/protocol/build.gradle.kts +++ b/src/protocol/build.gradle.kts @@ -18,7 +18,7 @@ subprojects { dependencies { api(project(":src:core")) - testCompile(project(":src:core", "testClasses")) + testImplementation(project(":src:core", "testClasses")) } } @@ -47,7 +47,7 @@ project("http") { dependencies { // for SearchTextExtension api(project(":src:components")) - testCompile(project(":src:components", "testClasses")) + testImplementation(project(":src:components", "testClasses")) api("com.thoughtworks.xstream:xstream") { because("HTTPResultConverter uses XStream in public API") @@ -84,6 +84,7 @@ project("http") { implementation("org.apache.httpcomponents:httpmime") implementation("org.apache.httpcomponents:httpcore") implementation("org.brotli:dec") + implementation("com.miglayout:miglayout-swing") testImplementation(testFixtures(project(":src:testkit-wiremock"))) testImplementation("com.github.tomakehurst:wiremock-jre8") } @@ -114,7 +115,7 @@ project("jdbc") { project("jms") { dependencies { - testCompile(project(":src:core", "testClasses")) + testImplementation(project(":src:core", "testClasses")) api("com.github.ben-manes.caffeine:caffeine") { because("MessageRenderer#getValueFromFile(..., caffeine.cache.Cache)") } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java index 3e035b552f..e97db473a7 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java @@ -46,7 +46,8 @@ import org.apache.jmeter.testelement.property.IntegerProperty; import org.apache.jmeter.testelement.property.StringProperty; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.gui.JFactory; -import org.apache.jorphan.gui.JLabeledTextField; + +import net.miginfocom.swing.MigLayout; /** * GUI for Http Request defaults @@ -55,14 +56,15 @@ import org.apache.jorphan.gui.JLabeledTextField; @TestElementMetadata(labelResource = "url_config_title") public class HttpDefaultsGui extends AbstractConfigGui { - private static final long serialVersionUID = 241L; + private static final long serialVersionUID = 242L; private UrlConfigGui urlConfigGui; private JCheckBox retrieveEmbeddedResources; private JCheckBox concurrentDwn; private JTextField concurrentPool; private JCheckBox useMD5; - private JLabeledTextField embeddedRE; // regular expression used to match against embedded resource URLs + private JTextField embeddedAllowRE; // regular expression used to match against embedded resource URLs to allow + private JTextField embeddedExcludeRE; // regular expression used to match against embedded resource URLs to discard private JTextField sourceIpAddr; // does not apply to Java implementation private JComboBox sourceIpType = new JComboBox<>(HTTPSamplerBase.getSourceTypeList()); private JTextField proxyScheme; @@ -131,12 +133,18 @@ public class HttpDefaultsGui extends AbstractConfigGui { } else { config.removeProperty(HTTPSamplerBase.MD5); } - if (!StringUtils.isEmpty(embeddedRE.getText())) { + if (!StringUtils.isEmpty(embeddedAllowRE.getText())) { config.setProperty(new StringProperty(HTTPSamplerBase.EMBEDDED_URL_RE, - embeddedRE.getText())); + embeddedAllowRE.getText())); } else { config.removeProperty(HTTPSamplerBase.EMBEDDED_URL_RE); } + if (!StringUtils.isEmpty(embeddedExcludeRE.getText())) { + config.setProperty(new StringProperty(HTTPSamplerBase.EMBEDDED_URL_EXCLUDE_RE, + embeddedExcludeRE.getText())); + } else { + config.removeProperty(HTTPSamplerBase.EMBEDDED_URL_EXCLUDE_RE); + } if(!StringUtils.isEmpty(sourceIpAddr.getText())) { config.setProperty(new StringProperty(HTTPSamplerBase.IP_SOURCE, @@ -170,7 +178,8 @@ public class HttpDefaultsGui extends AbstractConfigGui { enableConcurrentDwn(false); useMD5.setSelected(false); urlConfigGui.clear(); - embeddedRE.setText(""); // $NON-NLS-1$ + embeddedAllowRE.setText(""); // $NON-NLS-1$ + embeddedExcludeRE.setText(""); // $NON-NLS-1$ sourceIpAddr.setText(""); // $NON-NLS-1$ sourceIpType.setSelectedIndex(HTTPSamplerBase.SourceType.HOSTNAME.ordinal()); //default: IP/Hostname proxyScheme.setText(""); // $NON-NLS-1$ @@ -192,7 +201,8 @@ public class HttpDefaultsGui extends AbstractConfigGui { concurrentDwn.setSelected(samplerBase.getPropertyAsBoolean(HTTPSamplerBase.CONCURRENT_DWN)); concurrentPool.setText(samplerBase.getPropertyAsString(HTTPSamplerBase.CONCURRENT_POOL)); useMD5.setSelected(samplerBase.getPropertyAsBoolean(HTTPSamplerBase.MD5, false)); - embeddedRE.setText(samplerBase.getPropertyAsString(HTTPSamplerBase.EMBEDDED_URL_RE, ""));//$NON-NLS-1$ + embeddedAllowRE.setText(samplerBase.getPropertyAsString(HTTPSamplerBase.EMBEDDED_URL_RE, ""));//$NON-NLS-1$ + embeddedExcludeRE.setText(samplerBase.getPropertyAsString(HTTPSamplerBase.EMBEDDED_URL_EXCLUDE_RE, ""));//$NON-NLS-1$ sourceIpAddr.setText(samplerBase.getPropertyAsString(HTTPSamplerBase.IP_SOURCE)); //$NON-NLS-1$ sourceIpType.setSelectedIndex( samplerBase.getPropertyAsInt(HTTPSamplerBase.IP_SOURCE_TYPE, @@ -294,22 +304,33 @@ public class HttpDefaultsGui extends AbstractConfigGui { }); concurrentPool = new JTextField(2); // 2 columns size concurrentPool.setMinimumSize(new Dimension(10, (int) concurrentPool.getPreferredSize().getHeight())); - concurrentPool.setMaximumSize(new Dimension(30, (int) concurrentPool.getPreferredSize().getHeight())); + concurrentPool.setMaximumSize(new Dimension(60, (int) concurrentPool.getPreferredSize().getHeight())); - final JPanel embeddedRsrcPanel = new HorizontalPanel(); + final JPanel embeddedRsrcPanel = new JPanel(new MigLayout()); embeddedRsrcPanel.setBorder(BorderFactory.createTitledBorder( JMeterUtils.getResString("web_testing_retrieve_title"))); // $NON-NLS-1$ embeddedRsrcPanel.add(retrieveEmbeddedResources); embeddedRsrcPanel.add(concurrentDwn); - embeddedRsrcPanel.add(concurrentPool); + embeddedRsrcPanel.add(concurrentPool, "wrap"); - // Embedded URL match regex - embeddedRE = new JLabeledTextField(JMeterUtils.getResString("web_testing_embedded_url_pattern"),20); // $NON-NLS-1$ - embeddedRsrcPanel.add(embeddedRE); + // Embedded URL match regex to allow + embeddedAllowRE = addTextFieldWithLabel(embeddedRsrcPanel, JMeterUtils.getResString("web_testing_embedded_url_pattern")); // $NON-NLS-1$ + + // Embedded URL match regex to exclude + embeddedExcludeRE = addTextFieldWithLabel(embeddedRsrcPanel, JMeterUtils.getResString("web_testing_embedded_url_exclude_pattern")); // $NON-NLS-1$ return embeddedRsrcPanel; } + private JTextField addTextFieldWithLabel(JPanel panel, String labelText) { + JLabel label = new JLabel(labelText); // $NON-NLS-1$ + JTextField field = new JTextField(100); + label.setLabelFor(field); + panel.add(label); + panel.add(field, "span"); + return field; + } + protected JPanel createSourceAddrPanel() { final JPanel sourceAddrPanel = new HorizontalPanel(); sourceAddrPanel.setBorder(BorderFactory.createTitledBorder( @@ -343,17 +364,10 @@ public class HttpDefaultsGui extends AbstractConfigGui { } private void enableConcurrentDwn(final boolean enable) { - if (enable) { - concurrentDwn.setEnabled(true); - embeddedRE.setEnabled(true); - if (concurrentDwn.isSelected()) { - concurrentPool.setEnabled(true); - } - } else { - concurrentDwn.setEnabled(false); - concurrentPool.setEnabled(false); - embeddedRE.setEnabled(false); - } + concurrentDwn.setEnabled(enable); + embeddedAllowRE.setEnabled(enable); + embeddedExcludeRE.setEnabled(enable); + concurrentPool.setEnabled(concurrentDwn.isSelected() && enable); } /** diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java index b753e720e9..f7acf2a341 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/gui/HttpTestSampleGui.java @@ -43,7 +43,8 @@ import org.apache.jmeter.samplers.gui.AbstractSamplerGui; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.gui.JFactory; -import org.apache.jorphan.gui.JLabeledTextField; + +import net.miginfocom.swing.MigLayout; /** * HTTP Sampler GUI @@ -52,14 +53,15 @@ import org.apache.jorphan.gui.JLabeledTextField; @TestElementMetadata(labelResource = "web_testing_title") public class HttpTestSampleGui extends AbstractSamplerGui { - private static final long serialVersionUID = 241L; + private static final long serialVersionUID = 242L; private UrlConfigGui urlConfigGui; private JCheckBox retrieveEmbeddedResources; private JCheckBox concurrentDwn; private JTextField concurrentPool; private JCheckBox useMD5; - private JLabeledTextField embeddedRE; // regular expression used to match against embedded resource URLs + private JTextField embeddedAllowRE; // regular expression used to match against embedded resource URLs to allow + private JTextField embeddedExcludeRE; // regular expression used to match against embedded resource URLs to exclude private JTextField sourceIpAddr; // does not apply to Java implementation private JComboBox sourceIpType = new JComboBox<>(HTTPSamplerBase.getSourceTypeList()); private JTextField proxyScheme; @@ -96,7 +98,8 @@ public class HttpTestSampleGui extends AbstractSamplerGui { concurrentDwn.setSelected(samplerBase.isConcurrentDwn()); concurrentPool.setText(samplerBase.getConcurrentPool()); useMD5.setSelected(samplerBase.useMD5()); - embeddedRE.setText(samplerBase.getEmbeddedUrlRE()); + embeddedAllowRE.setText(samplerBase.getEmbeddedUrlRE()); + embeddedExcludeRE.setText(samplerBase.getEmbededUrlExcludeRE()); if (!isAJP) { sourceIpAddr.setText(samplerBase.getIpSource()); sourceIpType.setSelectedIndex(samplerBase.getIpSourceType()); @@ -136,7 +139,8 @@ public class HttpTestSampleGui extends AbstractSamplerGui { samplerBase.setConcurrentDwn(concurrentDwn.isSelected()); samplerBase.setConcurrentPool(concurrentPool.getText()); samplerBase.setMD5(useMD5.isSelected()); - samplerBase.setEmbeddedUrlRE(embeddedRE.getText()); + samplerBase.setEmbeddedUrlRE(embeddedAllowRE.getText()); + samplerBase.setEmbeddedUrlExcludeRE(embeddedExcludeRE.getText()); if (!isAJP) { samplerBase.setIpSource(sourceIpAddr.getText()); samplerBase.setIpSourceType(sourceIpType.getSelectedIndex()); @@ -256,22 +260,33 @@ public class HttpTestSampleGui extends AbstractSamplerGui { }); concurrentPool = new JTextField(2); // 2 column size concurrentPool.setMinimumSize(new Dimension(10, (int) concurrentPool.getPreferredSize().getHeight())); - concurrentPool.setMaximumSize(new Dimension(30, (int) concurrentPool.getPreferredSize().getHeight())); + concurrentPool.setMaximumSize(new Dimension(60, (int) concurrentPool.getPreferredSize().getHeight())); - final JPanel embeddedRsrcPanel = new HorizontalPanel(); + final JPanel embeddedRsrcPanel = new JPanel(new MigLayout()); embeddedRsrcPanel.setBorder(BorderFactory.createTitledBorder( JMeterUtils.getResString("web_testing_retrieve_title"))); // $NON-NLS-1$ embeddedRsrcPanel.add(retrieveEmbeddedResources); embeddedRsrcPanel.add(concurrentDwn); - embeddedRsrcPanel.add(concurrentPool); + embeddedRsrcPanel.add(concurrentPool, "wrap"); // Embedded URL match regex - embeddedRE = new JLabeledTextField(JMeterUtils.getResString("web_testing_embedded_url_pattern"),20); // $NON-NLS-1$ - embeddedRsrcPanel.add(embeddedRE); + embeddedAllowRE = addTextFieldWithLabel(embeddedRsrcPanel, JMeterUtils.getResString("web_testing_embedded_url_pattern")); // $NON-NLS-1$ + + // Embedded URL to not match regex + embeddedExcludeRE = addTextFieldWithLabel(embeddedRsrcPanel, JMeterUtils.getResString("web_testing_embedded_url_exclude_pattern")); // $NON-NLS-1$ return embeddedRsrcPanel; } + private JTextField addTextFieldWithLabel(JPanel panel, String labelText) { + JLabel label = new JLabel(labelText); // $NON-NLS-1$ + JTextField field = new JTextField(100); + label.setLabelFor(field); + panel.add(label); + panel.add(field, "span"); + return field; + } + /** * Create a panel containing the implementation details * @@ -334,7 +349,7 @@ public class HttpTestSampleGui extends AbstractSamplerGui { enableConcurrentDwn(false); useMD5.setSelected(false); urlConfigGui.clear(); - embeddedRE.setText(""); // $NON-NLS-1$ + embeddedAllowRE.setText(""); // $NON-NLS-1$ if (!isAJP) { sourceIpAddr.setText(""); // $NON-NLS-1$ sourceIpType.setSelectedIndex(HTTPSamplerBase.SourceType.HOSTNAME.ordinal()); //default: IP/Hostname @@ -350,17 +365,10 @@ public class HttpTestSampleGui extends AbstractSamplerGui { } private void enableConcurrentDwn(boolean enable) { - if (enable) { - concurrentDwn.setEnabled(true); - embeddedRE.setEnabled(true); - if (concurrentDwn.isSelected()) { - concurrentPool.setEnabled(true); - } - } else { - concurrentDwn.setEnabled(false); - concurrentPool.setEnabled(false); - embeddedRE.setEnabled(false); - } + concurrentDwn.setEnabled(enable); + embeddedAllowRE.setEnabled(enable); + embeddedExcludeRE.setEnabled(enable); + concurrentPool.setEnabled(concurrentDwn.isSelected() && enable); } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/AbstractSamplerCreator.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/AbstractSamplerCreator.java index 4881e5f123..4dc12dc286 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/AbstractSamplerCreator.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/AbstractSamplerCreator.java @@ -73,12 +73,6 @@ public abstract class AbstractSamplerCreator implements SamplerCreator { } - /** - * - */ - /** - * - */ public AbstractSamplerCreator() { super(); } @@ -97,6 +91,11 @@ public abstract class AbstractSamplerCreator implements SamplerCreator { incrementRequestNumberAndGet(); } + @Override + public void setCounter(int value) { + REQUEST_NUMBER.set(value); + } + /** * Increment request number * @return int number for created sampler diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/DefaultSamplerCreator.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/DefaultSamplerCreator.java index 4811415bc2..829735fa57 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/DefaultSamplerCreator.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/DefaultSamplerCreator.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; +import java.text.MessageFormat; import java.util.Map; import javax.xml.XMLConstants; @@ -60,6 +61,8 @@ public class DefaultSamplerCreator extends AbstractSamplerCreator { */ private static final int SAMPLER_NAME_NAMING_MODE_PREFIX = 0; // $NON-NLS-1$ private static final int SAMPLER_NAME_NAMING_MODE_COMPLETE = 1; // $NON-NLS-1$ + private static final int SAMPLER_NAME_NAMING_MODE_SUFFIX = 2; // $NON-NLS-1$ + private static final int SAMPLER_NAME_NAMING_MODE_FORMATTER = 3; // $NON_NLS-1$ /** * @@ -285,35 +288,50 @@ public class DefaultSamplerCreator extends AbstractSamplerCreator { */ protected void computeSamplerName(HTTPSamplerBase sampler, HttpRequestHdr request) { - String prefix = request.getPrefix(); + String prefix = StringUtils.defaultString(request.getPrefix(), ""); int httpSampleNameMode = request.getHttpSampleNameMode(); + String format = getFormat(httpSampleNameMode, request.getHttpSampleNameFormat()); if (!HTTPConstants.CONNECT.equals(request.getMethod()) && isNumberRequests()) { - if(StringUtils.isNotEmpty(prefix)) { - if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_PREFIX) { - sampler.setName(prefix + sampler.getPath()+ "-" + incrementRequestNumberAndGet()); - } else if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_COMPLETE) { - sampler.setName(prefix + "-" + incrementRequestNumberAndGet()); - } else { - log.debug("Sampler name naming mode not recognized"); - } - } else { - sampler.setName(sampler.getPath()+"-"+incrementRequestNumberAndGet()); - } + sampler.setName(MessageFormat.format(format, prefix, sampler.getPath(), incrementRequestNumberAndGet())); } else { - if(StringUtils.isNotEmpty(prefix)) { - if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_PREFIX) { - sampler.setName(prefix + sampler.getPath()); - } else if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_COMPLETE) { - sampler.setName(prefix); - } else { - log.debug("Sampler name naming mode not recognized"); - } - } else { - sampler.setName(sampler.getPath()); - } + sampler.setName(MessageFormat.format(format, prefix, sampler.getPath())); } } + private String getFormat(int httpSampleNameMode, String format) { + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_FORMATTER) { + return format.replaceAll("#\\{name([,}])", "{0$1") + .replaceAll("#\\{path([,}])", "{1$1") + .replaceAll("#\\{counter([,}])", "{2$1"); + } + if (isNumberRequests()) { + return getNumberedFormat(httpSampleNameMode, format); + } + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_PREFIX) { + return "{0}{1}"; + } + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_COMPLETE) { + return "{0}"; + } + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_SUFFIX) { + return "{0} {1}"; + } + return "{1}"; + } + + private String getNumberedFormat(int httpSampleNameMode, String format) { + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_PREFIX) { + return "{0}{1}-{2}"; + } + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_COMPLETE) { + return "{0}-{2}"; + } + if (httpSampleNameMode == SAMPLER_NAME_NAMING_MODE_SUFFIX) { + return "{0}-{2} {1}"; + } + return "{1}"; + } + /** * Set path on sampler * @param sampler {@link HTTPSamplerBase} diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java index 2a8955f4e2..e911bf1ca1 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java @@ -84,6 +84,8 @@ public class HttpRequestHdr { private int httpSampleNameMode; + private String httpSampleNameFormat; + public HttpRequestHdr() { this("", ""); } @@ -100,19 +102,21 @@ public class HttpRequestHdr { * @param httpSamplerName the http sampler name */ public HttpRequestHdr(String prefix, String httpSamplerName) { - this(prefix, httpSamplerName,0); + this(prefix, httpSamplerName, 0, "{0}{1}"); } /** * @param prefix Sampler prefix * @param httpSamplerName the http sampler name * @param httpSampleNameMode the naming mode of sampler name + * @param format format to use when mode is 3 */ - public HttpRequestHdr(String prefix, String httpSamplerName, int httpSampleNameMode) { + public HttpRequestHdr(String prefix, String httpSamplerName, int httpSampleNameMode, String format) { this.prefix = prefix; this.httpSamplerName = httpSamplerName; this.firstLine = "" ; // $NON-NLS-1$ this.httpSampleNameMode = httpSampleNameMode; + this.httpSampleNameFormat = format; } /** @@ -463,4 +467,8 @@ public class HttpRequestHdr { public int getHttpSampleNameMode() { return httpSampleNameMode; } + + public String getHttpSampleNameFormat() { + return httpSampleNameFormat; + } } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/Proxy.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/Proxy.java index 56489046d6..5faf29bca8 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/Proxy.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/Proxy.java @@ -32,6 +32,7 @@ import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -63,6 +64,9 @@ import org.slf4j.LoggerFactory; * JMeter test plan. */ public class Proxy extends Thread { + // Mime-types of resources, that are not HTML and not binary that should be skipped on form parsing in JSoup + private static final List NOT_HTML_TEXT_TYPES = Arrays.asList("application/javascript", "application/json", "text/javascript"); + private static final Logger log = LoggerFactory.getLogger(Proxy.class); private static final byte[] CRLF_BYTES = { 0x0d, 0x0a }; @@ -157,7 +161,8 @@ public class Proxy extends Thread { // Check which HTTPSampler class we should use String httpSamplerName = target.getSamplerTypeName(); - HttpRequestHdr request = new HttpRequestHdr(target.getPrefixHTTPSampleName(), httpSamplerName,target.getHTTPSampleNamingMode()); + HttpRequestHdr request = new HttpRequestHdr(target.getPrefixHTTPSampleName(), httpSamplerName, + target.getHTTPSampleNamingMode(), target.getHttpSampleNameFormat()); SampleResult result = null; HeaderManager headers = null; HTTPSamplerBase sampler = null; @@ -300,6 +305,15 @@ public class Proxy extends Thread { } } + /** + * Set the counter for all registered {@link SamplerCreatorFactory}s + * + * @param value to be initialized + */ + public static void setCounter(int value) { + SAMPLERFACTORY.setCounter(value); + } + /** * Get SSL connection from hashmap, creating it if necessary. * @@ -595,10 +609,16 @@ public class Proxy extends Thread { FormCharSetFinder finder = new FormCharSetFinder(); if (SampleResult.isBinaryType(result.getContentType())) { if (log.isDebugEnabled()) { - log.debug("Will not guess encoding of url:{} as it's binary", result.getUrlAsString()); + log.debug("Will not guess encoding of URL: {} as it's binary", result.getUrlAsString()); } return; // no point parsing anything else, e.g. GIF ... } + if (isNotHtmlType(result.getContentType())) { + if (log.isDebugEnabled()) { + log.debug("Will not guess encoding of URL: {} as it's not HTML", result.getUrlAsString()); + } + return; // None HTML types have been crashing JSoup parser, so return here early + } try { finder.addFormActionsAndCharSet(result.getResponseDataAsString(), formEncodings, pageEncoding); } @@ -609,6 +629,15 @@ public class Proxy extends Thread { } } + private boolean isNotHtmlType(String contentType) { + for (String mimeType: NOT_HTML_TEXT_TYPES) { + if (contentType.startsWith(mimeType)) { + return true; + } + } + return false; + } + private String getUrlWithoutQuery(URL url) { String fullUrl = url.toString(); String urlWithoutQuery = fullUrl; diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/ProxyControl.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/ProxyControl.java index 3d86b4d445..f634b16a2e 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/ProxyControl.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/ProxyControl.java @@ -288,6 +288,8 @@ public class ProxyControl extends GenericController implements NonTestElement { private transient javax.swing.Timer sampleWorkerTimer; + private String httpSampleNameFormat; + public ProxyControl() { setPort(DEFAULT_PORT); setExcludeList(new HashSet<>()); @@ -1658,8 +1660,7 @@ public class ProxyControl extends GenericController implements NonTestElement { private int groupingMode; private long recordedAt; - public SamplerInfo(HTTPSamplerBase sampler, TestElement[] testElements, JMeterTreeNode target, String prefix, - int groupingMode) { + public SamplerInfo(HTTPSamplerBase sampler, TestElement[] testElements, JMeterTreeNode target, String prefix, int groupingMode) { this.sampler = sampler; this.testElements = testElements; this.target = target; @@ -1668,4 +1669,12 @@ public class ProxyControl extends GenericController implements NonTestElement { this.recordedAt = System.currentTimeMillis(); } } + + public void setHttpSampleNameFormat(String text) { + this.httpSampleNameFormat = text; + } + + public String getHttpSampleNameFormat() { + return httpSampleNameFormat; + } } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreator.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreator.java index a99c1af64d..2f69d818df 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreator.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreator.java @@ -93,4 +93,12 @@ public interface SamplerCreator { * @return List */ List createChildren(HTTPSamplerBase sampler, SampleResult result); + + /** + * Set the counter for this implementation. The counter should be incremented + * before creating a new sampler by the implementation. + * + * @param value to be used + */ + default void setCounter(int value) {}; } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreatorFactory.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreatorFactory.java index 0e325b0cd3..3f3549cfb3 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreatorFactory.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/SamplerCreatorFactory.java @@ -42,6 +42,19 @@ public class SamplerCreatorFactory { init(); } + /** + * Set the counter for all available {@link SamplerCreator}s. + *

+ * The only implementation that is currently available, increments the counter before it is used! + * @param value to initialize the creators + */ + public void setCounter(int value) { + DEFAULT_SAMPLER_CREATOR.setCounter(value); + for (SamplerCreator samplerCreator: samplerCreatorMap.values()) { + samplerCreator.setCounter(value); + } + } + /** * Initialize factory from classpath */ diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/ProxyControlGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/ProxyControlGui.java index 3ec0e40a76..da28751d8b 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/ProxyControlGui.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/ProxyControlGui.java @@ -79,6 +79,7 @@ import org.apache.jmeter.gui.util.MenuFactory; import org.apache.jmeter.gui.util.PowerTableModel; import org.apache.jmeter.gui.util.VerticalPanel; import org.apache.jmeter.protocol.http.control.RecordingController; +import org.apache.jmeter.protocol.http.proxy.Proxy; import org.apache.jmeter.protocol.http.proxy.ProxyControl; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory; import org.apache.jmeter.testelement.TestElement; @@ -91,6 +92,8 @@ import org.apache.jorphan.gui.JLabeledTextField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.miginfocom.swing.MigLayout; + /** * GUI of HTTP(s) Test Script Recorder * @@ -229,7 +232,7 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp private transient RecorderDialog recorderDialog; - private Component labelDefaultEncoding; + private JTextField httpSampleNameFormat; //+ action names private static final String ACTION_STOP = "stop"; // $NON-NLS-1$ @@ -270,6 +273,9 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp // Used by itemListener private static final String PORT_FIELD_NAME = "portField"; // $NON-NLS-1$ + static final String HTTP_SAMPLER_NAME_FORMAT = "proxy_http_sampler_name_format"; + + public ProxyControlGui() { super(); log.debug("Creating ProxyControlGui"); @@ -416,12 +422,12 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp @Override public void itemStateChanged(ItemEvent e) { if (e.getSource() instanceof JComboBox) { - JComboBox combo = (JComboBox) e.getSource(); - if(HTTP_SAMPLER_NAMING_MODE.equals(combo.getName())){ + JComboBox combo = (JComboBox) e.getSource(); + if (HTTP_SAMPLER_NAMING_MODE.equals(combo.getName())) { model.setHTTPSampleNamingMode(httpSampleNamingMode.getSelectedIndex()); - } + httpSampleNameFormat.setEnabled(httpSampleNamingMode.getSelectedIndex() == 3); } - else { + } else { enableRestart(); } } @@ -710,6 +716,8 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp enableRestart(); } else if(fieldName.equals(PREFIX_HTTP_SAMPLER_NAME)) { model.setPrefixHTTPSampleName(prefixHTTPSampleName.getText()); + } else if (fieldName.equals(HTTP_SAMPLER_NAME_FORMAT)) { + model.setHttpSampleNameFormat(httpSampleNameFormat.getText()); } else if(fieldName.equals(PROXY_PAUSE_HTTP_SAMPLER)) { try { Long.parseLong(proxyPauseHTTPSample.getText()); @@ -868,22 +876,16 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp regexMatch.addActionListener(this); regexMatch.setActionCommand(ENABLE_RESTART); - VerticalPanel mainPanel = new VerticalPanel(); - mainPanel.setBorder(BorderFactory.createTitledBorder( + JPanel contentPanel = new JPanel(new MigLayout("fillx, wrap 3")); + contentPanel.setBorder(BorderFactory.createTitledBorder( JMeterUtils.getResString("proxy_test_plan_content"))); // $NON-NLS-1$ + addTargetToPanel(contentPanel); + addGroupingToPanel(contentPanel); + contentPanel.add(httpHeaders); + contentPanel.add(addAssertions); + contentPanel.add(regexMatch); - HorizontalPanel nodeCreationPanel = new HorizontalPanel(); - nodeCreationPanel.add(createGroupingPanel()); - nodeCreationPanel.add(httpHeaders); - nodeCreationPanel.add(addAssertions); - nodeCreationPanel.add(regexMatch); - - HorizontalPanel targetPanel = new HorizontalPanel(); - targetPanel.add(createTargetPanel()); - mainPanel.add(targetPanel); - mainPanel.add(nodeCreationPanel); - - return mainPanel; + return contentPanel; } private JPanel createHTTPSamplerPanel() { @@ -922,6 +924,8 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp DefaultComboBoxModel choice = new DefaultComboBoxModel<>(); choice.addElement(JMeterUtils.getResString("sample_name_prefix")); // $NON-NLS-1$ choice.addElement(JMeterUtils.getResString("sample_name_transaction")); // $NON-NLS-1$ + choice.addElement(JMeterUtils.getResString("sample_name_suffix")); // $NON-NLS-1$ + choice.addElement(JMeterUtils.getResString("sample_name_formatter")); // $NON-NLS-1$ httpSampleNamingMode = new JComboBox<>(choice); httpSampleNamingMode.setName(HTTP_SAMPLER_NAMING_MODE); httpSampleNamingMode.addItemListener(this); @@ -932,6 +936,10 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp prefixHTTPSampleName.addKeyListener(this); prefixHTTPSampleName.setName(PREFIX_HTTP_SAMPLER_NAME); + httpSampleNameFormat = new JTextField(20); + httpSampleNameFormat.addKeyListener(this); + httpSampleNameFormat.setName(HTTP_SAMPLER_NAME_FORMAT); + proxyPauseHTTPSample = new JTextField(10); proxyPauseHTTPSample.addKeyListener(this); proxyPauseHTTPSample.setName(PROXY_PAUSE_HTTP_SAMPLER); @@ -942,71 +950,50 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp JLabel labelDefaultEncoding = new JLabel(JMeterUtils.getResString("proxy_default_encoding")); // $NON-NLS-1$ labelDefaultEncoding.setLabelFor(defaultEncoding); - GridBagLayout gridBagLayout = new GridBagLayout(); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.FIRST_LINE_START; - gbc.fill = GridBagConstraints.NONE; - gbc.gridheight = 1; - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy = 0; - gbc.weightx = 1; - gbc.weighty = 1; - JPanel panel = new JPanel(gridBagLayout); + JPanel panel = new JPanel(new MigLayout("fillx, wrap 3")); panel.setBorder(BorderFactory.createTitledBorder( JMeterUtils.getResString("proxy_sampler_settings"))); // $NON-NLS-1$ - panel.add(httpSampleNamingMode, gbc.clone()); - gbc.gridx++; - gbc.weightx = 3; - gbc.fill=GridBagConstraints.HORIZONTAL; - panel.add(prefixHTTPSampleName, gbc.clone()); - gbc.gridx = 0; - gbc.gridy++; - panel.add(labelProxyPause, gbc.clone()); - gbc.gridx++; - gbc.weightx = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - panel.add(proxyPauseHTTPSample, gbc.clone()); - gbc.weightx = 1; + JLabel labelSampleTransactionName = new JLabel(JMeterUtils.getResString("sample_name_transaction")); + labelSampleTransactionName.setLabelFor(prefixHTTPSampleName); + panel.add(labelSampleTransactionName); + panel.add(prefixHTTPSampleName, "growx, span"); - gbc.gridx = 0; - gbc.gridy++; - panel.add(labelDefaultEncoding, gbc.clone()); - gbc.gridx++; - gbc.weightx = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - panel.add(defaultEncoding, gbc.clone()); - gbc.weightx = 1; + JLabel labelNamingScheme = new JLabel(JMeterUtils.getResString("sample_naming_scheme")); + labelNamingScheme.setLabelFor(httpSampleNamingMode); + panel.add(labelNamingScheme, "split 2"); + panel.add(httpSampleNamingMode); + panel.add(httpSampleNameFormat, "growx, span"); + httpSampleNameFormat.setToolTipText(JMeterUtils.getResString("sample_naming_format_help")); - gbc.gridx = 0; - gbc.gridy++; - gbc.fill=GridBagConstraints.VERTICAL; - panel.add(samplerDownloadImages, gbc.clone()); - gbc.gridx = 0; - gbc.gridy++; - gbc.fill=GridBagConstraints.VERTICAL; - panel.add(samplerRedirectAutomatically, gbc.clone()); - gbc.gridx++; - gbc.fill=GridBagConstraints.HORIZONTAL; - panel.add(samplerFollowRedirects, gbc.clone()); - gbc.gridx = 0; - gbc.gridy++; - gbc.fill=GridBagConstraints.VERTICAL; - panel.add(useKeepAlive, gbc.clone()); - gbc.gridx = 0; - gbc.gridy++; - gbc.fill=GridBagConstraints.VERTICAL; - panel.add(labelSamplerType, gbc.clone()); - gbc.gridx++; - gbc.fill=GridBagConstraints.HORIZONTAL; - panel.add(samplerTypeName, gbc.clone()); + JLabel labelSetCounter = new JLabel(JMeterUtils.getResString("sample_creator_counter_value")); + JTextField counterValue = new JTextField(10); + labelSetCounter.setLabelFor(counterValue); + JButton buttonSetCounter = new JButton(JMeterUtils.getResString("sample_creator_set_counter")); + buttonSetCounter.addActionListener(e -> Proxy.setCounter(Integer.valueOf(counterValue.getText()))); + panel.add(labelSetCounter); + panel.add(counterValue); + panel.add(buttonSetCounter); + + panel.add(labelProxyPause); + panel.add(proxyPauseHTTPSample, "growx, span"); + + panel.add(labelDefaultEncoding); + panel.add(defaultEncoding, "growx, span"); + + panel.add(samplerDownloadImages); + panel.add(samplerRedirectAutomatically); + panel.add(samplerFollowRedirects); + panel.add(useKeepAlive, "wrap"); + + panel.add(labelSamplerType); + panel.add(samplerTypeName, "growx, span"); return panel; } - private JPanel createTargetPanel() { + private void addTargetToPanel(JPanel destPanel) { targetNodesModel = new DefaultComboBoxModel<>(); targetNodes = new JComboBox<>(targetNodesModel); - targetNodes.setPrototypeDisplayValue(""); // $NON-NLS-1$ // Bug 56303 fixed the width of combo list + // Bug 56303 fixed the width of combo list JPopupMenu popup = (JPopupMenu) targetNodes.getUI().getAccessibleChild(targetNodes, 0); // get popup element JScrollPane scrollPane = findScrollPane(popup); if(scrollPane != null) { @@ -1019,11 +1006,8 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp JLabel label = new JLabel(JMeterUtils.getResString("proxy_target")); // $NON-NLS-1$ label.setLabelFor(targetNodes); - HorizontalPanel panel = new HorizontalPanel(); - panel.add(label); - panel.add(targetNodes); - - return panel; + destPanel.add(label); + destPanel.add(targetNodes, "growx, span"); } private JScrollPane findScrollPane(JPopupMenu popup) { @@ -1036,7 +1020,7 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp return null; } - private JPanel createGroupingPanel() { + private void addGroupingToPanel(JPanel destPanel) { DefaultComboBoxModel m = new DefaultComboBoxModel<>(); // Note: position of these elements in the menu *must* match the // corresponding ProxyControl.GROUPING_* values. @@ -1046,18 +1030,14 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp m.addElement(JMeterUtils.getResString("grouping_store_first_only")); // $NON-NLS-1$ m.addElement(JMeterUtils.getResString("grouping_in_transaction_controllers")); // $NON-NLS-1$ groupingMode = new JComboBox<>(m); - groupingMode.setPreferredSize(new Dimension(150, 20)); groupingMode.setSelectedIndex(0); groupingMode.addItemListener(this); JLabel label2 = new JLabel(JMeterUtils.getResString("grouping_mode")); // $NON-NLS-1$ label2.setLabelFor(groupingMode); - HorizontalPanel panel = new HorizontalPanel(); - panel.add(label2); - panel.add(groupingMode); - - return panel; + destPanel.add(label2); + destPanel.add(groupingMode, "growx, span"); } private JPanel createContentTypePanel() { @@ -1266,4 +1246,9 @@ public class ProxyControlGui extends LogicControllerGui implements JMeterGUIComp prefixHTTPSampleName.setText(text); model.setPrefixHTTPSampleName(text); } + + void setSampleNameFormat(String text) { + httpSampleNameFormat.setText(text); + model.setHttpSampleNameFormat(text); + } } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/RecorderDialog.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/RecorderDialog.java index 61d1c41413..5742847738 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/RecorderDialog.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/proxy/gui/RecorderDialog.java @@ -18,8 +18,6 @@ package org.apache.jmeter.protocol.http.proxy.gui; import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -47,8 +45,11 @@ import javax.swing.JTextField; import org.apache.jmeter.gui.action.KeyStrokes; import org.apache.jmeter.gui.util.JMeterToolBar; +import org.apache.jmeter.protocol.http.proxy.Proxy; import org.apache.jmeter.util.JMeterUtils; +import net.miginfocom.swing.MigLayout; + /** * Dialog for Recorder * @since 5.0 @@ -66,6 +67,8 @@ public class RecorderDialog extends JDialog implements ItemListener, KeyListener */ private JTextField prefixHTTPSampleName; + private JTextField sampleNameFormat; + private JTextField proxyPauseHTTPSample; /** @@ -120,6 +123,8 @@ public class RecorderDialog extends JDialog implements ItemListener, KeyListener DefaultComboBoxModel choice = new DefaultComboBoxModel<>(); choice.addElement(JMeterUtils.getResString("sample_name_prefix")); // $NON-NLS-1$ choice.addElement(JMeterUtils.getResString("sample_name_transaction")); // $NON-NLS-1$ + choice.addElement(JMeterUtils.getResString("sample_name_suffix")); // $NON-NLS-1$ + choice.addElement(JMeterUtils.getResString("sample_name_formatter")); // $NON-NLS-1$ httpSampleNamingMode = new JComboBox<>(choice); httpSampleNamingMode.setName(ProxyControlGui.HTTP_SAMPLER_NAMING_MODE); httpSampleNamingMode.addItemListener(this); @@ -136,31 +141,35 @@ public class RecorderDialog extends JDialog implements ItemListener, KeyListener JLabel labelProxyPause = new JLabel(JMeterUtils.getResString("proxy_pause_http_sampler")); // $NON-NLS-1$ labelProxyPause.setLabelFor(proxyPauseHTTPSample); - GridBagLayout gridBagLayout = new GridBagLayout(); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.FIRST_LINE_START; - gbc.fill = GridBagConstraints.NONE; - gbc.gridheight = 1; - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy = 0; - gbc.weightx = 1; - gbc.weighty = 1; - JPanel panel = new JPanel(gridBagLayout); + JPanel panel = new JPanel(new MigLayout("fillx, wrap 3")); panel.setBorder(BorderFactory.createTitledBorder( JMeterUtils.getResString("proxy_sampler_settings"))); // $NON-NLS-1$ - panel.add(httpSampleNamingMode, gbc.clone()); - gbc.gridx++; - gbc.weightx = 3; - gbc.fill=GridBagConstraints.HORIZONTAL; - panel.add(prefixHTTPSampleName, gbc.clone()); - gbc.gridx = 0; - gbc.gridy++; - panel.add(labelProxyPause, gbc.clone()); - gbc.gridx++; - gbc.weightx = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - panel.add(proxyPauseHTTPSample, gbc.clone()); + JLabel labelTransactionName = new JLabel(JMeterUtils.getResString("sample_name_transaction")); + labelTransactionName.setLabelFor(prefixHTTPSampleName); + panel.add(labelTransactionName); + panel.add(prefixHTTPSampleName, "span"); + + JLabel labelNamingScheme = new JLabel(JMeterUtils.getResString("sample_naming_scheme")); + labelNamingScheme.setLabelFor(httpSampleNamingMode); + panel.add(labelNamingScheme, "split 2"); + panel.add(httpSampleNamingMode); + sampleNameFormat = new JTextField(20); + sampleNameFormat.addKeyListener(this); + sampleNameFormat.setName(ProxyControlGui.HTTP_SAMPLER_NAME_FORMAT); + sampleNameFormat.setEnabled(httpSampleNamingMode.getSelectedIndex() == 3); + sampleNameFormat.setToolTipText(JMeterUtils.getResString("sample_naming_format_help")); + panel.add(sampleNameFormat, "span"); + + JLabel labelSetCounter = new JLabel(JMeterUtils.getResString("sample_creator_counter_value")); + JTextField counterValue = new JTextField(10); + labelSetCounter.setLabelFor(counterValue); + JButton buttonSetCounter = new JButton(JMeterUtils.getResString("sample_creator_set_counter")); + buttonSetCounter.addActionListener(e -> Proxy.setCounter(Integer.valueOf(counterValue.getText()))); + panel.add(labelSetCounter); + panel.add(counterValue); + panel.add(buttonSetCounter); + panel.add(labelProxyPause); + panel.add(proxyPauseHTTPSample, "span"); this.getContentPane().add(panel, BorderLayout.CENTER); @@ -194,9 +203,10 @@ public class RecorderDialog extends JDialog implements ItemListener, KeyListener @Override public void itemStateChanged(ItemEvent e) { if (e.getSource() instanceof JComboBox) { - JComboBox combo = (JComboBox) e.getSource(); + JComboBox combo = (JComboBox) e.getSource(); if(ProxyControlGui.HTTP_SAMPLER_NAMING_MODE.equals(combo.getName())){ recorderGui.setHTTPSampleNamingMode(httpSampleNamingMode.getSelectedIndex()); + sampleNameFormat.setEnabled(httpSampleNamingMode.getSelectedIndex() == 3); } } else { @@ -220,9 +230,11 @@ public class RecorderDialog extends JDialog implements ItemListener, KeyListener @Override public void keyReleased(KeyEvent e) { String fieldName = e.getComponent().getName(); - if(fieldName.equals(ProxyControlGui.PREFIX_HTTP_SAMPLER_NAME)) { + if (fieldName.equals(ProxyControlGui.PREFIX_HTTP_SAMPLER_NAME)) { recorderGui.setPrefixHTTPSampleName(prefixHTTPSampleName.getText()); - } else if(fieldName.equals(ProxyControlGui.PROXY_PAUSE_HTTP_SAMPLER)) { + } else if (fieldName.equals(ProxyControlGui.HTTP_SAMPLER_NAME_FORMAT)) { + recorderGui.setSampleNameFormat(sampleNameFormat.getText()); + } else if (fieldName.equals(ProxyControlGui.PROXY_PAUSE_HTTP_SAMPLER)) { try { Long.parseLong(proxyPauseHTTPSample.getText()); } catch (NumberFormatException nfe) { diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java index b73bf59f05..920be9e31f 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java @@ -100,7 +100,7 @@ public abstract class HTTPSamplerBase extends AbstractSampler implements TestStateListener, TestIterationListener, ThreadListener, HTTPConstantsInterface, Replaceable { - private static final long serialVersionUID = 242L; + private static final long serialVersionUID = 243L; private static final Logger log = LoggerFactory.getLogger(HTTPSamplerBase.class); @@ -266,6 +266,9 @@ public abstract class HTTPSamplerBase extends AbstractSampler // Embedded URLs must match this RE (if provided) public static final String EMBEDDED_URL_RE = "HTTPSampler.embedded_url_re"; // $NON-NLS-1$ + // Embedded URLs must not match this RE (if provided) + public static final String EMBEDDED_URL_EXCLUDE_RE = "HTTPSampler.embedded_url_exclude_re"; // $NON-NLS-1$ + public static final String MONITOR = "HTTPSampler.monitor"; // $NON-NLS-1$ // Store MD5 hash instead of storing response @@ -1026,6 +1029,17 @@ public abstract class HTTPSamplerBase extends AbstractSampler setProperty(new StringProperty(EMBEDDED_URL_RE, regex)); } + /** + * @return the regular (as String) expression that embedded URLs must not match + */ + public String getEmbededUrlExcludeRE() { + return getPropertyAsString(EMBEDDED_URL_EXCLUDE_RE, ""); + } + + public void setEmbeddedUrlExcludeRE(String regex) { + setProperty(new StringProperty(EMBEDDED_URL_EXCLUDE_RE, regex)); + } + /** * Populates the provided HTTPSampleResult with details from the Exception. * Does not create a new instance, so should not be used directly to add a subsample. @@ -1349,17 +1363,29 @@ public abstract class HTTPSamplerBase extends AbstractSampler res = lContainer; // Get the URL matcher - String re = getEmbeddedUrlRE(); + String allowRegex = getEmbeddedUrlRE(); Perl5Matcher localMatcher = null; - Pattern pattern = null; - if (re.length() > 0) { + Pattern allowPattern = null; + if (allowRegex.length() > 0) { try { - pattern = JMeterUtils.getPattern(re); + allowPattern = JMeterUtils.getPattern(allowRegex); localMatcher = JMeterUtils.getMatcher();// don't fetch unless pattern compiles } catch (MalformedCachePatternException e) { // NOSONAR log.warn("Ignoring embedded URL match string: {}", e.getMessage()); } } + Pattern excludePattern = null; + String excludeRegex = getEmbededUrlExcludeRE(); + if (excludeRegex.length() > 0) { + try { + excludePattern = JMeterUtils.getPattern(excludeRegex); + if (localMatcher == null) { + localMatcher = JMeterUtils.getMatcher();// don't fetch unless pattern compiles + } + } catch (MalformedCachePatternException e) { // NOSONAR + log.warn("Ignoring embedded URL exclude string: {}", e.getMessage()); + } + } // For concurrent get resources final List> list = new ArrayList<>(); @@ -1396,8 +1422,12 @@ public abstract class HTTPSamplerBase extends AbstractSampler setParentSampleSuccess(res, false); continue; } + log.debug("allowPattern: {}, excludePattern: {}, localMatcher: {}, url: {}", allowPattern, excludePattern, localMatcher, url); // I don't think localMatcher can be null here, but check just in case - if (pattern != null && localMatcher != null && !localMatcher.matches(url.toString(), pattern)) { + if (allowPattern != null && localMatcher != null && !localMatcher.matches(url.toString(), allowPattern)) { + continue; // we have a pattern and the URL does not match, so skip it + } + if (excludePattern != null && localMatcher != null && localMatcher.matches(url.toString(), excludePattern)) { continue; // we have a pattern and the URL does not match, so skip it } try { diff --git a/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java b/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java index 89600bd7e3..f9d4d48456 100644 --- a/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java +++ b/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.TimeZone; import org.apache.jmeter.junit.JMeterTestCase; +import org.apache.jmeter.protocol.http.control.CacheManager.CacheEntry; import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult; import org.apache.jmeter.protocol.http.util.HTTPConstants; import org.junit.jupiter.api.Test; @@ -281,8 +282,9 @@ public abstract class TestCacheManagerBase extends JMeterTestCase { assertNotNull(getThreadCacheEntry(LOCAL_HOST), "Should find entry"); assertTrue(this.cacheManager.inCache(url), "Should find valid entry"); sleepTill(start + age / 10 + 10); - assertNotNull(getThreadCacheEntry(LOCAL_HOST), "Should find entry"); - assertFalse(this.cacheManager.inCache(url), "Should not find valid entry"); + CacheEntry cachedEntry = getThreadCacheEntry(LOCAL_HOST); + assertNotNull(cachedEntry, "Should find entry"); + assertFalse(this.cacheManager.inCache(url), "Should not find valid entry. Found " + cachedEntry); } @Test diff --git a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java index 5fb5255228..96f37ebce7 100644 --- a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java +++ b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java @@ -631,7 +631,7 @@ public abstract class AbstractJDBCTestElement extends AbstractTestElement implem s.close(); } } catch (SQLException e) { - log.warn("Error closing Statement {}", s.toString(), e); + log.warn("Error closing Statement {}", s, e); } } diff --git a/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SynchronousTransportListener.java b/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SynchronousTransportListener.java index b79ae0e5c9..24bbb2a9f2 100644 --- a/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SynchronousTransportListener.java +++ b/src/protocol/mail/src/main/java/org/apache/jmeter/protocol/smtp/sampler/protocol/SynchronousTransportListener.java @@ -74,7 +74,7 @@ public class SynchronousTransportListener extends TransportAdapter { * Synchronized-method *

* Waits until {@link #finish()} was called and thus the end of the mail - * sending was signalled. + * sending was signaled. * * @throws InterruptedException * when interrupted while waiting with the lock @@ -93,7 +93,7 @@ public class SynchronousTransportListener extends TransportAdapter { public void finish() { finished = true; synchronized(LOCK) { - LOCK.notify(); + LOCK.notifyAll(); } } diff --git a/xdocs/changes.xml b/xdocs/changes.xml index 587af16dae..9e6ca2df67 100644 --- a/xdocs/changes.xml +++ b/xdocs/changes.xml @@ -63,9 +63,13 @@ Summary Core improvements Test Plan Scripting / Debugging enhancements -UX improvements Functions --> +UX improvements + +

The splash screen is now application-modal rather than system-modal, so it does not block other +applications when JMeter is starting up.

+ Incompatible changes @@ -77,6 +81,8 @@ Summary

HTTP Samplers and Test Script Recorder

    +
  • 63527Implement a new setting to allow the exclusion of embedded URLs
  • +
  • 64696571595Freestyle format for names in (Default)SamplerCreater. Based on a patch by Vincent Daburon (vdaburon at gmail.com)

Other samplers

@@ -113,6 +119,7 @@ Summary
  • 64446Better parse curl commands with backslash at line endings and support PUT method with data arguments
  • 599Ensure all buttons added to the toolbar behave/look consistently. Contributed by Jannis Weis
  • 64581Allow SampleResult#setIgnore to influence behaviour on Sampler Error
  • +
  • 64680Fall back to JMETER_HOME on startup to detect JMeter's installation directory
  • Non-functional changes @@ -121,11 +128,27 @@ Summary
  • 64454More precise error message, when no datasource value can be found in JDBC sampler
  • 64440Log exeptions reported via JMeterUtils#reportToUser even when in GUI mode
  • 591Remove deprecated sudo flag from travis file. Deng Liming (liming.d.pro at gmail.com)
  • -
  • Updated Darklaf to 2.4.2 (from 2.1.1)
  • +
  • Updated Darklaf to 2.4.5 (from 2.1.1)
  • Updated Groovy to 3.0.5 (from 3.0.3)
  • 596Use neutral words in documentation
  • 63809557Updated commons-collections to 4.4 (from 3.2.2) while keeping the jars for the old commons-collections 3.x for compatibility
  • 598Add another option for creating diffs to the building page. Contributed by jmetertea (github.com/jmetertea)
  • +
  • 609Make use of newer API for darklaf installation. Jannis Weis
  • +
  • 612Correct typos in README.me. Based on patches by Pooja Chandak (poojachandak002 at gmail.com)
  • +
  • 613Add documentation for Darklaf properties. Jannis Weis
  • +
  • Update SpotBugs to 4.1.2 (from 4.1.1), upgrade spotbugs-gradle-plugin to 4.5.0 (from 2.0.0)
  • +
  • Update org.sonarqube Gradle plugin to 3.0 (from 2.7.1)
  • +
  • Update Apache ActiveMQ to 5.16.0 (from 5.15.11)
  • +
  • Update Bouncycastle to 1.66 (from 1.64)
  • +
  • Update Apache commons-io to 2.7 (from 2.6)
  • +
  • Update Apache commons-lang3 to 3.11 (from 3.10)
  • +
  • Update Apache commons-net to 3.7 (from 3.6)
  • +
  • Update Apache commons-pool2 to 2.8.1 (from 2.8.0)
  • +
  • Update Apache commons-text to 1.9 (from 1.8)
  • +
  • Update equalsverifier to 3.4.2 (from 3.1.13)
  • +
  • Update junit5 to 5.6.2 (from 5.6.0)
  • +
  • Update Apache log4j2 to 2.13.3 (from 2.13.1)
  • +
  • Update rsyntaxtextarea to 3.1.1 (from 3.1.0)
  • @@ -135,6 +158,7 @@ Summary

    HTTP Samplers and Test Script Recorder

    • 64479Regression: HTTP(s) Script Recorder prevents proper shutdown in non-GUI mode
    • +
    • 64653Exclude Javascript and JSON from parsing for charsets from forms by proxy

    Other Samplers

    @@ -172,6 +196,7 @@ Summary

    Documentation

      +
    • 571Correct documented name of generated CA when using proxy script recorder. Part of a bigger PR. Vincent Daburon (vdaburon at gmail.com)

    General

    @@ -204,6 +229,8 @@ Summary
  • Daniel van den Ouden
  • Ubik Load Pack
  • Till Neunast (https://github.com/tilln)
  • +
  • Pooja Chandak (poojachandak002 at gmail.com)
  • +
  • Vincent Daburon (vdaburon at gmail.com)
  • We also thank bug reporters who helped us improve JMeter.

      diff --git a/xdocs/images/screenshots/http-config/http-request-defaults-advanced-tab.png b/xdocs/images/screenshots/http-config/http-request-defaults-advanced-tab.png index 0e85647700..6c77fa2c80 100644 Binary files a/xdocs/images/screenshots/http-config/http-request-defaults-advanced-tab.png and b/xdocs/images/screenshots/http-config/http-request-defaults-advanced-tab.png differ diff --git a/xdocs/images/screenshots/http-request-advanced-tab.png b/xdocs/images/screenshots/http-request-advanced-tab.png index e21d134b1e..b620384455 100644 Binary files a/xdocs/images/screenshots/http-request-advanced-tab.png and b/xdocs/images/screenshots/http-request-advanced-tab.png differ diff --git a/xdocs/images/screenshots/proxy_control.png b/xdocs/images/screenshots/proxy_control.png index a22128acea..72fd794949 100644 Binary files a/xdocs/images/screenshots/proxy_control.png and b/xdocs/images/screenshots/proxy_control.png differ diff --git a/xdocs/nightly.xml b/xdocs/nightly.xml index 79f9889600..925198d3a8 100644 --- a/xdocs/nightly.xml +++ b/xdocs/nightly.xml @@ -36,7 +36,7 @@ These builds should not be used in production.

      Last Build status on Jenkins

      - +

      Last Quality Report on Sonar

      Apache JMeter quality report @@ -45,7 +45,7 @@

      JMeter CI builds are currently run by Jenkins and Buildbot

      These are located at:

      @@ -55,10 +55,10 @@ JMeter is distributed as a set of zip (or tar-gz) archive files as are the released versions. You can find the bundles:
        -
      • apache-jmeter-{SVN revision}.zip - Bundle in ZIP format
      • -
      • apache-jmeter-{SVN revision}.tgz - Bundle in TAR GZIP format
      • -
      • apache-jmeter-{SVN revision}_src.zip - Sources in ZIP format
      • -
      • apache-jmeter-{SVN revision}_src.tgz - Sources in TAR GZIP format
      • +
      • {git revision}/apache-jmeter-{version}.zip - Bundle in ZIP format
      • +
      • {git revision}/apache-jmeter-{version}.tgz - Bundle in TAR GZIP format
      • +
      • {git revision}/apache-jmeter-{version}_src.zip - Sources in ZIP format
      • +
      • {git revision}/apache-jmeter-{version}_src.tgz - Sources in TAR GZIP format
      For each file you will also find a hash (MD5, SHA) that allows you to test its integrity.

      diff --git a/xdocs/usermanual/component_reference.xml b/xdocs/usermanual/component_reference.xml index 7a60c75c6e..cdc983a70c 100644 --- a/xdocs/usermanual/component_reference.xml +++ b/xdocs/usermanual/component_reference.xml @@ -331,10 +331,15 @@ and send HTTP/HTTPS requests for all images, Java applets, JavaScript files, CSS Instead, the 32 character MD5 hash of the data is calculated and stored instead. This is intended for testing large amounts of data. - + If present, this must be a regular expression that is used to match against any embedded URLs found. - So if you only want to download embedded resources from http://example.com/, use the expression: - http://example\.com/.* + So if you only want to download embedded resources from http://example.invalid/, use the expression: + http://example\.invalid/.* + + + If present, this must be a regular expression that is used to filter out any embedded URLs found. + So if you don't want to download PNG or SVG files from any source, use the expression: + .*\.(?i:svg|png) Use a pool of concurrent connections to get embedded resources. Pool size for concurrent connections used to get embedded resources. @@ -3898,10 +3903,15 @@ and send HTTP/HTTPS requests for all images, Java applets, JavaScript files, CSS Use a pool of concurrent connections to get embedded resources. Pool size for concurrent connections used to get embedded resources. - + If present, this must be a regular expression that is used to match against any embedded URLs found. - So if you only want to download embedded resources from http://example.com/, use the expression: - http://example\.com/.* + So if you only want to download embedded resources from http://example.invalid/, use the expression: + http://example\.invalid/.* + + + If present, this must be a regular expression that is used to filter out any embedded URLs found. + So if you don't want to download PNG or SVG files from any source, use the expression: + .*\.(?i:svg|png) @@ -6531,7 +6541,7 @@ Behaviour can be modified with some properties by setting in user.properti

      - +

      The HTTP(S) Test Script Recorder allows JMeter to intercept and record your actions while you browse your web application with your normal browser. JMeter will create test sample objects and store them directly into your test plan as you go (so you can view samples interactively while you make them).
      @@ -6607,9 +6617,9 @@ As a consequence:

    • The browser should display a dialogue asking if you want to accept the certificate or not. For example: 1) The server's name "www.example.com" does not match the certificate's name - "JMeter Proxy (DO NOT TRUST)". Somebody may be trying to eavesdrop on you. -2) The certificate for "JMeter Proxy (DO NOT TRUST)" is signed by the unknown Certificate Authority - "JMeter Proxy (DO NOT TRUST)". It is not possible to verify that this is a valid certificate. + "_ JMeter Root CA for recording (INSTALL ONLY IF IT S YOURS)". Somebody may be trying to eavesdrop on you. +2) The certificate for "_ JMeter Root CA for recording (INSTALL ONLY IF IT S YOURS)" is signed by the unknown Certificate Authority + "_ JMeter Root CA for recording (INSTALL ONLY IF IT S YOURS)". It is not possible to verify that this is a valid certificate. You will need to accept the certificate in order to allow the JMeter Proxy to intercept the SSL traffic in order to record it. @@ -6735,6 +6745,9 @@ Both Chrome and Internet Explorer use the same trust store for certificates. Add a blank assertion to each sampler? Use Regex Matching when replacing variables? If checked replacement will use word boundaries, i.e. it will only replace word matching values of variable, not part of a word. A word boundary follows Perl5 definition and is equivalent to \b. More information below in the paragraph about "User Defined Variable replacement". Add a prefix to sampler name during recording (Prefix mode). Or replace sampler name by user chosen name (Transaction name) + Select the naming scheme for sampler names during recording. Default is Transaction name + If Use format string is selected as naming scheme, a freestyle format can be given. Placeholders for the transaction name, path and counter can be given by #{name}, #{path} and #{counter}. A simple format could be "#{name}-#{counter}", which would be equivalent to the numbered default naming scheme. For more complex formatting Java formatting for MessageFormat can be used, as in "#{counter,number,000}: #{name}-#{path}", which would print the counter filled with up to three zeroes. Default is an empty string. + Can be used to reset the counter to a given value. Note, that the next sample will first increment and then use the value. If the first sampler should start with 1, reset the counter to 0. Inactivity time between two requests needed to consider them in two separate groups. Which type of sampler to generate (the HTTPClient default or Java) Set Redirect Automatically in the generated samplers? diff --git a/xdocs/usermanual/properties_reference.xml b/xdocs/usermanual/properties_reference.xml index 2674bbb682..18cdbad8d8 100644 --- a/xdocs/usermanual/properties_reference.xml +++ b/xdocs/usermanual/properties_reference.xml @@ -196,6 +196,17 @@ Defaults to: true Defaults to: 500 +
      + + + Enables custom window chrome when using a Darklaf Look And Feel. + Defaults to: false + + Enables the unified menubar on Windows when using a Darklaf Look and Feel.
      + This property only has an effect if darklaf.native is true. + Defaults to: true
      +
      +