You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by vl...@apache.org on 2023/05/11 13:20:24 UTC
[jmeter] branch master updated: feat: use ServiceLoader to find implementations instead of searching classes in jars (#5885)
This is an automated email from the ASF dual-hosted git repository.
vladimirsitnikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git
The following commit(s) were added to refs/heads/master by this push:
new eedabdd6c0 feat: use ServiceLoader to find implementations instead of searching classes in jars (#5885)
eedabdd6c0 is described below
commit eedabdd6c0c8030ee8063a937652c1ce59a4a042
Author: Vladimir Sitnikov <si...@gmail.com>
AuthorDate: Thu May 11 16:20:17 2023 +0300
feat: use ServiceLoader to find implementations instead of searching classes in jars (#5885)
ServiceLoader is Java standard approach for locating implementaitons,
and it allows pluggability without relying on a filesystem layout.
Fixes https://github.com/apache/jmeter/issues/5883
---
build-logic/jvm/build.gradle.kts | 3 +-
...adle.kts => build-logic.autoservice.gradle.kts} | 37 +++---
.../kotlin/build-logic.java-library.gradle.kts | 1 +
.../src/main/kotlin/build-logic.java.gradle.kts | 1 +
.../main/kotlin/build-logic.jvm-library.gradle.kts | 3 +-
.../src/main/kotlin/build-logic.kotlin.gradle.kts | 1 +
build-logic/root-build/build.gradle.kts | 1 +
.../main/kotlin/build-logic.root-build.gradle.kts | 1 +
build.gradle.kts | 3 +-
gradle.properties | 2 +
settings.gradle.kts | 1 +
src/bom-thirdparty/build.gradle.kts | 2 +
.../json/render/RenderAsJmesPathRenderer.java | 3 +
.../json/render/RenderAsJsonRenderer.java | 4 +
.../action/ExportTransactionAndSamplerNames.java | 6 +
.../visualizers/RenderAsBoundaryExtractor.java | 3 +
.../jmeter/visualizers/RenderAsCssJQuery.java | 3 +
.../jmeter/visualizers/RenderAsDocument.java | 3 +
.../apache/jmeter/visualizers/RenderAsHTML.java | 3 +
.../jmeter/visualizers/RenderAsHTMLFormatted.java | 3 +
.../visualizers/RenderAsHTMLWithEmbedded.java | 3 +
.../apache/jmeter/visualizers/RenderAsJSON.java | 3 +
.../apache/jmeter/visualizers/RenderAsRegexp.java | 3 +
.../apache/jmeter/visualizers/RenderAsText.java | 3 +
.../org/apache/jmeter/visualizers/RenderAsXML.java | 3 +
.../apache/jmeter/visualizers/RenderAsXPath.java | 3 +
.../apache/jmeter/visualizers/RenderAsXPath2.java | 3 +
.../apache/jmeter/visualizers/RenderInBrowser.java | 3 +
.../apache/jmeter/visualizers/RequestPanel.java | 43 +++----
.../org/apache/jmeter/visualizers/RequestView.java | 3 +
.../apache/jmeter/visualizers/RequestViewRaw.java | 3 +
.../apache/jmeter/visualizers/ResultRenderer.java | 2 +
.../visualizers/ViewResultsFullVisualizer.java | 43 +++----
.../backend/AbstractBackendListenerClient.java | 3 +
.../visualizers/backend/BackendListenerClient.java | 2 +
.../visualizers/backend/BackendListenerGui.java | 34 ++---
.../graphite/GraphiteBackendListenerClient.java | 4 +
.../influxdb/InfluxDBRawBackendListenerClient.java | 3 +
.../influxdb/InfluxdbBackendListenerClient.java | 4 +
.../jmeter/engine/util/CompoundVariable.java | 19 +--
.../java/org/apache/jmeter/functions/Function.java | 6 +-
.../org/apache/jmeter/gui/HtmlReportAction.java | 7 ++
.../org/apache/jmeter/gui/action/AboutCommand.java | 3 +
.../org/apache/jmeter/gui/action/ActionRouter.java | 1 +
.../org/apache/jmeter/gui/action/AddParent.java | 3 +
.../gui/action/AddThinkTimeBetweenEachStep.java | 3 +
.../org/apache/jmeter/gui/action/AddToTree.java | 3 +
.../jmeter/gui/action/ApplyNamingConvention.java | 3 +
.../apache/jmeter/gui/action/ChangeLanguage.java | 3 +
.../org/apache/jmeter/gui/action/ChangeParent.java | 3 +
.../org/apache/jmeter/gui/action/CheckDirty.java | 3 +
.../java/org/apache/jmeter/gui/action/Clear.java | 3 +
.../java/org/apache/jmeter/gui/action/Close.java | 3 +
.../apache/jmeter/gui/action/CollapseExpand.java | 3 +
.../gui/action/CollapseExpandTreeBranch.java | 3 +
.../gui/action/CompileJSR223TestElements.java | 6 +
.../java/org/apache/jmeter/gui/action/Copy.java | 3 +
.../jmeter/gui/action/CreateFunctionDialog.java | 3 +
.../java/org/apache/jmeter/gui/action/Cut.java | 3 +
.../org/apache/jmeter/gui/action/Duplicate.java | 3 +
.../org/apache/jmeter/gui/action/EditCommand.java | 3 +
.../apache/jmeter/gui/action/EnableComponent.java | 3 +
.../org/apache/jmeter/gui/action/ExitCommand.java | 3 +
.../java/org/apache/jmeter/gui/action/Help.java | 3 +
.../java/org/apache/jmeter/gui/action/Load.java | 2 +
.../jmeter/gui/action/LoadRecentProject.java | 3 +
.../apache/jmeter/gui/action/LogLevelCommand.java | 3 +
.../gui/action/LoggerPanelEnableDisable.java | 3 +
.../jmeter/gui/action/LookAndFeelCommand.java | 2 +
.../java/org/apache/jmeter/gui/action/Move.java | 3 +
.../apache/jmeter/gui/action/OpenLinkAction.java | 3 +
.../java/org/apache/jmeter/gui/action/Paste.java | 3 +
.../org/apache/jmeter/gui/action/RemoteStart.java | 3 +
.../java/org/apache/jmeter/gui/action/Remove.java | 3 +
.../jmeter/gui/action/ResetSearchCommand.java | 3 +
.../java/org/apache/jmeter/gui/action/Restart.java | 6 +
.../apache/jmeter/gui/action/RevertProject.java | 3 +
.../jmeter/gui/action/SSLManagerCommand.java | 3 +
.../java/org/apache/jmeter/gui/action/Save.java | 3 +
.../apache/jmeter/gui/action/SaveBeforeRun.java | 3 +
.../org/apache/jmeter/gui/action/SaveGraphics.java | 3 +
.../apache/jmeter/gui/action/SchematicView.java | 6 +
.../jmeter/gui/action/SearchTreeCommand.java | 3 +
.../java/org/apache/jmeter/gui/action/Start.java | 3 +
.../apache/jmeter/gui/action/StopStoppables.java | 3 +
.../apache/jmeter/gui/action/TemplatesCommand.java | 3 +
.../org/apache/jmeter/gui/action/UndoCommand.java | 3 +
.../java/org/apache/jmeter/gui/action/What.java | 3 +
.../org/apache/jmeter/gui/action/ZoomInOut.java | 3 +
.../org/apache/jmeter/gui/plugin/MenuCreator.java | 3 +
.../org/apache/jmeter/gui/util/JMeterMenuBar.java | 47 ++-----
.../org/apache/jmeter/gui/util/MenuFactory.java | 2 +
.../threads/RemoteThreadsLifeCycleListener.java | 3 +
.../jmeter/threads/RemoteThreadsListenerImpl.java | 38 ++----
.../java/org/apache/jmeter/util/JMeterUtils.java | 75 +++++++++++
.../java/org/apache/jorphan/test/AllTests.java | 2 +
.../java/org/apache/jmeter/junit/JMeterTest.java | 1 +
.../apache/jmeter/testbeans/gui/PackageTest.java | 1 +
.../apache/jorphan/reflect/TestClassFinder.java | 17 ++-
.../org/apache/jmeter/functions/BeanShell.java | 3 +
.../java/org/apache/jmeter/functions/CSVRead.java | 3 +
.../org/apache/jmeter/functions/ChangeCase.java | 3 +
.../org/apache/jmeter/functions/CharFunction.java | 3 +
.../jmeter/functions/DateTimeConvertFunction.java | 3 +
.../jmeter/functions/DigestEncodeFunction.java | 3 +
.../org/apache/jmeter/functions/EscapeHtml.java | 3 +
.../jmeter/functions/EscapeOroRegexpChars.java | 3 +
.../org/apache/jmeter/functions/EscapeXml.java | 3 +
.../org/apache/jmeter/functions/EvalFunction.java | 3 +
.../apache/jmeter/functions/EvalVarFunction.java | 3 +
.../org/apache/jmeter/functions/FileToString.java | 3 +
.../org/apache/jmeter/functions/FileWrapper.java | 2 +
.../java/org/apache/jmeter/functions/Groovy.java | 3 +
.../java/org/apache/jmeter/functions/IntSum.java | 3 +
.../org/apache/jmeter/functions/IsPropDefined.java | 3 +
.../org/apache/jmeter/functions/IsVarDefined.java | 3 +
.../apache/jmeter/functions/IterationCounter.java | 3 +
.../org/apache/jmeter/functions/JavaScript.java | 3 +
.../org/apache/jmeter/functions/Jexl2Function.java | 3 +
.../org/apache/jmeter/functions/Jexl3Function.java | 3 +
.../org/apache/jmeter/functions/LogFunction.java | 3 +
.../org/apache/jmeter/functions/LogFunction2.java | 3 +
.../java/org/apache/jmeter/functions/LongSum.java | 3 +
.../org/apache/jmeter/functions/MachineIP.java | 3 +
.../org/apache/jmeter/functions/MachineName.java | 3 +
.../java/org/apache/jmeter/functions/Property.java | 3 +
.../org/apache/jmeter/functions/Property2.java | 3 +
.../java/org/apache/jmeter/functions/Random.java | 3 +
.../org/apache/jmeter/functions/RandomDate.java | 2 +
.../jmeter/functions/RandomFromMultipleVars.java | 3 +
.../org/apache/jmeter/functions/RandomString.java | 3 +
.../org/apache/jmeter/functions/RegexFunction.java | 3 +
.../org/apache/jmeter/functions/SamplerName.java | 3 +
.../org/apache/jmeter/functions/SetProperty.java | 3 +
.../org/apache/jmeter/functions/SplitFunction.java | 3 +
.../apache/jmeter/functions/StringFromFile.java | 3 +
.../org/apache/jmeter/functions/StringToFile.java | 3 +
.../org/apache/jmeter/functions/TestPlanName.java | 3 +
.../apache/jmeter/functions/ThreadGroupName.java | 3 +
.../org/apache/jmeter/functions/ThreadNumber.java | 3 +
.../org/apache/jmeter/functions/TimeFunction.java | 3 +
.../org/apache/jmeter/functions/TimeShift.java | 2 +
.../java/org/apache/jmeter/functions/UnEscape.java | 3 +
.../org/apache/jmeter/functions/UnEscapeHtml.java | 3 +
.../org/apache/jmeter/functions/UrlDecode.java | 3 +
.../org/apache/jmeter/functions/UrlEncode.java | 3 +
.../java/org/apache/jmeter/functions/Uuid.java | 3 +
.../java/org/apache/jmeter/functions/Variable.java | 3 +
.../java/org/apache/jmeter/functions/XPath.java | 3 +
.../jmeter/functions/FunctionServicesTest.kt | 43 +++++++
.../org/apache/jorphan/reflect/ClassFinder.java | 132 +++++++++++++++++++-
.../CollectServiceLoadExceptionHandler.java} | 29 ++---
.../reflect/IgnoreServiceLoadExceptionHandler.java | 12 +-
.../org/apache/jorphan/reflect/JMeterService.java | 39 ++++++
.../LogAndIgnoreServiceLoadExceptionHandler.java | 54 ++++++++
.../RethrowServiceLoadExceptionHandler.java} | 23 ++--
.../reflect/ServiceLoadExceptionHandler.java} | 35 +++---
.../jorphan/reflect/ServiceLoadFailure.java} | 38 ++++--
.../http/gui/action/ParseCurlCommandAction.java | 7 ++
.../protocol/http/proxy/DefaultSamplerCreator.java | 2 +
.../jmeter/protocol/http/proxy/SamplerCreator.java | 2 +
.../protocol/http/proxy/SamplerCreatorFactory.java | 56 +++------
.../http/sampler/AccessLogSamplerBeanInfo.java | 138 +++++++++++----------
.../protocol/http/util/accesslog/Filter.java | 2 +
.../protocol/http/util/accesslog/LogFilter.java | 3 +
.../protocol/http/util/accesslog/LogParser.java | 2 +
.../util/accesslog/OrderPreservingLogParser.java | 3 +
.../http/util/accesslog/SessionFilter.java | 3 +
.../http/util/accesslog/SharedTCLogParser.java | 3 +
.../protocol/http/util/accesslog/TCLogParser.java | 3 +
.../protocol/http/visualizers/RequestViewHTTP.java | 3 +
.../protocol/java/config/gui/JavaConfigGui.java | 29 ++---
.../protocol/java/sampler/JavaSamplerClient.java | 2 +
.../apache/jmeter/protocol/java/test/JavaTest.java | 5 +-
.../jmeter/protocol/java/test/SleepTest.java | 4 +
.../java/control/gui/JUnitTestSamplerGui.java | 12 +-
.../test-services/build.gradle.kts | 8 +-
.../apache/jmeter/util/services/AbstractService.kt | 12 +-
.../services/ServiceNotImplementingInterface.kt | 12 +-
.../util/services/ServiceThrowingException.kt} | 28 ++---
.../util/services/ServiceWithPrivateConstructor.kt | 12 +-
.../apache/jmeter/util/services/WorkableService.kt | 18 +--
.../apache/jmeter/util/services/loadServices.kt} | 37 ++----
.../src/test/kotlin/ServiceLoaderTest.kt | 98 +++++++++++++++
xdocs/changes.xml | 1 +
xdocs/usermanual/jmeter_tutorial.xml | 54 +++++++-
186 files changed, 1282 insertions(+), 448 deletions(-)
diff --git a/build-logic/jvm/build.gradle.kts b/build-logic/jvm/build.gradle.kts
index 0c8b9388a6..7044d76d98 100644
--- a/build-logic/jvm/build.gradle.kts
+++ b/build-logic/jvm/build.gradle.kts
@@ -24,6 +24,7 @@ dependencies {
api(projects.verification)
api("com.github.vlsi.crlf:com.github.vlsi.crlf.gradle.plugin:1.88")
api("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.88")
- api("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21")
+ api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.21")
+ api("org.jetbrains.kotlin.kapt:org.jetbrains.kotlin.kapt.gradle.plugin:1.8.21")
api("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:1.8.10")
}
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.autoservice.gradle.kts
similarity index 53%
copy from build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts
copy to build-logic/jvm/src/main/kotlin/build-logic.autoservice.gradle.kts
index 65cb342e6d..532256faa9 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/build-logic.autoservice.gradle.kts
@@ -16,36 +16,35 @@
*/
import com.github.vlsi.gradle.dsl.configureEach
-import com.github.vlsi.gradle.properties.dsl.props
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("java-library")
- id("build-logic.java")
- id("build-logic.test-base")
- id("com.github.autostyle")
- kotlin("jvm")
}
dependencies {
- testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
+ annotationProcessor("com.google.auto.service:auto-service")
+ compileOnlyApi("com.google.auto.service:auto-service-annotations")
}
-val String.v: String get() = rootProject.extra["$this.version"] as String
+tasks.configureEach<JavaCompile> {
+ // Verify @AutoService annotations
+ options.compilerArgs.add("-Averify=true")
+}
+
+plugins.withId("org.jetbrains.kotlin.jvm") {
+ apply(plugin = "org.jetbrains.kotlin.kapt")
-kotlin {
- // Require explicit access modifiers and require explicit types for public APIs.
- // See https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors
- if (props.bool("kotlin.explicitApi", default = true)) {
- explicitApi()
+ dependencies {
+ "kapt"("com.google.auto.service:auto-service")
+ // Unfortunately, plugins.withId("..kapt") can't be used as kapt plugin adds configuration via plugins.withId
+ findProject(":src:bom-thirdparty")?.let {
+ "kapt"(platform(it))
+ }
}
}
-tasks.configureEach<KotlinCompile> {
- kotlinOptions {
- if (!name.startsWith("compileTest")) {
- apiVersion = "kotlin.api".v
- }
- jvmTarget = java.targetCompatibility.toString()
+tasks.configureEach<Jar> {
+ manifest {
+ attributes["JMeter-Skip-Class-Scanning"] = "true"
}
}
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
index 534f894dfc..b2d6967d71 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
@@ -18,4 +18,5 @@
plugins {
id("build-logic.java")
id("java-library")
+ id("build-logic.autoservice")
}
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts
index 175c9740af..3f4a60691f 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts
@@ -59,6 +59,7 @@ dependencies {
}
findProject(":src:bom-thirdparty")?.let{
api(platform(it))
+ annotationProcessor(platform(it))
}
}
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts
index cabe6e0282..7f5f0315fd 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts
@@ -16,8 +16,7 @@
*/
plugins {
- id("build-logic.java")
- id("java-library")
+ id("build-logic.java-library")
}
if (file("src/main/groovy").isDirectory || file("src/test/groovy").isDirectory) {
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts
index 65cb342e6d..59a63eb01d 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts
@@ -25,6 +25,7 @@ plugins {
id("build-logic.test-base")
id("com.github.autostyle")
kotlin("jvm")
+ kotlin("kapt") apply false
}
dependencies {
diff --git a/build-logic/root-build/build.gradle.kts b/build-logic/root-build/build.gradle.kts
index a467ad083a..5e3d3e23bd 100644
--- a/build-logic/root-build/build.gradle.kts
+++ b/build-logic/root-build/build.gradle.kts
@@ -27,4 +27,5 @@ dependencies {
api("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.88")
api("org.nosphere.apache.rat:org.nosphere.apache.rat.gradle.plugin:0.8.0")
api("org.jetbrains.gradle.plugin.idea-ext:org.jetbrains.gradle.plugin.idea-ext.gradle.plugin:1.1.7")
+ api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.21")
}
diff --git a/build-logic/root-build/src/main/kotlin/build-logic.root-build.gradle.kts b/build-logic/root-build/src/main/kotlin/build-logic.root-build.gradle.kts
index 2e75d5e448..7fcb96ff8d 100644
--- a/build-logic/root-build/src/main/kotlin/build-logic.root-build.gradle.kts
+++ b/build-logic/root-build/src/main/kotlin/build-logic.root-build.gradle.kts
@@ -26,6 +26,7 @@ plugins {
id("com.github.vlsi.ide")
id("org.nosphere.apache.rat")
id("org.jetbrains.gradle.plugin.idea-ext")
+ kotlin("jvm") apply false
}
ide {
diff --git a/build.gradle.kts b/build.gradle.kts
index 91cb5261d7..50ac4136f7 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -61,6 +61,7 @@ val notPublishedProjects by extra {
projects.src.release,
projects.src.testkit,
projects.src.testkitWiremock,
+ projects.src.testServices,
).mapTo(mutableSetOf()) { it.dependencyProject }
}
@@ -86,7 +87,7 @@ publishedProjects.forEach {project ->
throw IllegalStateException(
"Project ${project.path} is listed in publishedProjects, however it misses maven-publish plugin. " +
"Please add maven-publish plugin (e.g. replace build-logic.jvm-library with build-logic.jvm-published-library) or " +
- "move the project to the list of published ones"
+ "move the project to the list of notPublishedProjects"
)
}
}
diff --git a/gradle.properties b/gradle.properties
index 3801fc003f..eb14a43881 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,6 +21,8 @@ org.gradle.parallel=true
org.gradle.caching=true
#org.gradle.caching.debug=true
+kapt.include.compile.classpath=false
+
# 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
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2537f530c1..307ffd7b29 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -76,6 +76,7 @@ include(
"src:release",
"src:testkit",
"src:testkit-wiremock",
+ "src:test-services",
"src:dist",
"src:dist-check"
)
diff --git a/src/bom-thirdparty/build.gradle.kts b/src/bom-thirdparty/build.gradle.kts
index c7de118202..3483b7c2c5 100644
--- a/src/bom-thirdparty/build.gradle.kts
+++ b/src/bom-thirdparty/build.gradle.kts
@@ -42,6 +42,8 @@ dependencies {
api("bsf:bsf:2.4.0")
api("cglib:cglib-nodep:3.3.0")
+ api("com.google.auto.service:auto-service:1.0.1")
+ api("com.google.auto.service:auto-service-annotations:1.0.1")
api("com.fasterxml.jackson.core:jackson-annotations:2.13.4")
api("com.fasterxml.jackson.core:jackson-core:2.13.4")
api("com.fasterxml.jackson.core:jackson-databind:2.13.4.2")
diff --git a/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJmesPathRenderer.java b/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJmesPathRenderer.java
index d2131a3350..45e1241d3c 100644
--- a/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJmesPathRenderer.java
+++ b/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJmesPathRenderer.java
@@ -19,6 +19,7 @@ package org.apache.jmeter.extractor.json.render;
import org.apache.jmeter.extractor.json.jmespath.JMESPathCache;
import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.visualizers.ResultRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,11 +27,13 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for JMES Path tester
* @since 5.2
*/
+@AutoService(ResultRenderer.class)
public class RenderAsJmesPathRenderer extends AbstractRenderAsJsonRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsJmesPathRenderer.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
diff --git a/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJsonRenderer.java b/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJsonRenderer.java
index e7d856db82..0aa1a01132 100644
--- a/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJsonRenderer.java
+++ b/src/components/src/main/java/org/apache/jmeter/extractor/json/render/RenderAsJsonRenderer.java
@@ -21,13 +21,17 @@ import java.util.List;
import org.apache.jmeter.extractor.json.jsonpath.JSONManager;
import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.visualizers.ResultRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implement ResultsRender for JSON Path tester
* @since 3.0
*/
+@AutoService(ResultRenderer.class)
public class RenderAsJsonRenderer extends AbstractRenderAsJsonRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsJsonRenderer.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/gui/action/ExportTransactionAndSamplerNames.java b/src/components/src/main/java/org/apache/jmeter/gui/action/ExportTransactionAndSamplerNames.java
index 093f217391..cc988f7d9f 100644
--- a/src/components/src/main/java/org/apache/jmeter/gui/action/ExportTransactionAndSamplerNames.java
+++ b/src/components/src/main/java/org/apache/jmeter/gui/action/ExportTransactionAndSamplerNames.java
@@ -53,10 +53,16 @@ import org.apache.jorphan.gui.ComponentUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Export transactions names for web report
* @since 3.3
*/
+@AutoService({
+ Command.class,
+ MenuCreator.class
+})
public class ExportTransactionAndSamplerNames extends AbstractAction implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(ExportTransactionAndSamplerNames.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsBoundaryExtractor.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsBoundaryExtractor.java
index e48002e246..236d2db7f3 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsBoundaryExtractor.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsBoundaryExtractor.java
@@ -41,9 +41,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.GuiUtils;
import org.apache.jorphan.gui.JLabeledTextField;
+import com.google.auto.service.AutoService;
+
/**
* Implement ResultsRender for Boundary Extractor tester
*/
+@AutoService(ResultRenderer.class)
public class RenderAsBoundaryExtractor implements ResultRenderer, ActionListener {
private static final String BOUNDARY_EXTRACTOR_TESTER_COMMAND = "boundary_extractor_tester"; // $NON-NLS-1$
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsCssJQuery.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsCssJQuery.java
index e802246fea..aa0be670a0 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsCssJQuery.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsCssJQuery.java
@@ -49,10 +49,13 @@ import org.apache.jorphan.gui.JLabeledChoice;
import org.apache.jorphan.gui.JLabeledTextField;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
+import com.google.auto.service.AutoService;
+
/**
* Implement ResultsRender for CSS/JQuery tester
* @since 2.10
*/
+@AutoService(ResultRenderer.class)
public class RenderAsCssJQuery implements ResultRenderer, ActionListener {
private static final String CSSJQUEY_TESTER_COMMAND = "cssjquery_tester"; // $NON-NLS-1$
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsDocument.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsDocument.java
index d7fde8f641..0bb01d437d 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsDocument.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsDocument.java
@@ -23,6 +23,9 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsDocument extends SamplerResultTab implements ResultRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsDocument.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTML.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTML.java
index 043e7bc9f9..cd26262a15 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTML.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTML.java
@@ -33,6 +33,9 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsHTML extends SamplerResultTab implements ResultRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsHTML.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLFormatted.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLFormatted.java
index cfbb7d208e..66b2b84ab4 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLFormatted.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLFormatted.java
@@ -21,6 +21,9 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.jsoup.Jsoup;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsHTMLFormatted extends SamplerResultTab implements ResultRenderer {
/** {@inheritDoc} */
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java
index d92f150120..4fa67e0569 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java
@@ -20,6 +20,9 @@ package org.apache.jmeter.visualizers;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsHTMLWithEmbedded extends RenderAsHTML
implements ResultRenderer {
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsJSON.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsJSON.java
index b1550a754b..b99d3d6c59 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsJSON.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsJSON.java
@@ -29,6 +29,9 @@ import net.minidev.json.JSONStyle;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsJSON extends SamplerResultTab implements ResultRenderer {
private static final String TAB_SEPARATOR = " "; //$NON-NLS-1$
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsRegexp.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsRegexp.java
index 375d90299d..01d8358e03 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsRegexp.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsRegexp.java
@@ -50,9 +50,12 @@ import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
+import com.google.auto.service.AutoService;
+
/**
* Implement ResultsRender for Regexp tester
*/
+@AutoService(ResultRenderer.class)
public class RenderAsRegexp implements ResultRenderer, ActionListener {
private static final String REGEXP_TESTER_COMMAND = "regexp_tester"; // $NON-NLS-1$
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsText.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsText.java
index 34616fe982..5d315677a5 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsText.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsText.java
@@ -20,6 +20,9 @@ package org.apache.jmeter.visualizers;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsText extends SamplerResultTab implements ResultRenderer {
/** {@inheritDoc} */
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXML.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXML.java
index 5c8b6af25b..beb976f2ad 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXML.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXML.java
@@ -53,6 +53,9 @@ import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
import org.xml.sax.SAXException;
+import com.google.auto.service.AutoService;
+
+@AutoService(ResultRenderer.class)
public class RenderAsXML extends SamplerResultTab
implements ResultRenderer {
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath.java
index df39c62e71..6058ce7ca3 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath.java
@@ -59,10 +59,13 @@ import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
+import com.google.auto.service.AutoService;
+
/**
* Implement ResultsRender for XPath tester
*/
+@AutoService(ResultRenderer.class)
public class RenderAsXPath implements ResultRenderer, ActionListener {
private static final Logger log = LoggerFactory.getLogger(RenderAsXPath.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath2.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath2.java
index e36484bf90..42214a70af 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath2.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsXPath2.java
@@ -48,9 +48,12 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implement ResultsRender for XPath tester
*/
+@AutoService(ResultRenderer.class)
public class RenderAsXPath2 implements ResultRenderer, ActionListener {
private static final Logger log = LoggerFactory.getLogger(RenderAsXPath2.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderInBrowser.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderInBrowser.java
index 575313c359..eab0bfd39a 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderInBrowser.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderInBrowser.java
@@ -27,6 +27,8 @@ import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
+import com.google.auto.service.AutoService;
+
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
@@ -43,6 +45,7 @@ import javafx.scene.web.WebView;
* {@link ResultRenderer} implementation that uses JAVAFX WebEngine to render as browser do
* @since 3.2
*/
+@AutoService(ResultRenderer.class)
public class RenderInBrowser extends SamplerResultTab implements ResultRenderer {
private JFXPanel jfxPanel;
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RequestPanel.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestPanel.java
index ac3a6a09e1..2b668a8ec3 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RequestPanel.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestPanel.java
@@ -18,11 +18,9 @@
package org.apache.jmeter.visualizers;
import java.awt.BorderLayout;
-import java.io.IOException;
import java.util.ArrayDeque;
-import java.util.Collections;
import java.util.Deque;
-import java.util.List;
+import java.util.ServiceLoader;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
@@ -30,6 +28,7 @@ import javax.swing.SwingConstants;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,37 +51,23 @@ public class RequestPanel {
*/
public RequestPanel() {
listRequestView = new ArrayDeque<>();
- List<String> classesToAdd = Collections.<String> emptyList();
- try {
- classesToAdd = JMeterUtils.findClassesThatExtend(RequestView.class);
- } catch (IOException e1) {
- // ignored
- }
String rawTab = JMeterUtils.getResString(RequestViewRaw.KEY_LABEL); // $NON-NLS-1$
- Object rawObject = null;
- for (String clazz : classesToAdd) {
- try {
- // Instantiate requestview classes
- final RequestView requestView = Class.forName(clazz)
- .asSubclass(RequestView.class)
- .getDeclaredConstructor().newInstance();
- if (rawTab.equals(requestView.getLabel())) {
- rawObject = requestView; // use later
- } else {
- listRequestView.add(requestView);
- }
- }
- catch (NoClassDefFoundError e) {
- log.error("Exception registering implementation: [{}] of interface: [{}], a dependency used by the plugin class is missing",
- clazz, RequestView.class, e);
- } catch (Exception e) {
- log.error("Exception registering implementation: [{}] of interface: [{}], a jar is probably missing",
- clazz, RequestView.class, e);
+ RequestView rawObject = null;
+ for (RequestView requestView : JMeterUtils.loadServicesAndScanJars(
+ RequestView.class,
+ ServiceLoader.load(RequestView.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ )) {
+ if (rawTab.equals(requestView.getLabel())) {
+ rawObject = requestView; // use later
+ } else {
+ listRequestView.add(requestView);
}
}
// place raw tab in first position (first tab)
if (rawObject != null) {
- listRequestView.addFirst((RequestView) rawObject);
+ listRequestView.addFirst(rawObject);
}
// Prepare the Request tabbed pane
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RequestView.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestView.java
index 792f5e2ffc..e84cc4c038 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RequestView.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestView.java
@@ -19,12 +19,15 @@ package org.apache.jmeter.visualizers;
import javax.swing.JPanel;
+import org.apache.jorphan.reflect.JMeterService;
+
/**
* Interface for request panel in View Results Tree
* All classes which implements this interface is display
* on bottom tab in request panel
*
*/
+@JMeterService
public interface RequestView {
/**
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RequestViewRaw.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestViewRaw.java
index c721a152d5..b253e0381d 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RequestViewRaw.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestViewRaw.java
@@ -30,10 +30,13 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.GuiUtils;
+import com.google.auto.service.AutoService;
+
/**
* (historical) Panel to view request data
*
*/
+@AutoService(RequestView.class)
public class RequestViewRaw implements RequestView {
// Used by Request Panel
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/ResultRenderer.java b/src/components/src/main/java/org/apache/jmeter/visualizers/ResultRenderer.java
index 3f9e5f22b9..dba2ba3e58 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/ResultRenderer.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/ResultRenderer.java
@@ -22,11 +22,13 @@ import java.awt.Color;
import javax.swing.JTabbedPane;
import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jorphan.reflect.JMeterService;
/**
* Interface to results render
*/
+@JMeterService
public interface ResultRenderer {
void clearData();
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java b/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
index 4f44ca2219..5ec83c5e5c 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
@@ -25,7 +25,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
-import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -36,6 +35,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
@@ -73,6 +73,7 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jorphan.gui.JMeterUIDefaults;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.apache.jorphan.util.StringWrap;
import org.apiguardian.api.API;
import org.slf4j.Logger;
@@ -454,38 +455,24 @@ implements ActionListener, TreeSelectionListener, Clearable, ItemListener {
selectRenderPanel.addActionListener(this);
// if no results render in jmeter.properties, load Standard (default)
- List<String> classesToAdd = Collections.<String>emptyList();
- try {
- classesToAdd = JMeterUtils.findClassesThatExtend(ResultRenderer.class);
- } catch (IOException e1) {
- // ignored
- }
String defaultRenderer = expandToClassname(".RenderAsText"); // $NON-NLS-1$
if (VIEWERS_ORDER.length() > 0) {
defaultRenderer = expandToClassname(VIEWERS_ORDER.split(",", 2)[0]);
}
- Object defaultObject = null;
- Map<String, ResultRenderer> map = new HashMap<>(classesToAdd.size());
- for (String clazz : classesToAdd) {
- try {
- // Instantiate render classes
- final ResultRenderer renderer = Class.forName(clazz)
- .asSubclass(ResultRenderer.class)
- .getDeclaredConstructor().newInstance();
- if (defaultRenderer.equals(clazz)) {
- defaultObject=renderer;
- }
- renderer.setBackgroundColor(getBackground());
- map.put(renderer.getClass().getName(), renderer);
- } catch (NoClassDefFoundError e) { // NOSONAR See bug 60583
- if (e.getMessage() != null && e.getMessage().contains("javafx")) {
- log.info("Add JavaFX to your Java installation if you want to use renderer: {}", clazz);
- } else {
- log.warn("Error loading result renderer: {}", clazz, e);
- }
- } catch (Exception e) {
- log.warn("Error loading result renderer: {}", clazz, e);
+ ResultRenderer defaultObject = null;
+ Map<String, ResultRenderer> map = new HashMap<>();
+ for (ResultRenderer renderer : JMeterUtils.loadServicesAndScanJars(
+ ResultRenderer.class,
+ ServiceLoader.load(ResultRenderer.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ )) {
+ // Instantiate render classes
+ if (defaultRenderer.equals(renderer.getClass().getName())) {
+ defaultObject = renderer;
}
+ renderer.setBackgroundColor(getBackground());
+ map.put(renderer.getClass().getName(), renderer);
}
if (VIEWERS_ORDER.length() > 0) {
Arrays.stream(VIEWERS_ORDER.split(","))
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
index ec0a870e43..79cdd58a6e 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/AbstractBackendListenerClient.java
@@ -24,6 +24,8 @@ import org.apache.jmeter.config.Arguments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* An abstract implementation of the BackendListenerClient interface. This
* implementation provides default implementations of most of the methods in the
@@ -49,6 +51,7 @@ import org.slf4j.LoggerFactory;
* @see BackendListener#sampleOccurred(org.apache.jmeter.samplers.SampleEvent)
* @since 2.13
*/
+@AutoService(BackendListenerClient.class)
public abstract class AbstractBackendListenerClient implements BackendListenerClient {
private static final Logger log = LoggerFactory.getLogger(AbstractBackendListenerClient.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerClient.java b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerClient.java
index e3aee0aa10..b8ab2dc4e7 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerClient.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerClient.java
@@ -21,6 +21,7 @@ import java.util.List;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jorphan.reflect.JMeterService;
/**
* This interface defines the interactions between the {@link BackendListener}
@@ -63,6 +64,7 @@ import org.apache.jmeter.samplers.SampleResult;
*
* @since 2.13
*/
+@JMeterService
public interface BackendListenerClient {
/**
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerGui.java b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerGui.java
index dc98715df5..2e79427ed1 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerGui.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/BackendListenerGui.java
@@ -20,11 +20,10 @@ package org.apache.jmeter.visualizers.backend;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
+import java.util.ServiceLoader;
import java.util.Set;
import javax.swing.ComboBoxModel;
@@ -33,7 +32,6 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
-import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
@@ -45,7 +43,7 @@ import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractListenerGui;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -77,7 +75,6 @@ public class BackendListenerGui extends AbstractListenerGui implements ActionLis
/** The current className of the Backend listener **/
private String className;
-
/**
* Create a new BackendListenerGui as a standalone component.
*/
@@ -117,25 +114,18 @@ public class BackendListenerGui extends AbstractListenerGui implements ActionLis
* @return a panel containing the relevant components
*/
private JPanel createClassnamePanel() {
- List<String> possibleClasses = new ArrayList<>();
-
- try {
- // Find all the classes which implement the BackendListenerClient
- // interface.
- possibleClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),
- new Class[] { BackendListenerClient.class });
-
- // Remove the BackendListener class from the list since it only
- // implements the interface for error conditions.
-
- possibleClasses.remove(BackendListener.class.getName() + "$ErrorBackendListenerClient");
- } catch (Exception e) {
- log.debug("Exception getting interfaces.", e);
- }
-
JLabel label = new JLabel(JMeterUtils.getResString("backend_listener_classname")); // $NON-NLS-1$
- classnameCombo = new JComboBox<>(possibleClasses.toArray(ArrayUtils.EMPTY_STRING_ARRAY));
+ String[] listenerClasses = JMeterUtils.loadServicesAndScanJars(
+ BackendListenerClient.class,
+ ServiceLoader.load(BackendListenerClient.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ ).stream()
+ .map(s -> s.getClass().getName())
+ .sorted()
+ .toArray(String[]::new);
+ classnameCombo = new JComboBox<>(listenerClasses);
classnameCombo.addActionListener(this);
classnameCombo.setEditable(false);
label.setLabelFor(classnameCombo);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
index 16d8b4a7db..36ad35ec00 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/graphite/GraphiteBackendListenerClient.java
@@ -37,17 +37,21 @@ import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
+import org.apache.jmeter.visualizers.backend.BackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.apache.jmeter.visualizers.backend.SamplerMetric;
import org.apache.jmeter.visualizers.backend.UserMetric;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Graphite based Listener using Pickle Protocol
* @see <a href="http://graphite.readthedocs.org/en/latest/overview.html">Graphite Overview</a>
* @since 2.13
*/
+@AutoService(BackendListenerClient.class)
public class GraphiteBackendListenerClient extends AbstractBackendListenerClient implements Runnable {
//+ Argument names
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxDBRawBackendListenerClient.java b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxDBRawBackendListenerClient.java
index f959691f17..57be7a8a39 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxDBRawBackendListenerClient.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxDBRawBackendListenerClient.java
@@ -29,6 +29,8 @@ import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implementation of {@link BackendListenerClient} to write the response times
* of every sample to InfluxDB. If more "raw" information is required in InfluxDB
@@ -38,6 +40,7 @@ import org.slf4j.LoggerFactory;
*
* @since 5.3
*/
+@AutoService(BackendListenerClient.class)
public class InfluxDBRawBackendListenerClient implements BackendListenerClient {
private static final Logger log = LoggerFactory.getLogger(InfluxDBRawBackendListenerClient.class);
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
index 0dc8d4f7fc..11a8739b8c 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/backend/influxdb/InfluxdbBackendListenerClient.java
@@ -36,6 +36,7 @@ import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
+import org.apache.jmeter.visualizers.backend.BackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.apache.jmeter.visualizers.backend.ErrorMetric;
import org.apache.jmeter.visualizers.backend.SamplerMetric;
@@ -43,12 +44,15 @@ import org.apache.jmeter.visualizers.backend.UserMetric;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implementation of {@link AbstractBackendListenerClient} to write to InfluxDB
* using a custom schema; since JMeter 5.2, this also support the InfluxDB v2.
*
* @since 3.2
*/
+@AutoService(BackendListenerClient.class)
public class InfluxdbBackendListenerClient extends AbstractBackendListenerClient implements Runnable {
private static final Logger log = LoggerFactory.getLogger(InfluxdbBackendListenerClient.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/engine/util/CompoundVariable.java b/src/core/src/main/java/org/apache/jmeter/engine/util/CompoundVariable.java
index 1059d4675d..b298fde061 100644
--- a/src/core/src/main/java/org/apache/jmeter/engine/util/CompoundVariable.java
+++ b/src/core/src/main/java/org/apache/jmeter/engine/util/CompoundVariable.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.ServiceLoader;
import org.apache.jmeter.functions.Function;
import org.apache.jmeter.functions.InvalidVariableException;
@@ -30,7 +31,7 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.JMeterUtils;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,15 +70,15 @@ public class CompoundVariable implements Function {
log.info("Note: Function class names must not contain the string: '{}'", notContain);
}
- List<String> classes = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),
- new Class[] { Function.class }, true, contain, notContain);
- for (String clazzName : classes) {
- Function tempFunc = Class.forName(clazzName)
- .asSubclass(Function.class)
- .getDeclaredConstructor().newInstance();
- String referenceKey = tempFunc.getReferenceKey();
+ for (Function function : JMeterUtils.loadServicesAndScanJars(
+ Function.class,
+ ServiceLoader.load(Function.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ )) {
+ String referenceKey = function.getReferenceKey();
if (referenceKey.length() > 0) { // ignore self
- functions.put(referenceKey, tempFunc.getClass());
+ functions.put(referenceKey, function.getClass());
}
}
diff --git a/src/core/src/main/java/org/apache/jmeter/functions/Function.java b/src/core/src/main/java/org/apache/jmeter/functions/Function.java
index ff27cd5595..fb4d759d10 100644
--- a/src/core/src/main/java/org/apache/jmeter/functions/Function.java
+++ b/src/core/src/main/java/org/apache/jmeter/functions/Function.java
@@ -23,18 +23,20 @@ import java.util.List;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
+import org.apache.jorphan.reflect.JMeterService;
/**
* Methods that a function must implement
*/
+@JMeterService
public interface Function {
/**
* Given the previous SampleResult and the current Sampler, return a string
* to use as a replacement value for the function call. Assume
* "setParameter" was previously called.
*
- * This method must be threadsafe - multiple threads will be using the same
- * object.
+ * <p>This method must be thread-safe - multiple threads will be using the same
+ * object.</p>
* @param previousResult The previous {@link SampleResult}
* @param currentSampler The current {@link Sampler}
* @return The replacement value, which was generated by the function
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/HtmlReportAction.java b/src/core/src/main/java/org/apache/jmeter/gui/HtmlReportAction.java
index cecd111bdb..42e1e175ef 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/HtmlReportAction.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/HtmlReportAction.java
@@ -30,9 +30,16 @@ import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.gui.action.AbstractAction;
import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
+import org.apache.jmeter.gui.action.Command;
import org.apache.jmeter.gui.plugin.MenuCreator;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
+@AutoService({
+ Command.class,
+ MenuCreator.class
+})
public class HtmlReportAction extends AbstractAction implements MenuCreator {
private static final Set<String> commands = new HashSet<>();
private HtmlReportUI htmlReportPanel;
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/AboutCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/AboutCommand.java
index d200380078..4be2b38389 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/AboutCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/AboutCommand.java
@@ -42,10 +42,13 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.util.EscapeDialog;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* About Command. It may be extended in the future to add a list of installed
* protocols, config options, etc.
*/
+@AutoService(Command.class)
public class AboutCommand extends AbstractAction {
private static final Set<String> commandSet;
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ActionRouter.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ActionRouter.java
index 53110bd3a9..49b66206c1 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ActionRouter.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ActionRouter.java
@@ -309,6 +309,7 @@ public final class ActionRouter implements ActionListener {
}
}
+ @SuppressWarnings("deprecation")
private static List<String> findClassesThatExtend(String className, String excluding, String[] searchPath) throws IOException, ClassNotFoundException {
return ClassFinder.findClassesThatExtend(
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/AddParent.java b/src/core/src/main/java/org/apache/jmeter/gui/action/AddParent.java
index 32dad85108..4e8b9b8b42 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/AddParent.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/AddParent.java
@@ -28,9 +28,12 @@ import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Add Parent menu command
*/
+@AutoService(Command.class)
public class AddParent extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(AddParent.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/AddThinkTimeBetweenEachStep.java b/src/core/src/main/java/org/apache/jmeter/gui/action/AddThinkTimeBetweenEachStep.java
index b7966e7946..44ec34fc65 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/AddThinkTimeBetweenEachStep.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/AddThinkTimeBetweenEachStep.java
@@ -33,10 +33,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Add ThinkTime (TestAction + UniformRandomTimer)
* @since 3.2
*/
+@AutoService(Command.class)
public class AddThinkTimeBetweenEachStep extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(AddThinkTimeBetweenEachStep.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/AddToTree.java b/src/core/src/main/java/org/apache/jmeter/gui/action/AddToTree.java
index 8de19b9804..ecd423060c 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/AddToTree.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/AddToTree.java
@@ -32,6 +32,9 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
+@AutoService(Command.class)
public class AddToTree extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(AddToTree.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ApplyNamingConvention.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ApplyNamingConvention.java
index 2cda16bb13..01ea85f269 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ApplyNamingConvention.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ApplyNamingConvention.java
@@ -32,10 +32,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Allows to apply naming convention on nodes
* @since 3.2
*/
+@AutoService(Command.class)
public class ApplyNamingConvention extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(ApplyNamingConvention.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeLanguage.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeLanguage.java
index 1cb2807381..2d72b1f041 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeLanguage.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeLanguage.java
@@ -28,9 +28,12 @@ import org.apache.jorphan.util.JMeterError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Change language
*/
+@AutoService(Command.class)
public class ChangeLanguage extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeParent.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeParent.java
index 3396c51fed..8c9729416a 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeParent.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ChangeParent.java
@@ -38,9 +38,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Allows to change Controller implementation
*/
+@AutoService(Command.class)
public class ChangeParent extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(ChangeParent.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/CheckDirty.java b/src/core/src/main/java/org/apache/jmeter/gui/action/CheckDirty.java
index 3ab37b81e5..eb3885d32c 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/CheckDirty.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/CheckDirty.java
@@ -33,10 +33,13 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Check if the TestPlan has been changed since it was last saved
*
*/
+@AutoService(Command.class)
public class CheckDirty extends AbstractAction implements HashTreeTraverser, ActionListener {
private static final Logger log = LoggerFactory.getLogger(CheckDirty.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Clear.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Clear.java
index 1f6964d0b8..a3f3a3998b 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Clear.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Clear.java
@@ -28,12 +28,15 @@ import org.apache.jmeter.samplers.Clearable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Handles the following actions:
* - Clear (Data)
* - Clear All (Data)
* - Reset (Clear GUI)
*/
+@AutoService(Command.class)
public class Clear extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Clear.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Close.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Close.java
index 405f39bb8b..a0c8348ed4 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Close.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Close.java
@@ -29,11 +29,14 @@ import org.apache.jmeter.gui.util.FocusRequester;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* This command clears the existing test plan, allowing the creation of a New
* test plan.
*
*/
+@AutoService(Command.class)
public class Close extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpand.java
index 1008ef763c..72da59d65f 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpand.java
@@ -25,10 +25,13 @@ import javax.swing.JTree;
import org.apache.jmeter.gui.GuiPackage;
+import com.google.auto.service.AutoService;
+
/**
* Processes the Collapse All and Expand All options.
*
*/
+@AutoService(Command.class)
public class CollapseExpand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpandTreeBranch.java b/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpandTreeBranch.java
index a70509d6fa..2b7af0730c 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpandTreeBranch.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/CollapseExpandTreeBranch.java
@@ -30,9 +30,12 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
+import com.google.auto.service.AutoService;
+
/**
* Processes the collapse and expand of a tree branch
*/
+@AutoService(Command.class)
public class CollapseExpandTreeBranch extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/CompileJSR223TestElements.java b/src/core/src/main/java/org/apache/jmeter/gui/action/CompileJSR223TestElements.java
index f2dd30e985..7539d09d6d 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/CompileJSR223TestElements.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/CompileJSR223TestElements.java
@@ -38,10 +38,16 @@ import org.apache.jorphan.collections.HashTreeTraverser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Compile JSR223 Test Element that use Compilable script language
* @since 5.1
*/
+@AutoService({
+ Command.class,
+ MenuCreator.class
+})
public class CompileJSR223TestElements extends AbstractAction implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(CompileJSR223TestElements.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Copy.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Copy.java
index 96165d07d7..3979512efe 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Copy.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Copy.java
@@ -37,9 +37,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Copy menu command
*/
+@AutoService(Command.class)
public class Copy extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Copy.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/CreateFunctionDialog.java b/src/core/src/main/java/org/apache/jmeter/gui/action/CreateFunctionDialog.java
index 371a7e15a2..5e4d43a6f0 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/CreateFunctionDialog.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/CreateFunctionDialog.java
@@ -23,6 +23,9 @@ import java.util.Set;
import org.apache.jmeter.functions.gui.FunctionHelper;
+import com.google.auto.service.AutoService;
+
+@AutoService(Command.class)
public class CreateFunctionDialog extends AbstractAction {
private static final Set<String> commands;
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Cut.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Cut.java
index 43e2ffbb09..a5a8f201a3 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Cut.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Cut.java
@@ -24,9 +24,12 @@ import java.util.Set;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Cut menu item command
*/
+@AutoService(Command.class)
public class Cut extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Duplicate.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Duplicate.java
index 683a4d8495..996cc149e8 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Duplicate.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Duplicate.java
@@ -26,9 +26,12 @@ import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Duplicate menu command
*/
+@AutoService(Command.class)
public class Duplicate extends AbstractAction {
private static final HashSet<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/EditCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/EditCommand.java
index 705286de04..a6e8c8025e 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/EditCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/EditCommand.java
@@ -25,9 +25,12 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.JMeterGUIComponent;
import org.apache.jorphan.gui.ui.TextComponentUI;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Edit menu item.
*/
+@AutoService(Command.class)
public class EditCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/EnableComponent.java b/src/core/src/main/java/org/apache/jmeter/gui/action/EnableComponent.java
index 65f4407187..e5b9a225ed 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/EnableComponent.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/EnableComponent.java
@@ -26,9 +26,12 @@ import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Enable menu item.
*/
+@AutoService(Command.class)
public class EnableComponent extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(EnableComponent.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ExitCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ExitCommand.java
index e9478721f0..3868e3c844 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ExitCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ExitCommand.java
@@ -26,6 +26,9 @@ import javax.swing.JOptionPane;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
+@AutoService(Command.class)
public class ExitCommand extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Help.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Help.java
index ab39e349ee..78f9767b5a 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Help.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Help.java
@@ -35,9 +35,12 @@ import org.apache.jorphan.gui.ComponentUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Help menu item.
*/
+@AutoService(Command.class)
public class Help extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Help.class);
private static final boolean USE_LOCAL_HELP =
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Load.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Load.java
index 0755b855b6..10b997d141 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Load.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Load.java
@@ -42,6 +42,7 @@ import org.apache.jorphan.collections.HashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
@@ -49,6 +50,7 @@ import com.thoughtworks.xstream.io.StreamException;
* Handles the Open (load a new file) and Merge commands.
*
*/
+@AutoService(Command.class)
public class Load extends AbstractActionWithNoRunningTest {
private static final Logger log = LoggerFactory.getLogger(Load.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/LoadRecentProject.java b/src/core/src/main/java/org/apache/jmeter/gui/action/LoadRecentProject.java
index 9b684ea07c..0b394a749b 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/LoadRecentProject.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/LoadRecentProject.java
@@ -34,10 +34,13 @@ import java.util.stream.IntStream;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
+import com.google.auto.service.AutoService;
+
/**
* Handles the loading of recent files, and also the content and
* visibility of menu items for loading the recent files
*/
+@AutoService(Command.class)
public class LoadRecentProject extends Load {
/** Prefix for the user preference key */
private static final String USER_PREFS_KEY = "recent_file_"; //$NON-NLS-1$
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/LogLevelCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/LogLevelCommand.java
index a21473f19c..b00337f774 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/LogLevelCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/LogLevelCommand.java
@@ -29,10 +29,13 @@ import org.apiguardian.api.API;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Implements log level setting menu item.
* @since 3.2
*/
+@AutoService(Command.class)
public class LogLevelCommand extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(LogLevelCommand.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/LoggerPanelEnableDisable.java b/src/core/src/main/java/org/apache/jmeter/gui/action/LoggerPanelEnableDisable.java
index 7b7c30ee42..f3b3c3e1d9 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/LoggerPanelEnableDisable.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/LoggerPanelEnableDisable.java
@@ -26,10 +26,13 @@ import javax.swing.UIManager;
import org.apache.jmeter.gui.GuiPackage;
+import com.google.auto.service.AutoService;
+
/**
* Hide / unhide LoggerPanel.
*
*/
+@AutoService(Command.class)
public class LoggerPanelEnableDisable extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
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 260c59f4f8..0821b8a05d 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
@@ -40,10 +40,12 @@ import org.apache.jorphan.gui.JFactory;
import com.github.weisj.darklaf.LafManager;
import com.github.weisj.darklaf.theme.DarculaTheme;
import com.github.weisj.darklaf.theme.Theme;
+import com.google.auto.service.AutoService;
/**
* Implements the Look and Feel menu item.
*/
+@AutoService(Command.class)
public class LookAndFeelCommand extends AbstractAction {
private static final String JMETER_LAF = "jmeter.laf"; // $NON-NLS-1$
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Move.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Move.java
index 90350e2a31..d39bde84a5 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Move.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Move.java
@@ -33,10 +33,13 @@ import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
+import com.google.auto.service.AutoService;
+
/**
* Move a node up/down/left/right
*
*/
+@AutoService(Command.class)
public class Move extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/OpenLinkAction.java b/src/core/src/main/java/org/apache/jmeter/gui/action/OpenLinkAction.java
index 418b1b0d1b..be6ea6c430 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/OpenLinkAction.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/OpenLinkAction.java
@@ -29,6 +29,9 @@ import javax.swing.JOptionPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
+@AutoService(Command.class)
public class OpenLinkAction extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(OpenLinkAction.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Paste.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Paste.java
index 6506d3faa5..cc6f670328 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Paste.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Paste.java
@@ -33,9 +33,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Places a copied JMeterTreeNode under the selected node.
*/
+@AutoService(Command.class)
public class Paste extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Paste.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/RemoteStart.java b/src/core/src/main/java/org/apache/jmeter/gui/action/RemoteStart.java
index bb7e67148d..7ca26794da 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/RemoteStart.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/RemoteStart.java
@@ -33,6 +33,9 @@ import org.apache.jmeter.threads.RemoteThreadsListenerTestElement;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
+import com.google.auto.service.AutoService;
+
+@AutoService(Command.class)
public class RemoteStart extends AbstractAction {
private static final String LOCAL_HOST = "127.0.0.1"; // NOSONAR $NON-NLS-1$
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Remove.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Remove.java
index 3e18f68e88..d3a95d8517 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Remove.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Remove.java
@@ -29,9 +29,12 @@ import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Implements the Remove menu item.
*/
+@AutoService(Command.class)
public class Remove extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ResetSearchCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ResetSearchCommand.java
index d10be439ac..32ae567bf0 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ResetSearchCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ResetSearchCommand.java
@@ -26,9 +26,12 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.Searchable;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
+import com.google.auto.service.AutoService;
+
/**
* Reset Search
*/
+@AutoService(Command.class)
public class ResetSearchCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Restart.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Restart.java
index 2b49b69498..e2fcc1d39f 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Restart.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Restart.java
@@ -39,11 +39,17 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Restart JMeter
* Based on https://dzone.com/articles/programmatically-restart-java
* @since 5.0
*/
+@AutoService({
+ Command.class,
+ MenuCreator.class
+})
public class Restart extends AbstractActionWithNoRunningTest implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(Restart.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/RevertProject.java b/src/core/src/main/java/org/apache/jmeter/gui/action/RevertProject.java
index 23f5e5bc30..f05ebba94b 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/RevertProject.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/RevertProject.java
@@ -27,10 +27,13 @@ import javax.swing.JOptionPane;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Handles the Revert Project command.
*
*/
+@AutoService(Command.class)
public class RevertProject extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/SSLManagerCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/SSLManagerCommand.java
index e3a1e47d5a..9b0ec71ec3 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/SSLManagerCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/SSLManagerCommand.java
@@ -32,6 +32,8 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.SSLManager;
+import com.google.auto.service.AutoService;
+
//
/**
* SSL Manager Command. The SSL Manager provides a mechanism to change your
@@ -52,6 +54,7 @@ import org.apache.jmeter.util.SSLManager;
* already defined via the property.
*
*/
+@AutoService(Command.class)
public class SSLManagerCommand extends AbstractAction {
private static final Set<String> commandSet;
static {
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Save.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Save.java
index 9b6c6f4236..32e3846cac 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Save.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Save.java
@@ -56,12 +56,15 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Save the current test plan; implements:
* Save
* Save TestPlan As
* Save (Selection) As
*/
+@AutoService(Command.class)
public class Save extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Save.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/SaveBeforeRun.java b/src/core/src/main/java/org/apache/jmeter/gui/action/SaveBeforeRun.java
index ac4e1929f0..39191e5296 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/SaveBeforeRun.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/SaveBeforeRun.java
@@ -23,11 +23,14 @@ import java.util.Set;
import org.apache.jmeter.gui.GuiPackage;
+import com.google.auto.service.AutoService;
+
/**
* Save Before Run Action To save test plan before GUI execution
*
* @since 4.0
*/
+@AutoService(Command.class)
public class SaveBeforeRun extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/SaveGraphics.java b/src/core/src/main/java/org/apache/jmeter/gui/action/SaveGraphics.java
index 481809c44e..c73cabefc3 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/SaveGraphics.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/SaveGraphics.java
@@ -34,6 +34,8 @@ import org.apache.jmeter.save.SaveGraphicsService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.Printable;
+import com.google.auto.service.AutoService;
+
/**
* SaveGraphics action is meant to be a generic reusable Action. The class will
* use GUIPackage to get the current gui. Once it does, it checks to see if the
@@ -42,6 +44,7 @@ import org.apache.jmeter.visualizers.Printable;
* file if no extension is provided. If either .png or .tif is in the filename,
* it will call SaveGraphicsService to save in the format.
*/
+@AutoService(Command.class)
public class SaveGraphics extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/SchematicView.java b/src/core/src/main/java/org/apache/jmeter/gui/action/SchematicView.java
index 33ec59f944..8b0f1c9b64 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/SchematicView.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/SchematicView.java
@@ -47,10 +47,16 @@ import org.apache.jorphan.collections.HashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Schematic view of Test Plan
* @since 5.1
*/
+@AutoService({
+ Command.class,
+ MenuCreator.class
+})
public class SchematicView extends AbstractAction implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(SchematicView.class);
private static final String DEFAULT_XSL_FILE =
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/SearchTreeCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/SearchTreeCommand.java
index 91f0c66452..0661c17a63 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/SearchTreeCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/SearchTreeCommand.java
@@ -23,10 +23,13 @@ import java.util.Set;
import javax.swing.JFrame;
+import com.google.auto.service.AutoService;
+
/**
* Search nodes for a text
* TODO Enhance search dialog to select kind of nodes ....
*/
+@AutoService(Command.class)
public class SearchTreeCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/Start.java b/src/core/src/main/java/org/apache/jmeter/gui/action/Start.java
index fee1a4baee..b31d1f3d73 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/Start.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/Start.java
@@ -45,6 +45,8 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Set of Actions to:
* <ul>
@@ -57,6 +59,7 @@ import org.slf4j.LoggerFactory;
* <li>Validate a set of Thread Groups with/without sleeping on the timers depending on jmeter properties</li>
* </ul>
*/
+@AutoService(Command.class)
public class Start extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Start.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/StopStoppables.java b/src/core/src/main/java/org/apache/jmeter/gui/action/StopStoppables.java
index c92b6a0916..f4f5ff60a6 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/StopStoppables.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/StopStoppables.java
@@ -26,10 +26,13 @@ import java.util.Set;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.Stoppable;
+import com.google.auto.service.AutoService;
+
/**
* Stops stopables (Proxy, Mirror)
* @since 2.5.1
*/
+@AutoService(Command.class)
public class StopStoppables extends AbstractAction implements ActionListener {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/TemplatesCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/TemplatesCommand.java
index 4896ae4e2a..58b6c58b26 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/TemplatesCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/TemplatesCommand.java
@@ -21,10 +21,13 @@ import java.awt.event.ActionEvent;
import java.util.HashSet;
import java.util.Set;
+import com.google.auto.service.AutoService;
+
/**
* Open Templates
* @since 2.10
*/
+@AutoService(Command.class)
public class TemplatesCommand extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/UndoCommand.java b/src/core/src/main/java/org/apache/jmeter/gui/action/UndoCommand.java
index a4a56f6637..fdae2d28ce 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/UndoCommand.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/UndoCommand.java
@@ -26,10 +26,13 @@ import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jorphan.collections.HashTree;
+import com.google.auto.service.AutoService;
+
/**
* Menu command to serve Undo/Redo
* @since 2.12
*/
+@AutoService(Command.class)
public class UndoCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/What.java b/src/core/src/main/java/org/apache/jmeter/gui/action/What.java
index 1d202e30c2..08384fc606 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/What.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/What.java
@@ -35,6 +35,8 @@ import org.apache.logging.log4j.core.config.Configurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
*
* Debug class to show details of the currently selected object
@@ -43,6 +45,7 @@ import org.slf4j.LoggerFactory;
* Also enables/disables debug for the test element.
*
*/
+@AutoService(Command.class)
public class What extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(What.class);
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/ZoomInOut.java b/src/core/src/main/java/org/apache/jmeter/gui/action/ZoomInOut.java
index a6b496fe72..7209bfe740 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/ZoomInOut.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/ZoomInOut.java
@@ -24,10 +24,13 @@ import java.util.Set;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.JMeterUIDefaults;
+import com.google.auto.service.AutoService;
+
/**
* Zoom IN/OUT
* @since 3.2
*/
+@AutoService(Command.class)
public class ZoomInOut extends AbstractAction {
private static final Set<String> commands = new HashSet<>();
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/plugin/MenuCreator.java b/src/core/src/main/java/org/apache/jmeter/gui/plugin/MenuCreator.java
index c4b902df6c..3d03fed1e1 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/plugin/MenuCreator.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/plugin/MenuCreator.java
@@ -21,9 +21,12 @@ import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.MenuElement;
+import org.apache.jorphan.reflect.JMeterService;
+
/**
* @since 2.10
*/
+@JMeterService
public interface MenuCreator {
enum MENU_LOCATION {
FILE,
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java b/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java
index 5072aa31ad..cef9cd2690 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/util/JMeterMenuBar.java
@@ -19,14 +19,13 @@ package org.apache.jmeter.gui.util;
import java.awt.Component;
import java.awt.event.KeyEvent;
-import java.io.IOException;
-import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.ServiceLoader;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
@@ -54,7 +53,7 @@ import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.LocaleChangeEvent;
import org.apache.jmeter.util.LocaleChangeListener;
import org.apache.jmeter.util.SSLManager;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.logging.log4j.Level;
import org.slf4j.Logger;
@@ -92,7 +91,13 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
private JMenu remoteExit;
private final Collection<JMenuItem> remoteEngineExit;
private JMenu searchMenu;
- private List<MenuCreator> menuCreators;
+ private final Collection<MenuCreator> menuCreators =
+ JMeterUtils.loadServicesAndScanJars(
+ MenuCreator.class,
+ ServiceLoader.load(MenuCreator.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ );
public static final String SYSTEM_LAF = "System"; // $NON-NLS-1$
public static final String CROSS_PLATFORM_LAF = "CrossPlatform"; // $NON-NLS-1$
@@ -192,9 +197,6 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
* should be defined in a file somewhere, but that is for later.
*/
public void createMenuBar() {
-
- this.menuCreators = findMenuCreators();
-
makeFileMenu();
makeEditMenu();
makeRunMenu();
@@ -217,35 +219,6 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
this.add(helpMenu);
}
- private static List<MenuCreator> findMenuCreators() {
- List<MenuCreator> creators = new ArrayList<>();
- try {
- List<String> listClasses = ClassFinder.findClassesThatExtend(
- JMeterUtils.getSearchPaths(),
- new Class[] {MenuCreator.class });
- for (String strClassName : listClasses) {
- try {
- log.debug("Loading menu creator class: {}", strClassName);
- Class<?> commandClass = Class.forName(strClassName);
- if (!Modifier.isAbstract(commandClass.getModifiers())) {
- log.debug("Instantiating: {}", commandClass);
- MenuCreator creator = (MenuCreator) commandClass.getDeclaredConstructor().newInstance();
- creators.add(creator);
- }
- } catch (NoClassDefFoundError e) {
- log.error("Exception registering implementation: [{}] of interface: [{}], a dependency used by the plugin class is missing",
- strClassName, MenuCreator.class, e);
- } catch (Exception e) {
- log.error("Exception registering implementation: [{}] of interface: [{}], a jar is probably missing",
- strClassName, MenuCreator.class, e);
- }
- }
- } catch (IOException e) {
- log.error("Exception finding implementations of {}", MenuCreator.class, e);
- }
- return creators;
- }
-
private void makeHelpMenu() {
helpMenu = makeMenuRes("help",'H'); //$NON-NLS-1$
@@ -590,7 +563,7 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
* @param menuCreators
* @param location
*/
- private static void addPluginsMenuItems(JMenu menu, List<MenuCreator> menuCreators, MENU_LOCATION location) {
+ private static void addPluginsMenuItems(JMenu menu, Collection<MenuCreator> menuCreators, MENU_LOCATION location) {
for (MenuCreator menuCreator : menuCreators) {
JMenuItem[] menuItems = menuCreator.getMenuItemsAtLocation(location);
if (menuItems.length != 0) {
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java b/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java
index 4aafa80341..bc84feb916 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java
@@ -124,6 +124,8 @@ public final class MenuFactory {
private static void initializeMenus(
Map<String, List<MenuInfo>> menus, Set<String> elementsToSkip) {
try {
+ // TODO: migrate to ServiceLoader or something else
+ @SuppressWarnings("deprecation")
List<String> guiClasses = ClassFinder
.findClassesThatExtend(
JMeterUtils.getSearchPaths(),
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java b/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
index f2c8d99012..83dd5b71ba 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
@@ -17,10 +17,13 @@
package org.apache.jmeter.threads;
+import org.apache.jorphan.reflect.JMeterService;
+
/**
* Interface notified when number of active threads changes
* @since 2.10
*/
+@JMeterService
public interface RemoteThreadsLifeCycleListener {
/**
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java b/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java
index adb0f593a4..058702827a 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java
@@ -17,18 +17,16 @@
package org.apache.jmeter.threads;
-import java.io.IOException;
-import java.lang.reflect.Modifier;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.ServiceLoader;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.util.JMeterUtils;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +37,14 @@ import org.slf4j.LoggerFactory;
public class RemoteThreadsListenerImpl extends UnicastRemoteObject implements
RemoteThreadsListener, ThreadListener {
private static final Logger log = LoggerFactory.getLogger(RemoteThreadsListenerImpl.class);
- private final List<RemoteThreadsLifeCycleListener> listeners = new ArrayList<>();
+
+ private final Collection<RemoteThreadsLifeCycleListener> listeners =
+ JMeterUtils.loadServicesAndScanJars(
+ RemoteThreadsLifeCycleListener.class,
+ ServiceLoader.load(RemoteThreadsLifeCycleListener.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ );
/**
*
@@ -56,27 +61,6 @@ public class RemoteThreadsListenerImpl extends UnicastRemoteObject implements
*/
public RemoteThreadsListenerImpl() throws RemoteException {
super(DEFAULT_LOCAL_PORT, RmiUtils.createClientSocketFactory(), RmiUtils.createServerSocketFactory());
- try {
- List<String> listClasses = ClassFinder.findClassesThatExtend(
- JMeterUtils.getSearchPaths(),
- new Class[] {RemoteThreadsLifeCycleListener.class });
- for (String strClassName : listClasses) {
- try {
- log.debug("Loading class: {}", strClassName);
- Class<?> commandClass = Class.forName(strClassName);
- if (!Modifier.isAbstract(commandClass.getModifiers())) {
- log.debug("Instantiating: {}", commandClass);
- RemoteThreadsLifeCycleListener listener = (RemoteThreadsLifeCycleListener) commandClass.getDeclaredConstructor().newInstance();
- listeners.add(listener);
- }
- } catch (Exception e) {
- log.error("Exception registering {} with implementation: {}", RemoteThreadsLifeCycleListener.class,
- strClassName, e);
- }
- }
- } catch (IOException e) {
- log.error("Exception finding implementations of {}", RemoteThreadsLifeCycleListener.class, e);
- }
}
private static int addOffset(int port, int offset) {
diff --git a/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java b/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java
index 3a8408bb2a..de46f1b684 100644
--- a/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java
+++ b/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java
@@ -27,19 +27,25 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
+import java.util.ServiceLoader;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
@@ -59,6 +65,7 @@ import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jorphan.gui.JFactory;
import org.apache.jorphan.gui.JMeterUIDefaults;
import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.ServiceLoadExceptionHandler;
import org.apache.jorphan.test.UnitTestManager;
import org.apache.jorphan.util.JMeterError;
import org.apache.jorphan.util.JOrphanUtils;
@@ -316,6 +323,71 @@ public class JMeterUtils implements UnitTestManager {
getProperties(file);
}
+ /**
+ * Loads services implementing a given interface and scans JMeter search path for the implementations.
+ * This is a transition replacement for {@link ClassFinder}, and JMeter would migrate to {@link ServiceLoader}-only
+ * lookup in the future.
+ * <p>Note: it is not always safe to cache the result as {@code search_paths} property might change over time</p>
+ *
+ * @param service interface that services should extend.
+ * @param serviceLoader ServiceLoader to fetch services.
+ * @param classLoader classLoader to use when searching for classes on the search path.
+ * @param exceptionHandler exception handler to use for services that fail to load.
+ * @return collection of services that load successfully
+ * @param <S> type of service (class or interface)
+ */
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ public static <S> Collection<S> loadServicesAndScanJars(
+ @SuppressWarnings("BoundedWildcard") Class<S> service,
+ ServiceLoader<S> serviceLoader,
+ ClassLoader classLoader,
+ ServiceLoadExceptionHandler<? super S> exceptionHandler
+ ) {
+ Collection<S> services = ClassFinder.loadServices(service, serviceLoader, exceptionHandler);
+
+ List<String> classesFromJars;
+ try (ClassFinder.Closeable ignored = ClassFinder.skipJarsWithJmeterSkipClassScanningAttribute()) {
+ classesFromJars = findClassesThatExtend(service);
+ } catch (IOException e) {
+ log.warn("Unable to lookup {} with ClassFinder.findClassesThatExtend. " +
+ "Will use only results from ServiceLoader ({} items found)", service, services.size(), e);
+ return services;
+ }
+
+ if (classesFromJars.isEmpty()) {
+ return services;
+ }
+
+ Set<String> loadedClasses = new HashSet<>((int) (services.size() / 0.75f) + 1);
+ for (S s : services) {
+ loadedClasses.add(s.getClass().getName());
+ }
+
+ List<S> result = new ArrayList<>(services.size() + classesFromJars.size());
+ result.addAll(services);
+ for (String className : classesFromJars) {
+ // Ignore classes that we loaded previously (e.g. with a ServiceLoader)
+ if (!loadedClasses.add(className)) {
+ continue;
+ }
+ try {
+ Class<? extends S> klass = Class.forName(className, false, classLoader)
+ .asSubclass(service);
+ if (!Modifier.isAbstract(klass.getModifiers())) {
+ continue;
+ }
+ result.add(klass.getDeclaredConstructor().newInstance());
+ } catch (Throwable e) {
+ if (e instanceof InvocationTargetException) {
+ //noinspection AssignmentToCatchBlockParameter
+ e = e.getCause();
+ }
+ exceptionHandler.handle(service, className, e);
+ }
+ }
+ return result;
+ }
+
/**
* Convenience method for
* {@link ClassFinder#findClassesThatExtend(String[], Class[], boolean)}
@@ -325,7 +397,10 @@ public class JMeterUtils implements UnitTestManager {
* @param superClass - single class to search for
* @return List of Strings containing discovered class names.
* @throws IOException when the used {@link ClassFinder} throws one while searching for the class
+ * @deprecated use {@link #loadServicesAndScanJars(Class, ServiceLoader, ClassLoader, ServiceLoadExceptionHandler)} instead
*/
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ @Deprecated
public static List<String> findClassesThatExtend(Class<?> superClass)
throws IOException {
return ClassFinder.findClassesThatExtend(getSearchPaths(), new Class[]{superClass}, false);
diff --git a/src/core/src/test/java/org/apache/jorphan/test/AllTests.java b/src/core/src/test/java/org/apache/jorphan/test/AllTests.java
index bbbeefa46f..e64fb568de 100644
--- a/src/core/src/test/java/org/apache/jorphan/test/AllTests.java
+++ b/src/core/src/test/java/org/apache/jorphan/test/AllTests.java
@@ -321,8 +321,10 @@ public final class AllTests {
}
}
+ @SuppressWarnings("deprecation")
private static List<String> findJMeterJUnitTests(String searchPathString) throws IOException {
final String[] searchPaths = JOrphanUtils.split(searchPathString, ",");
+ // TODO: do we really need class searching here?
return ClassFinder.findClasses(searchPaths, new JunitTestFilter());
}
diff --git a/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java b/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java
index 7054ff33b5..19152c90ab 100644
--- a/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java
+++ b/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java
@@ -440,6 +440,7 @@ public class JMeterTest extends JMeterTestCaseJUnit implements Describable {
public static Collection<Object> getObjects(Class<?> extendsClass) throws Throwable {
String exName = extendsClass.getName();
+ @SuppressWarnings("deprecation")
Iterator<String> classes = ClassFinder
.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { extendsClass }).iterator();
List<Object> objects = new ArrayList<>();
diff --git a/src/dist-check/src/test/java/org/apache/jmeter/testbeans/gui/PackageTest.java b/src/dist-check/src/test/java/org/apache/jmeter/testbeans/gui/PackageTest.java
index 03e9704f31..f076fb3283 100644
--- a/src/dist-check/src/test/java/org/apache/jmeter/testbeans/gui/PackageTest.java
+++ b/src/dist-check/src/test/java/org/apache/jmeter/testbeans/gui/PackageTest.java
@@ -171,6 +171,7 @@ public final class PackageTest extends JMeterTestCaseJUnit implements Describabl
public static Test suite() throws Exception {
TestSuite suite = new TestSuite("Bean Resource Test Suite");
+ @SuppressWarnings("deprecation")
List<String> testBeanClassNames = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { TestBean.class });
boolean errorDetected = false;
diff --git a/src/dist-check/src/test/java/org/apache/jorphan/reflect/TestClassFinder.java b/src/dist-check/src/test/java/org/apache/jorphan/reflect/TestClassFinder.java
index a8a355670f..a035d32142 100644
--- a/src/dist-check/src/test/java/org/apache/jorphan/reflect/TestClassFinder.java
+++ b/src/dist-check/src/test/java/org/apache/jorphan/reflect/TestClassFinder.java
@@ -48,6 +48,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArray() throws IOException {
+ @SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Exception.class });
@@ -56,6 +57,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayTrue() throws Exception {
+ @SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Object.class },
@@ -66,6 +68,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayFalse() throws Exception {
+ @SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Exception.class },
@@ -77,6 +80,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayBooleanStringString() throws Exception {
+ @SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Exception.class },
@@ -90,6 +94,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayBooleanStringStringTrue() throws Exception {
+ @SuppressWarnings("deprecation")
List<String> annotatedClasses = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { java.beans.Transient.class },
@@ -102,7 +107,7 @@ public class TestClassFinder {
@Test
public void testFindAnnotatedClasses() throws Exception {
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"deprecation", "unchecked"})
List<String> annotatedClasses = ClassFinder.findAnnotatedClasses(
libDirs,
new Class[] { java.beans.Transient.class});
@@ -111,7 +116,7 @@ public class TestClassFinder {
@Test
public void testFindAnnotatedInnerClasses() throws Exception {
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"deprecation", "unchecked"})
List<String> annotatedClasses = ClassFinder.findAnnotatedClasses(libDirs,
new Class[] { java.lang.Deprecated.class}, true);
Assert.assertTrue(annotatedClasses.stream().anyMatch(s->s.contains("$")));
@@ -119,12 +124,16 @@ public class TestClassFinder {
@Test
public void testFindClasses() throws IOException {
- Assert.assertFalse(ClassFinder.findClasses(libDirs, className -> true).isEmpty());
+ @SuppressWarnings("deprecation")
+ List<String> classes = ClassFinder.findClasses(libDirs, className -> true);
+ Assert.assertFalse(classes.isEmpty());
}
@Test
public void testFindClassesNone() throws IOException {
- Assert.assertTrue(ClassFinder.findClasses(libDirs, className -> false).isEmpty());
+ @SuppressWarnings("deprecation")
+ List<String> classes = ClassFinder.findClasses(libDirs, className -> false);
+ Assert.assertTrue(classes.isEmpty());
}
}
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/BeanShell.java b/src/functions/src/main/java/org/apache/jmeter/functions/BeanShell.java
index 88d64cd8e6..f72284c877 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/BeanShell.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/BeanShell.java
@@ -32,10 +32,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* A function which understands BeanShell
* @since 1.X
*/
+@AutoService(Function.class)
public class BeanShell extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(BeanShell.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/CSVRead.java b/src/functions/src/main/java/org/apache/jmeter/functions/CSVRead.java
index c9a80a34a5..612a83ec1c 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/CSVRead.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/CSVRead.java
@@ -28,6 +28,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* The function represented by this class allows data to be read from CSV files.
* Syntax is similar to StringFromFile function. The function allows the test to
@@ -53,6 +55,7 @@ import org.slf4j.LoggerFactory;
* {@code __CSVRead(*ONE,1);}, etc.
* @since 1.9
*/
+@AutoService(Function.class)
public class CSVRead extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(CSVRead.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/ChangeCase.java b/src/functions/src/main/java/org/apache/jmeter/functions/ChangeCase.java
index deb5c88a05..38e15271a2 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/ChangeCase.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/ChangeCase.java
@@ -31,6 +31,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Change Case Function
*
@@ -45,6 +47,7 @@ import org.slf4j.LoggerFactory;
* @since 4.0
*
*/
+@AutoService(Function.class)
public class ChangeCase extends AbstractFunction {
private static final Logger LOGGER = LoggerFactory.getLogger(ChangeCase.class);
private static final List<String> DESC = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/CharFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/CharFunction.java
index 94e5df40c1..482c7ed6e6 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/CharFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/CharFunction.java
@@ -28,10 +28,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Function to generate chars from a list of decimal or hex values
* @since 2.3.3
*/
+@AutoService(Function.class)
public class CharFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(CharFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/DateTimeConvertFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/DateTimeConvertFunction.java
index c382ba1747..47e9a0d612 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/DateTimeConvertFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/DateTimeConvertFunction.java
@@ -31,6 +31,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* DateConvert function to change date format
* Can optionally store it in a variable.
@@ -38,6 +40,7 @@ import org.slf4j.LoggerFactory;
* @since 4.0
*
*/
+@AutoService(Function.class)
public class DateTimeConvertFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(DateTimeConvertFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/DigestEncodeFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/DigestEncodeFunction.java
index 3bc2d990f5..36c9f479e8 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/DigestEncodeFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/DigestEncodeFunction.java
@@ -34,6 +34,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Digest Encode Function that provides computing of different SHA-XXX, can
* uppercase the result and store it in a variable.
@@ -44,6 +46,7 @@ import org.slf4j.LoggerFactory;
*
* @since 4.0
*/
+@AutoService(Function.class)
public class DigestEncodeFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(DigestEncodeFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeHtml.java b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeHtml.java
index 4b3d4186c8..8606779ad0 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeHtml.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeHtml.java
@@ -27,6 +27,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* <p>Function which escapes the characters in a <code>String</code> using HTML entities.</p>
*
@@ -46,6 +48,7 @@ import org.apache.jmeter.util.JMeterUtils;
* @see StringEscapeUtils#escapeHtml4(String) (Commons Lang)
* @since 2.3.3
*/
+@AutoService(Function.class)
public class EscapeHtml extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java
index 91c8d99641..a3fbf62aa6 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeOroRegexpChars.java
@@ -30,10 +30,13 @@ import org.apache.oro.text.regex.Perl5Compiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Escape ORO meta characters
* @since 2.9
*/
+@AutoService(Function.class)
public class EscapeOroRegexpChars extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(EscapeOroRegexpChars.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeXml.java b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeXml.java
index 4dce679a76..ac83b05b77 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/EscapeXml.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/EscapeXml.java
@@ -27,6 +27,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* <p>Function which escapes the characters in a <code>String</code> using XML 1.0 entities.</p>
*
@@ -43,6 +45,7 @@ import org.apache.jmeter.util.JMeterUtils;
* @see StringEscapeUtils#escapeXml10(String) (Commons Lang)
* @since 3.2
*/
+@AutoService(Function.class)
public class EscapeXml extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/EvalFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/EvalFunction.java
index 33c90be05f..e5126c7731 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/EvalFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/EvalFunction.java
@@ -26,6 +26,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to evaluate a string which may contain variable or function references.
*
@@ -34,6 +36,7 @@ import org.apache.jmeter.util.JMeterUtils;
* Returns: the evaluated value
* @since 2.3.1
*/
+@AutoService(Function.class)
public class EvalFunction extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/EvalVarFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/EvalVarFunction.java
index 4d3521e81e..71fc5bf409 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/EvalVarFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/EvalVarFunction.java
@@ -29,6 +29,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Function to evaluate a string which may contain variable or function references.
*
@@ -37,6 +39,7 @@ import org.slf4j.LoggerFactory;
* Returns: the evaluated value
* @since 2.3.1
*/
+@AutoService(Function.class)
public class EvalVarFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(EvalVarFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/FileToString.java b/src/functions/src/main/java/org/apache/jmeter/functions/FileToString.java
index 519fb69919..5f71500b70 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/FileToString.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/FileToString.java
@@ -32,6 +32,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* FileToString Function to read a complete file into a String.
* <p>
@@ -50,6 +52,7 @@ import org.slf4j.LoggerFactory;
* </ul>
* @since 2.4
*/
+@AutoService(Function.class)
public class FileToString extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(FileToString.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/FileWrapper.java b/src/functions/src/main/java/org/apache/jmeter/functions/FileWrapper.java
index a26b482643..30710df41f 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/FileWrapper.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/FileWrapper.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
/**
* This class wraps the FileRowColContainer for use across multiple threads.
* <p>
@@ -34,6 +35,7 @@ import org.slf4j.LoggerFactory;
* together with the current line number.
*
*/
+//@AutoService(Function.class)
public final class FileWrapper {
private static final Logger log = LoggerFactory.getLogger(FileWrapper.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Groovy.java b/src/functions/src/main/java/org/apache/jmeter/functions/Groovy.java
index 5e23c7b5a3..23612d0eb2 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Groovy.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Groovy.java
@@ -41,11 +41,14 @@ import org.apache.jmeter.util.JSR223TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* __groovy function
* Provides a Groovy interpreter
* @since 3.1
*/
+@AutoService(Function.class)
public class Groovy extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(Groovy.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/IntSum.java b/src/functions/src/main/java/org/apache/jmeter/functions/IntSum.java
index 92019be77c..fb2db081de 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/IntSum.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/IntSum.java
@@ -27,12 +27,15 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Provides an intSum function that adds two or more integer values.
*
* @see LongSum
* @since 1.8.1
*/
+@AutoService(Function.class)
public class IntSum extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/IsPropDefined.java b/src/functions/src/main/java/org/apache/jmeter/functions/IsPropDefined.java
index eb86e50151..b50922d6a2 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/IsPropDefined.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/IsPropDefined.java
@@ -26,11 +26,14 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Test if a JMeter property is defined
*
* @since 4.0
*/
+@AutoService(Function.class)
public class IsPropDefined extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
private static final String KEY = "__isPropDefined";
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/IsVarDefined.java b/src/functions/src/main/java/org/apache/jmeter/functions/IsVarDefined.java
index edb8515e6a..99a2e9e1b7 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/IsVarDefined.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/IsVarDefined.java
@@ -27,11 +27,14 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Test if a JMeter variable is defined
*
* @since 4.0
*/
+@AutoService(Function.class)
public class IsVarDefined extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/IterationCounter.java b/src/functions/src/main/java/org/apache/jmeter/functions/IterationCounter.java
index a5c730b905..4742a9c560 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/IterationCounter.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/IterationCounter.java
@@ -29,11 +29,14 @@ import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Counter that can be referenced anywhere in the Thread Group. It can be configured per User (Thread Local)
* or globally.
* @since 1.X
*/
+@AutoService(Function.class)
public class IterationCounter extends AbstractFunction implements ThreadListener {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/JavaScript.java b/src/functions/src/main/java/org/apache/jmeter/functions/JavaScript.java
index 00ea98bc6f..bc4729b6a2 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/JavaScript.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/JavaScript.java
@@ -40,10 +40,13 @@ import org.mozilla.javascript.Scriptable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* javaScript function implementation that executes a piece of JavaScript (not Java!) code and returns its value
* @since 1.9
*/
+@AutoService(Function.class)
public class JavaScript extends AbstractFunction {
private static final String NASHORN_ENGINE_NAME = "nashorn"; //$NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Jexl2Function.java b/src/functions/src/main/java/org/apache/jmeter/functions/Jexl2Function.java
index 7b6c185b27..1d90bca74c 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Jexl2Function.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Jexl2Function.java
@@ -36,11 +36,14 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* A function which understands Commons JEXL2
* @since 2.6
*/
// For unit tests, see TestJexlFunction
+@AutoService(Function.class)
public class Jexl2Function extends AbstractFunction implements ThreadListener {
private static final Logger log = LoggerFactory.getLogger(Jexl2Function.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Jexl3Function.java b/src/functions/src/main/java/org/apache/jmeter/functions/Jexl3Function.java
index ac74281d47..6c37bc92b0 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Jexl3Function.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Jexl3Function.java
@@ -37,11 +37,14 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* A function which understands Commons JEXL3
* @since 3.0
*/
// For unit tests, see TestJexlFunction
+@AutoService(Function.class)
public class Jexl3Function extends AbstractFunction implements ThreadListener {
private static final Logger log = LoggerFactory.getLogger(Jexl3Function.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction.java
index de5b0f9a57..a40d6d668d 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction.java
@@ -30,6 +30,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
+import com.google.auto.service.AutoService;
+
/**
* <p>
* Function to log a message.
@@ -46,6 +48,7 @@ import org.slf4j.event.Level;
* Returns: - the input string
* @since 2.2
*/
+@AutoService(Function.class)
public class LogFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(LogFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction2.java b/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction2.java
index 9f45af1ae4..23901d8450 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction2.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/LogFunction2.java
@@ -28,6 +28,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* <p>
* Function to log a message.
@@ -43,6 +45,7 @@ import org.slf4j.LoggerFactory;
* Returns: - Empty String (so can be used where return value would be a nuisance)
* @since 2.2
*/
+@AutoService(Function.class)
public class LogFunction2 extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(LogFunction2.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/LongSum.java b/src/functions/src/main/java/org/apache/jmeter/functions/LongSum.java
index 37a8e8de51..b29a3551d6 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/LongSum.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/LongSum.java
@@ -27,11 +27,14 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Provides a longSum function that adds two or more long values.
* @see IntSum
* @since 2.3.2
*/
+@AutoService(Function.class)
public class LongSum extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java b/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
index 82acc7183b..e80a944d74 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
@@ -19,10 +19,13 @@ package org.apache.jmeter.functions;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Return Machine IP
* @since 2.6
*/
+@AutoService(Function.class)
public class MachineIP extends AbstractHostIPName {
private static final String KEY = "__machineIP"; //$NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java b/src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java
index 50898adff7..8218b8e41f 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java
@@ -19,10 +19,13 @@ package org.apache.jmeter.functions;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Return Machine Host
* @since 1.X
*/
+@AutoService(Function.class)
public class MachineName extends AbstractHostIPName {
private static final String KEY = "__machineName"; //$NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Property.java b/src/functions/src/main/java/org/apache/jmeter/functions/Property.java
index b2168a307f..db4be23411 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Property.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Property.java
@@ -27,6 +27,8 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to get a JMeter property, and optionally store it
*
@@ -41,6 +43,7 @@ import org.apache.jmeter.util.JMeterUtils;
* - the property name itself
* @since 2.0
*/
+@AutoService(Function.class)
public class Property extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Property2.java b/src/functions/src/main/java/org/apache/jmeter/functions/Property2.java
index 701dd0fd46..0694219ba6 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Property2.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Property2.java
@@ -26,6 +26,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to get a JMeter property, or a default. Does not offer the option to
* store the value, as it is just as easy to refetch it. This is a
@@ -46,6 +48,7 @@ import org.apache.jmeter.util.JMeterUtils;
* not present - "1" (suitable for use in ThreadGroup GUI)
* @since 2.0
*/
+@AutoService(Function.class)
public class Property2 extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Random.java b/src/functions/src/main/java/org/apache/jmeter/functions/Random.java
index 20921a187d..bfffeb7c7a 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Random.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Random.java
@@ -28,11 +28,14 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Provides a Random function which returns a random long integer between a min
* (first argument) and a max (second argument).
* @since 1.9
*/
+@AutoService(Function.class)
public class Random extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/RandomDate.java b/src/functions/src/main/java/org/apache/jmeter/functions/RandomDate.java
index 83243d76f4..d28e815546 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/RandomDate.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/RandomDate.java
@@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.auto.service.AutoService;
/**
* RandomDate Function generates a date in a specific range
@@ -59,6 +60,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
*
* @since 3.3
*/
+@AutoService(Function.class)
public class RandomDate extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(RandomDate.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/RandomFromMultipleVars.java b/src/functions/src/main/java/org/apache/jmeter/functions/RandomFromMultipleVars.java
index 7a22beca1e..897fd48f11 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/RandomFromMultipleVars.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/RandomFromMultipleVars.java
@@ -31,6 +31,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Provides a RandomFromMultipleVars function which returns a random element from a multi valued extracted variable.
* Those kind of variables are extracted by:
@@ -41,6 +43,7 @@ import org.slf4j.LoggerFactory;
*
* @since 3.1
*/
+@AutoService(Function.class)
public class RandomFromMultipleVars extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(RandomFromMultipleVars.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/RandomString.java b/src/functions/src/main/java/org/apache/jmeter/functions/RandomString.java
index 1dd0048392..e0528d1c77 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/RandomString.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/RandomString.java
@@ -31,11 +31,14 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Provides a RandomString function which returns a random String of length (first argument)
* using characters (second argument)
* @since 2.6
*/
+@AutoService(Function.class)
public class RandomString extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(RandomString.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/RegexFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/RegexFunction.java
index a3c50e2b4e..8ea9e82019 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/RegexFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/RegexFunction.java
@@ -39,6 +39,8 @@ import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
+import com.google.auto.service.AutoService;
/**
* Implements regular expression parsing of sample results and variables
* @since 1.X
@@ -46,6 +48,7 @@ import org.slf4j.LoggerFactory;
// @see TestRegexFunction for unit tests
+@AutoService(Function.class)
public class RegexFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(RegexFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/SamplerName.java b/src/functions/src/main/java/org/apache/jmeter/functions/SamplerName.java
index eedb707b96..fa624db65f 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/SamplerName.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/SamplerName.java
@@ -27,10 +27,13 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to return the name of the current sampler.
* @since 2.5
*/
+@AutoService(Function.class)
public class SamplerName extends AbstractFunction {
private static final String KEY = "__samplerName"; //$NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/SetProperty.java b/src/functions/src/main/java/org/apache/jmeter/functions/SetProperty.java
index 7811c5686c..5e8bce06cf 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/SetProperty.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/SetProperty.java
@@ -27,6 +27,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to set a JMeter property
*
@@ -40,6 +42,7 @@ import org.apache.jmeter.util.JMeterUtils;
* Returns: nothing or original value if the 3rd parameter is true
* @since 2.1
*/
+@AutoService(Function.class)
public class SetProperty extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/SplitFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/SplitFunction.java
index 17e8bb1012..825f0d8a62 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/SplitFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/SplitFunction.java
@@ -31,6 +31,8 @@ import org.apache.jorphan.util.JOrphanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
// @see org.apache.jmeter.functions.PackageTest for unit tests
/**
@@ -53,6 +55,7 @@ import org.slf4j.LoggerFactory;
* </ul>
* @since 2.0.2
*/
+@AutoService(Function.class)
public class SplitFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(SplitFunction.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/StringFromFile.java b/src/functions/src/main/java/org/apache/jmeter/functions/StringFromFile.java
index fc2207feee..5e7081e1b0 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/StringFromFile.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/StringFromFile.java
@@ -37,6 +37,8 @@ import org.apache.jorphan.util.JMeterStopThreadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* <p>StringFromFile Function to read a String from a text file.</p>
*
@@ -70,6 +72,7 @@ import org.slf4j.LoggerFactory;
* Because function instances are shared, it does not make sense to use the thread number as part of the file name.
* @since 1.9
*/
+@AutoService(Function.class)
public class StringFromFile extends AbstractFunction implements TestStateListener {
private static final Logger log = LoggerFactory.getLogger(StringFromFile.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/StringToFile.java b/src/functions/src/main/java/org/apache/jmeter/functions/StringToFile.java
index 25d644e8ee..41a363976b 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/StringToFile.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/StringToFile.java
@@ -41,6 +41,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* StringToFile Function to write a String to a file
*
@@ -55,6 +57,7 @@ import org.slf4j.LoggerFactory;
*
* @since 5.2
*/
+@AutoService(Function.class)
public class StringToFile extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(StringToFile.class);
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/TestPlanName.java b/src/functions/src/main/java/org/apache/jmeter/functions/TestPlanName.java
index c0ab9e8a94..ba298545ac 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/TestPlanName.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/TestPlanName.java
@@ -26,10 +26,13 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.services.FileServer;
+import com.google.auto.service.AutoService;
+
/**
* Returns Test Plan name
* @since 2.6
*/
+@AutoService(Function.class)
public class TestPlanName extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/ThreadGroupName.java b/src/functions/src/main/java/org/apache/jmeter/functions/ThreadGroupName.java
index 08a4386708..36c6b38f46 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/ThreadGroupName.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/ThreadGroupName.java
@@ -26,11 +26,14 @@ import org.apache.jmeter.threads.AbstractThreadGroup;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
+import com.google.auto.service.AutoService;
+
/**
* Returns Thread Group Name
*
* @since 5.0
*/
+@AutoService(Function.class)
public class ThreadGroupName extends AbstractFunctionByKey {
private static final String KEY = "__threadGroupName"; //$NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/ThreadNumber.java b/src/functions/src/main/java/org/apache/jmeter/functions/ThreadNumber.java
index 0c353c32e6..12d3221e0b 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/ThreadNumber.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/ThreadNumber.java
@@ -25,10 +25,13 @@ import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
+import com.google.auto.service.AutoService;
+
/**
* Function to return the current thread number.
* @since 1.X
*/
+@AutoService(Function.class)
public class ThreadNumber extends AbstractFunction {
private static final String KEY = "__threadNum"; //$NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java
index 65e6c01a9d..1dd087b7ed 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java
@@ -35,12 +35,15 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
// See org.apache.jmeter.functions.TestTimeFunction for unit tests
/**
* __time() function - returns the current time in milliseconds
* @since 2.2
*/
+@AutoService(Function.class)
public class TimeFunction extends AbstractFunction {
private static final String KEY = "__time"; // $NON-NLS-1$
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/TimeShift.java b/src/functions/src/main/java/org/apache/jmeter/functions/TimeShift.java
index 131d360dae..2b011401b8 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/TimeShift.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/TimeShift.java
@@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.auto.service.AutoService;
/**
* timeShifting Function permit to shift a date
@@ -65,6 +66,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
*
* @since 3.3
*/
+@AutoService(Function.class)
public class TimeShift extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(TimeShift.class);
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/UnEscape.java b/src/functions/src/main/java/org/apache/jmeter/functions/UnEscape.java
index 910c4bb48f..933af1421e 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/UnEscape.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/UnEscape.java
@@ -28,6 +28,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to unescape any Java literals found in the String.
* For example, it will turn a sequence of '\' and 'n' into a newline character,
@@ -36,6 +38,7 @@ import org.apache.jmeter.util.JMeterUtils;
* @see StringEscapeUtils#unescapeJava(String)
* @since 2.3.3
*/
+@AutoService(Function.class)
public class UnEscape extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/UnEscapeHtml.java b/src/functions/src/main/java/org/apache/jmeter/functions/UnEscapeHtml.java
index 97a8bafa9c..85f57bbe73 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/UnEscapeHtml.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/UnEscapeHtml.java
@@ -28,6 +28,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to unescape a string containing entity escapes
* to a string containing the actual Unicode characters corresponding to the escapes.
@@ -42,6 +44,7 @@ import org.apache.jmeter.util.JMeterUtils;
* @see StringEscapeUtils#unescapeHtml4(String)
* @since 2.3.3
*/
+@AutoService(Function.class)
public class UnEscapeHtml extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/UrlDecode.java b/src/functions/src/main/java/org/apache/jmeter/functions/UrlDecode.java
index a31f1aa2a4..9890ec650c 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/UrlDecode.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/UrlDecode.java
@@ -30,11 +30,14 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to decode a application/x-www-form-urlencoded string.
*
* @since 2.10
*/
+@AutoService(Function.class)
public class UrlDecode extends AbstractFunction {
private static final String CHARSET_ENCODING = StandardCharsets.UTF_8.name();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/UrlEncode.java b/src/functions/src/main/java/org/apache/jmeter/functions/UrlEncode.java
index 410ac89a58..e890c4383c 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/UrlEncode.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/UrlEncode.java
@@ -30,11 +30,14 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to encode a string to a application/x-www-form-urlencoded string.
*
* @since 2.10
*/
+@AutoService(Function.class)
public class UrlEncode extends AbstractFunction {
private static final String CHARSET_ENCODING = StandardCharsets.UTF_8.name();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Uuid.java b/src/functions/src/main/java/org/apache/jmeter/functions/Uuid.java
index 16461968cc..b4b2ee370f 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Uuid.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Uuid.java
@@ -26,6 +26,8 @@ import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
+import com.google.auto.service.AutoService;
+
/**
* Function to create a UUID
*
@@ -36,6 +38,7 @@ import org.apache.jmeter.samplers.Sampler;
* - A pseudo random UUID 4
* @since 2.9
*/
+@AutoService(Function.class)
public class Uuid extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/Variable.java b/src/functions/src/main/java/org/apache/jmeter/functions/Variable.java
index 18ee52db41..ce83531819 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/Variable.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/Variable.java
@@ -27,6 +27,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
+import com.google.auto.service.AutoService;
+
/**
* Function to get a JMeter Variable
*
@@ -39,6 +41,7 @@ import org.apache.jmeter.util.JMeterUtils;
* - the default value if set, and if not the variable name itself
* @since 2.3RC3
*/
+@AutoService(Function.class)
public class Variable extends AbstractFunction {
private static final List<String> desc = new ArrayList<>();
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/XPath.java b/src/functions/src/main/java/org/apache/jmeter/functions/XPath.java
index 1200ccc55a..a2fe3adf68 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/XPath.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/XPath.java
@@ -29,6 +29,8 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
// @see org.apache.jmeter.functions.PackageTest for unit tests
/**
@@ -45,6 +47,7 @@ import org.slf4j.LoggerFactory;
* is opened and used for all threads.
* @since 2.0.3
*/
+@AutoService(Function.class)
public class XPath extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(XPath.class);
diff --git a/src/functions/src/test/kotlin/org/apache/jmeter/functions/FunctionServicesTest.kt b/src/functions/src/test/kotlin/org/apache/jmeter/functions/FunctionServicesTest.kt
new file mode 100644
index 0000000000..32ad1d2913
--- /dev/null
+++ b/src/functions/src/test/kotlin/org/apache/jmeter/functions/FunctionServicesTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jmeter.functions
+
+import org.apache.jmeter.util.JMeterUtils
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import java.util.ServiceLoader
+
+class FunctionServicesTest {
+ @Test
+ fun `__counter loads`() {
+ val functions = JMeterUtils.loadServicesAndScanJars(
+ Function::class.java,
+ ServiceLoader.load(Function::class.java),
+ Thread.currentThread().contextClassLoader
+ ) { service, className, throwable ->
+ throw IllegalStateException(
+ "Failed to load $service implementations, implementation: $className",
+ throwable
+ )
+ }.map { it.referenceKey }
+
+ Assertions.assertTrue("__counter" in functions) {
+ "__counter function should be discoverable with ServiceLoader.load(Function), all found functions are $functions"
+ }
+ }
+}
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/reflect/ClassFinder.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ClassFinder.java
index 7d04e1374e..22d08caed9 100644
--- a/src/jorphan/src/main/java/org/apache/jorphan/reflect/ClassFinder.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ClassFinder.java
@@ -26,18 +26,24 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
+import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import org.apiguardian.api.API;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * This class finds classes that extend one of a set of parent classes
+ * This class finds classes that extend one of a set of parent classes.
*/
public final class ClassFinder {
private static final Logger log = LoggerFactory.getLogger(ClassFinder.class);
@@ -46,10 +52,99 @@ public final class ClassFinder {
private static final String DOT_CLASS = ".class"; // $NON-NLS-1$
private static final int DOT_CLASS_LEN = DOT_CLASS.length();
+ private static final ThreadLocal<Boolean> SKIP_JARS_WITH_JMETER_SKIP_ATTRIBUTE = new ThreadLocal<>();
+
+ public static final String JMETER_SKIP_CLASS_SCANNING_ATTRIBUTE = "JMeter-Skip-Class-Scanning";
+
+ @API(status = API.Status.EXPERIMENTAL, since = "5.6")
+ public interface Closeable extends AutoCloseable {
+ @Override
+ void close();
+ }
+
// static only
private ClassFinder() {
}
+ @API(status = API.Status.EXPERIMENTAL, since = "5.6")
+ public static boolean getSkipJarsWithJmeterSkipClassScanningAttribute() {
+ return Objects.equals(SKIP_JARS_WITH_JMETER_SKIP_ATTRIBUTE.get(), Boolean.TRUE);
+ }
+
+ /**
+ * Configures if {@link ClassFinder} should skip jar files that have {@code JMeter-Skip-Class-Scanning: true}
+ * manifest attribute.
+ * JMeter will skip such jars when it uses both {@link java.util.ServiceLoader} and {@link ClassFinder}.
+ * However, {@link ClassFinder} was public, so it was possible that custom plugins could use it, and they should
+ * be able to find the implementations even if they are in jars with {@code JMeter-Skip-Class-Scanning: true}.
+ * <p>
+ * Sample usage:
+ * <pre>
+ * List<String> classNames;
+ * try (ClassFinder.Closeable ignored = ClassFinder.skipJarsWithJmeterSkipClassScanningAttribute()) {
+ * // findClassesThatExtend will not skip jars with JMeter-Skip-Class-Scanning: true manifest attribute
+ * classNames = ClassFinder.findClassesThatExtend(...);
+ * </pre>
+ *
+ * @return closeable that will reset "skip jar files with manifest entry" flag when closed. Use it in try-with-resources
+ */
+ @API(status = API.Status.INTERNAL, since = "5.6")
+ public static Closeable skipJarsWithJmeterSkipClassScanningAttribute() {
+ SKIP_JARS_WITH_JMETER_SKIP_ATTRIBUTE.set(true);
+ return SKIP_JARS_WITH_JMETER_SKIP_ATTRIBUTE::remove;
+ }
+
+ /**
+ * Loads services implementing a given interface.
+ * This is an intended replacement for {@code findClassesThatExtend}.
+ *
+ * @param service interface that services should extend.
+ * @param serviceLoader ServiceLoader to fetch services.
+ * @param exceptionHandler exception handler to use for services that fail to load.
+ * @return collection of services that load successfully
+ * @param <S> type of service (class or interface)
+ */
+ public static <S> Collection<S> loadServices(
+ @SuppressWarnings("BoundedWildcard") Class<S> service,
+ ServiceLoader<S> serviceLoader,
+ ServiceLoadExceptionHandler<? super S> exceptionHandler
+ ) {
+ List<S> result = new ArrayList<>();
+ @SuppressWarnings("ForEachIterable")
+ Iterator<S> it = serviceLoader.iterator();
+ while (it.hasNext()) {
+ try {
+ // This can't be for-each loop because we need to catch exceptions from next()
+ result.add(it.next());
+ } catch (ServiceConfigurationError e) {
+ // Java does not expose class name of the problematic class in question, so we extract it
+ // from the message
+ String message = e.getMessage();
+ String className = "";
+ if (message.startsWith(service.getName())) {
+ if (message.endsWith(" Unable to get public no-arg constructor")) {
+ className = message.substring(
+ service.getName().length() + ": ".length(),
+ message.length() - " Unable to get public no-arg constructor".length()
+ );
+ } else if (message.endsWith(" not a subtype")) {
+ className = message.substring(
+ service.getName().length() + ": ".length(),
+ message.length() - " not a subtype".length()
+ );
+ } else if (message.endsWith(" could not be instantiated")) {
+ className = message.substring(
+ service.getName().length() + ": ".length() + "Provider ".length(),
+ message.length() - " could not be instantiated".length()
+ );
+ }
+ }
+ exceptionHandler.handle(service, className, e);
+ }
+ }
+ return Collections.unmodifiableCollection(result);
+ }
+
/**
* Filter updates by only storing classes
* that extend one of the parent classes
@@ -176,7 +271,9 @@ public final class ClassFinder {
* @param superClasses required parent class(es)
* @return List of Strings containing discovered class names.
* @throws IOException when scanning the classes fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @Deprecated
public static List<String> findClassesThatExtend(String[] paths, Class<?>[] superClasses)
throws IOException {
return findClassesThatExtend(paths, superClasses, false);
@@ -206,7 +303,9 @@ public final class ClassFinder {
* @param innerClasses should we include inner classes?
* @return List containing discovered classes
* @throws IOException when scanning for classes fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @Deprecated
public static List<String> findClassesThatExtend(String[] strPathsOrJars,
final Class<?>[] superClasses, final boolean innerClasses)
throws IOException {
@@ -223,7 +322,10 @@ public final class ClassFinder {
* @param notContains classname should not contain this string
* @return List containing discovered classes
* @throws IOException when scanning classes fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ @Deprecated
public static List<String> findClassesThatExtend(String[] strPathsOrJars,
final Class<?>[] superClasses, final boolean innerClasses,
String contains, String notContains)
@@ -239,7 +341,10 @@ public final class ClassFinder {
* @param innerClasses should we include inner classes?
* @return List containing discovered classes
* @throws IOException when scanning classes fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ @Deprecated
public static List<String> findAnnotatedClasses(String[] strPathsOrJars,
final Class<? extends Annotation>[] annotations, final boolean innerClasses)
throws IOException {
@@ -254,7 +359,10 @@ public final class ClassFinder {
* @param annotations required annotations
* @return List containing discovered classes
* @throws IOException when scanning classes fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ @Deprecated
public static List<String> findAnnotatedClasses(String[] strPathsOrJars,
final Class<? extends Annotation>[] annotations)
throws IOException {
@@ -272,7 +380,10 @@ public final class ClassFinder {
* @param annotations true if classnames are annotations
* @return List containing discovered classes
* @throws IOException when scanning classes fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ @Deprecated
public static List<String> findClassesThatExtend(String[] searchPathsOrJars,
final Class<?>[] classNames, final boolean innerClasses,
String contains, String notContains, boolean annotations)
@@ -307,7 +418,10 @@ public final class ClassFinder {
* conform to
* @return list of all classes in the jars, that conform to {@code filter}
* @throws IOException when reading the jar files fails
+ * @deprecated use {@link #loadServices(Class, ServiceLoader, ServiceLoadExceptionHandler)} or {@code JMeterUtils#loadServicesAndScanJars}
*/
+ @API(status = API.Status.DEPRECATED, since = "5.6")
+ @Deprecated
public static List<String> findClasses(String[] searchPathsOrJars, ClassFilter filter) throws IOException {
if (log.isDebugEnabled()) {
log.debug("findClasses with searchPathsOrJars : {} and classFilter : {}",
@@ -352,10 +466,22 @@ public final class ClassFinder {
}
- private static void findClassesInOnePath(File file, Set<String> listClasses, ClassFilter filter) throws IOException {
+ private static void findClassesInOnePath(File file, Set<String> listClasses, ClassFilter filter) {
if (file.isDirectory()) {
findClassesInPathsDir(file.getAbsolutePath(), file, listClasses, filter);
} else if (file.exists()) {
+ if (getSkipJarsWithJmeterSkipClassScanningAttribute() && file.getName().endsWith(DOT_JAR)) {
+ // Ignore jars with JMeter-Skip-Class-Scanning attribute
+ try (JarFile jar = new JarFile(file)) {
+ String value = jar.getManifest().getMainAttributes().getValue(JMETER_SKIP_CLASS_SCANNING_ATTRIBUTE);
+ if (Boolean.parseBoolean(value)) {
+ log.info("Jar {} is skipped for scanning since it has {}={} attribute", file, JMETER_SKIP_CLASS_SCANNING_ATTRIBUTE, value);
+ return;
+ }
+ } catch (IOException e) {
+ log.warn("Can not open the jar {}, message: {}", file.getAbsolutePath(), e.getLocalizedMessage(), e);
+ }
+ }
try (ZipFile zipFile = new ZipFile(file);
Stream<? extends ZipEntry> entries = zipFile.stream()) {
entries.filter(entry -> entry.getName().endsWith(DOT_CLASS))
@@ -371,7 +497,7 @@ public final class ClassFinder {
}
- private static void findClassesInPathsDir(String strPathElement, File dir, Set<String> listClasses, ClassFilter filter) throws IOException {
+ private static void findClassesInPathsDir(String strPathElement, File dir, Set<String> listClasses, ClassFilter filter) {
File[] list = dir.listFiles();
if (list == null) {
log.warn("{} is not a folder", dir.getAbsolutePath());
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/CollectServiceLoadExceptionHandler.java
similarity index 54%
copy from src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java
copy to src/jorphan/src/main/java/org/apache/jorphan/reflect/CollectServiceLoadExceptionHandler.java
index 50898adff7..41f80d4df8 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/MachineName.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/CollectServiceLoadExceptionHandler.java
@@ -15,29 +15,26 @@
* limitations under the License.
*/
-package org.apache.jmeter.functions;
+package org.apache.jorphan.reflect;
-import org.apache.jmeter.util.JMeterUtils;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
/**
- * Return Machine Host
- * @since 1.X
+ * Collects all the failures in a collection.
+ * @param <S> the service type
*/
-public class MachineName extends AbstractHostIPName {
-
- private static final String KEY = "__machineName"; //$NON-NLS-1$
-
- public MachineName() {
- }
+public class CollectServiceLoadExceptionHandler<S> implements ServiceLoadExceptionHandler<S> {
+ private final List<ServiceLoadFailure<S>> failures = new ArrayList<>();
@Override
- protected String compute() {
- return JMeterUtils.getLocalHostName();
+ public void handle(Class<? extends S> service, String className, Throwable throwable) {
+ failures.add(new ServiceLoadFailure<>(service, className, throwable));
}
- /** {@inheritDoc} */
- @Override
- public String getReferenceKey() {
- return KEY;
+ public Collection<ServiceLoadFailure<S>> toCollection() {
+ return Collections.unmodifiableCollection(failures);
}
}
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/src/jorphan/src/main/java/org/apache/jorphan/reflect/IgnoreServiceLoadExceptionHandler.java
similarity index 74%
copy from build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
copy to src/jorphan/src/main/java/org/apache/jorphan/reflect/IgnoreServiceLoadExceptionHandler.java
index 534f894dfc..3ee2cf88b4 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/IgnoreServiceLoadExceptionHandler.java
@@ -15,7 +15,13 @@
* limitations under the License.
*/
-plugins {
- id("build-logic.java")
- id("java-library")
+package org.apache.jorphan.reflect;
+
+/**
+ * Ignores all failures.
+ */
+public class IgnoreServiceLoadExceptionHandler implements ServiceLoadExceptionHandler<Object> {
+ @Override
+ public void handle(Class<?> service, String className, Throwable throwable) {
+ }
}
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/reflect/JMeterService.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/JMeterService.java
new file mode 100644
index 0000000000..c641256f03
--- /dev/null
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/JMeterService.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jorphan.reflect;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apiguardian.api.API;
+
+/**
+ * This is a marker annotation that describes JMeter will try using {@link java.util.ServiceLoader} to find the implementations.
+ * If the plugin exposes the service via service loader (META-INF/services) then it will improve JMeter startup.
+ * <p>Note: JMeter will still try scanning the classes in the jars for backward compatibility reasons,
+ * so if you expose services, then consider adding {@code JMeter-Skip-Class-Scanning: true} manifest attribute
+ * to your jar file. JMeter will skip scanning class files in such jars</p>
+ * @since 5.6
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@API(status = API.Status.EXPERIMENTAL, since = "5.6")
+public @interface JMeterService {
+}
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/reflect/LogAndIgnoreServiceLoadExceptionHandler.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/LogAndIgnoreServiceLoadExceptionHandler.java
new file mode 100644
index 0000000000..5959bb54cc
--- /dev/null
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/LogAndIgnoreServiceLoadExceptionHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jorphan.reflect;
+
+import org.slf4j.Logger;
+
+/**
+ * Logs all the failures to agiven {@link Logger} and ignores them.
+ */
+public class LogAndIgnoreServiceLoadExceptionHandler implements ServiceLoadExceptionHandler<Object> {
+ private final Logger log;
+
+ public LogAndIgnoreServiceLoadExceptionHandler(Logger log) {
+ this.log = log;
+ }
+
+ @Override
+ public void handle(Class<?> service, String className, Throwable throwable) {
+ if (throwable instanceof NoClassDefFoundError) {
+ if (throwable.getMessage().contains("javafx")) {
+ log.error(
+ "Exception registering implementation: [{}] of interface: [{}], a dependency used by the plugin class is missing. " +
+ "Add JavaFX to your Java installation if you want to use renderer: {}",
+ className, service, className, throwable
+ );
+ } else {
+ log.error(
+ "Exception registering implementation: [{}] of interface: [{}], a dependency used by the plugin class is missing",
+ className, service, throwable
+ );
+ }
+ } else {
+ log.error(
+ "Exception registering implementation: [{}] of interface: [{}], a jar is probably missing",
+ className, service, throwable
+ );
+ }
+ }
+}
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/RethrowServiceLoadExceptionHandler.java
similarity index 65%
copy from src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
copy to src/jorphan/src/main/java/org/apache/jorphan/reflect/RethrowServiceLoadExceptionHandler.java
index f2c8d99012..91400f3c8e 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/RethrowServiceLoadExceptionHandler.java
@@ -15,23 +15,14 @@
* limitations under the License.
*/
-package org.apache.jmeter.threads;
+package org.apache.jorphan.reflect;
/**
- * Interface notified when number of active threads changes
- * @since 2.10
+ * Rethrows service loading failures as {@link IllegalStateException}.
*/
-public interface RemoteThreadsLifeCycleListener {
-
- /**
- *
- * @param numberOfThreads number of active threads
- */
- void threadNumberIncreased(int numberOfThreads);
-
- /**
- *
- * @param numberOfThreads number of active threads
- */
- void threadNumberDecreased(int numberOfThreads);
+public class RethrowServiceLoadExceptionHandler implements ServiceLoadExceptionHandler<Object> {
+ @Override
+ public void handle(Class<?> service, String className, Throwable throwable) {
+ throw new IllegalStateException("Can't load class " + className + " for instantiating service " + service, throwable);
+ }
}
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadExceptionHandler.java
similarity index 57%
copy from src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
copy to src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadExceptionHandler.java
index 82acc7183b..816c2931db 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadExceptionHandler.java
@@ -15,29 +15,22 @@
* limitations under the License.
*/
-package org.apache.jmeter.functions;
+package org.apache.jorphan.reflect;
-import org.apache.jmeter.util.JMeterUtils;
+import org.apiguardian.api.API;
/**
- * Return Machine IP
- * @since 2.6
+ * Service loading might fail (e.g. due to a missing dependency).
+ * This handler enables client code factor the failure handing.
+ *
+ * @param <S> type of the service
+ * @since 5.6
+ * @see IgnoreServiceLoadExceptionHandler
+ * @see LogAndIgnoreServiceLoadExceptionHandler
+ * @see RethrowServiceLoadExceptionHandler
*/
-public class MachineIP extends AbstractHostIPName {
-
- private static final String KEY = "__machineIP"; //$NON-NLS-1$
-
- public MachineIP() {
- }
-
- @Override
- protected String compute() {
- return JMeterUtils.getLocalHostIP();
- }
-
- /** {@inheritDoc} */
- @Override
- public String getReferenceKey() {
- return KEY;
- }
+@FunctionalInterface
+@API(status = API.Status.EXPERIMENTAL, since = "5.6")
+public interface ServiceLoadExceptionHandler<S> {
+ void handle(Class<? extends S> service, String className, Throwable throwable);
}
diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadFailure.java
similarity index 51%
copy from src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java
copy to src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadFailure.java
index d92f150120..825f39aeb6 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsHTMLWithEmbedded.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadFailure.java
@@ -15,25 +15,37 @@
* limitations under the License.
*/
-package org.apache.jmeter.visualizers;
+package org.apache.jorphan.reflect;
-import org.apache.jmeter.samplers.SampleResult;
-import org.apache.jmeter.util.JMeterUtils;
+public class ServiceLoadFailure<S> {
+ private final Class<? extends S> service;
+ private final String className;
+ private final Throwable throwable;
-public class RenderAsHTMLWithEmbedded extends RenderAsHTML
- implements ResultRenderer {
+ public ServiceLoadFailure(Class<? extends S> service, String className, Throwable throwable) {
+ this.service = service;
+ this.className = className;
+ this.throwable = throwable;
+ }
- /** {@inheritDoc} */
- @Override
- protected void showRenderedResponse(String response, SampleResult res) {
- // enable embedded html resources
- showRenderedResponse(response, res, true);
+ public Class<? extends S> getService() {
+ return service;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public Throwable getThrowable() {
+ return throwable;
}
- /** {@inheritDoc} */
@Override
public String toString() {
- return JMeterUtils.getResString("view_results_render_html_embedded"); // $NON-NLS-1$
+ return "ServiceLoadFailure{" +
+ "service=" + service +
+ ", className='" + className + '\'' +
+ ", throwable=" + throwable +
+ '}';
}
-
}
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
index d9388b3050..966c3502af 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
@@ -63,6 +63,7 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.action.AbstractAction;
import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
+import org.apache.jmeter.gui.action.Command;
import org.apache.jmeter.gui.plugin.MenuCreator;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
@@ -110,12 +111,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
+import com.google.auto.service.AutoService;
+
/**
* Opens a popup where user can enter a cURL command line and create a test plan
* from it
*
* @since 5.1
*/
+@AutoService({
+ Command.class,
+ MenuCreator.class
+})
public class ParseCurlCommandAction extends AbstractAction implements MenuCreator, ActionListener { // NOSONAR
private static final Logger LOGGER = LoggerFactory.getLogger(ParseCurlCommandAction.class);
private static final String ACCEPT_ENCODING = "Accept-Encoding";
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 051389da24..27b9894be6 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
@@ -57,10 +57,12 @@ import org.xml.sax.helpers.DefaultHandler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.auto.service.AutoService;
/**
* Default implementation that handles classical HTTP textual + Multipart requests
*/
+@AutoService(SamplerCreator.class)
public class DefaultSamplerCreator extends AbstractSamplerCreator {
private static final Logger log = LoggerFactory.getLogger(DefaultSamplerCreator.class);
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 81bc9d99f5..54768d353d 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
@@ -23,10 +23,12 @@ import java.util.Map;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
+import org.apache.jorphan.reflect.JMeterService;
/**
* Factory of sampler
*/
+@JMeterService
public interface SamplerCreator {
/**
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 3f3549cfb3..679db1dbe8 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
@@ -17,14 +17,12 @@
package org.apache.jmeter.protocol.http.proxy;
-import java.io.IOException;
-import java.lang.reflect.Modifier;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
+import java.util.ServiceLoader;
import org.apache.jmeter.util.JMeterUtils;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,41 +57,27 @@ public class SamplerCreatorFactory {
* Initialize factory from classpath
*/
private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
- try {
- List<String> listClasses = ClassFinder.findClassesThatExtend(
- JMeterUtils.getSearchPaths(),
- new Class[] {SamplerCreator.class });
- for (String strClassName : listClasses) {
- try {
- if(log.isDebugEnabled()) {
- log.debug("Loading class: {}", strClassName);
+ for (SamplerCreator creator : JMeterUtils.loadServicesAndScanJars(
+ SamplerCreator.class,
+ ServiceLoader.load(SamplerCreator.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ )) {
+ try {
+ String[] contentTypes = creator.getManagedContentTypes();
+ for (String contentType : contentTypes) {
+ log.debug("Registering samplerCreator {} for content type:{}",
+ creator.getClass().getName(), contentType);
+ SamplerCreator oldSamplerCreator = samplerCreatorMap.put(contentType, creator);
+ if (oldSamplerCreator != null) {
+ log.warn("A sampler creator was already registered for:{}, class:{}, it will be replaced",
+ contentType, oldSamplerCreator.getClass());
}
- Class<?> commandClass = Class.forName(strClassName);
- if (!Modifier.isAbstract(commandClass.getModifiers())) {
- if(log.isDebugEnabled()) {
- log.debug("Instantiating: {}", commandClass.getName());
- }
- SamplerCreator creator = (SamplerCreator) commandClass.getDeclaredConstructor().newInstance();
- String[] contentTypes = creator.getManagedContentTypes();
- for (String contentType : contentTypes) {
- if(log.isDebugEnabled()) {
- log.debug("Registering samplerCreator {} for content type:{}",
- commandClass.getName(), contentType);
- }
- SamplerCreator oldSamplerCreator = samplerCreatorMap.put(contentType, creator);
- if(oldSamplerCreator!=null) {
- log.warn("A sampler creator was already registered for:{}, class:{}, it will be replaced",
- contentType, oldSamplerCreator.getClass());
- }
- }
- }
- } catch (Exception e) {
- log.error("Exception registering {} with implementation:{}",
- SamplerCreator.class.getName(),strClassName, e);
}
+ } catch (Exception e) {
+ log.error("Exception registering {} with implementation:{}",
+ SamplerCreator.class.getName(), creator.getClass(), e);
}
- } catch (IOException e) {
- log.error("Exception finding implementations of {}", SamplerCreator.class, e);
}
}
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/AccessLogSamplerBeanInfo.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/AccessLogSamplerBeanInfo.java
index 45aa4e1b91..2495527214 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/AccessLogSamplerBeanInfo.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/sampler/AccessLogSamplerBeanInfo.java
@@ -18,90 +18,92 @@
package org.apache.jmeter.protocol.http.sampler;
import java.beans.PropertyDescriptor;
-import java.io.IOException;
-import java.util.Collections;
import java.util.List;
+import java.util.ServiceLoader;
+import java.util.stream.Collectors;
import org.apache.jmeter.protocol.http.util.accesslog.Filter;
import org.apache.jmeter.protocol.http.util.accesslog.LogParser;
import org.apache.jmeter.testbeans.BeanInfoSupport;
import org.apache.jmeter.testbeans.gui.FileEditor;
import org.apache.jmeter.util.JMeterUtils;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AccessLogSamplerBeanInfo extends BeanInfoSupport {
private static final Logger log = LoggerFactory.getLogger(AccessLogSamplerBeanInfo.class);
- private static final List<String> LOG_PARSER_CLASSES = logParsers();
-
- private static List<String> logParsers() {
- try {
- return ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { LogParser.class });
- } catch (IOException e) {
- log.warn("Could not find log parsers.", e);
- return Collections.emptyList();
- }
- }
+ private static final List<String> LOG_PARSER_CLASSES =
+ JMeterUtils.loadServicesAndScanJars(
+ LogParser.class,
+ ServiceLoader.load(LogParser.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ ).stream()
+ .map(s -> s.getClass().getName())
+ .sorted()
+ .collect(Collectors.toList());
public AccessLogSamplerBeanInfo() {
super(AccessLogSampler.class);
log.debug("Entered access log sampler bean info");
- try {
- createPropertyGroup("defaults", // $NON-NLS-1$
- new String[] { "protocol", "domain", "portString", "imageParsing" });// $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
-
- createPropertyGroup("plugins", // $NON-NLS-1$
- new String[] { "parserClassName", "filterClassName" }); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
-
- createPropertyGroup("accesslogfile", // $NON-NLS-1$
- new String[] { "logFile" }); // $NON-NLS-1$
-
- PropertyDescriptor p;
-
- p = property("parserClassName");
- p.setValue(NOT_UNDEFINED, Boolean.TRUE);
- p.setValue(DEFAULT, AccessLogSampler.DEFAULT_CLASS);
- p.setValue(NOT_OTHER, Boolean.TRUE);
- p.setValue(NOT_EXPRESSION, Boolean.TRUE);
-
- log.debug("found parsers: {}", LOG_PARSER_CLASSES);
- p.setValue(TAGS, LOG_PARSER_CLASSES.toArray(new String[LOG_PARSER_CLASSES.size()]));
-
- p = property("filterClassName"); // $NON-NLS-1$
- p.setValue(NOT_UNDEFINED, Boolean.FALSE);
- p.setValue(DEFAULT, ""); // $NON-NLS-1$
- p.setValue(NOT_EXPRESSION, Boolean.TRUE);
- List<String> classes = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),
- new Class[] { Filter.class }, false);
- p.setValue(TAGS, classes.toArray(new String[classes.size()]));
-
- p = property("logFile"); // $NON-NLS-1$
- p.setValue(NOT_UNDEFINED, Boolean.TRUE);
- p.setValue(DEFAULT, "");
- p.setPropertyEditorClass(FileEditor.class);
-
- p = property("domain"); // $NON-NLS-1$
- p.setValue(NOT_UNDEFINED, Boolean.TRUE);
- p.setValue(DEFAULT, "");
-
- p = property("protocol"); // $NON-NLS-1$
- p.setValue(NOT_UNDEFINED, Boolean.TRUE);
- p.setValue(DEFAULT, "http"); // $NON-NLS-1$
- p.setValue(DEFAULT_NOT_SAVED, Boolean.TRUE);
-
- p = property("portString"); // $NON-NLS-1$
- p.setValue(NOT_UNDEFINED, Boolean.TRUE);
- p.setValue(DEFAULT, ""); // $NON-NLS-1$
-
- p = property("imageParsing"); // $NON-NLS-1$
- p.setValue(NOT_UNDEFINED, Boolean.TRUE);
- p.setValue(DEFAULT, Boolean.FALSE);
- p.setValue(NOT_OTHER, Boolean.TRUE);
- } catch (IOException e) {
- log.warn("couldn't find classes and set up properties", e);
- throw new RuntimeException("Could not find classes with class finder", e);
- }
+ createPropertyGroup("defaults", // $NON-NLS-1$
+ new String[] { "protocol", "domain", "portString", "imageParsing" });// $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
+
+ createPropertyGroup("plugins", // $NON-NLS-1$
+ new String[] { "parserClassName", "filterClassName" }); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
+
+ createPropertyGroup("accesslogfile", // $NON-NLS-1$
+ new String[] { "logFile" }); // $NON-NLS-1$
+
+ PropertyDescriptor p;
+
+ p = property("parserClassName");
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, AccessLogSampler.DEFAULT_CLASS);
+ p.setValue(NOT_OTHER, Boolean.TRUE);
+ p.setValue(NOT_EXPRESSION, Boolean.TRUE);
+
+ log.debug("found parsers: {}", LOG_PARSER_CLASSES);
+ p.setValue(TAGS, LOG_PARSER_CLASSES.toArray(new String[0]));
+
+ p = property("filterClassName"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.FALSE);
+ p.setValue(DEFAULT, ""); // $NON-NLS-1$
+ p.setValue(NOT_EXPRESSION, Boolean.TRUE);
+ String[] classes = JMeterUtils.loadServicesAndScanJars(
+ Filter.class,
+ ServiceLoader.load(Filter.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ ).stream()
+ .map(s -> s.getClass().getName())
+ .sorted()
+ .toArray(String[]::new);
+ p.setValue(TAGS, classes);
+
+ p = property("logFile"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "");
+ p.setPropertyEditorClass(FileEditor.class);
+
+ p = property("domain"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "");
+
+ p = property("protocol"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "http"); // $NON-NLS-1$
+ p.setValue(DEFAULT_NOT_SAVED, Boolean.TRUE);
+
+ p = property("portString"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, ""); // $NON-NLS-1$
+
+ p = property("imageParsing"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, Boolean.FALSE);
+ p.setValue(NOT_OTHER, Boolean.TRUE);
log.debug("Got to end of access log sampler bean info init");
}
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/Filter.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/Filter.java
index 182c361b1f..f78d6bf170 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/Filter.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/Filter.java
@@ -18,6 +18,7 @@
package org.apache.jmeter.protocol.http.util.accesslog;
import org.apache.jmeter.testelement.TestElement;
+import org.apache.jorphan.reflect.JMeterService;
/**
* Description:<br>
@@ -35,6 +36,7 @@ import org.apache.jmeter.testelement.TestElement;
*
*/
+@JMeterService
public interface Filter {
/**
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java
index 698e03953a..ce9487fb12 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogFilter.java
@@ -29,6 +29,8 @@ import org.apache.oro.text.regex.Perl5Compiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Description:<br>
* <br>
@@ -69,6 +71,7 @@ import org.slf4j.LoggerFactory;
* that should be replaced.
*/
@SuppressWarnings("InconsistentCapitalization")
+@AutoService(Filter.class)
public class LogFilter implements Filter, Serializable {
private static final long serialVersionUID = 241L;
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogParser.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogParser.java
index d08809c9e5..c29fa43b35 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogParser.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/LogParser.java
@@ -18,6 +18,7 @@
package org.apache.jmeter.protocol.http.util.accesslog;
import org.apache.jmeter.testelement.TestElement;
+import org.apache.jorphan.reflect.JMeterService;
/**
* Description:<br>
@@ -34,6 +35,7 @@ import org.apache.jmeter.testelement.TestElement;
*
*/
+@JMeterService
public interface LogParser {
/**
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java
index 581a21acdc..3f1f5dbc7b 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/OrderPreservingLogParser.java
@@ -19,6 +19,9 @@ package org.apache.jmeter.protocol.http.util.accesslog;
import org.apache.jmeter.testelement.TestElement;
+import com.google.auto.service.AutoService;
+
+@AutoService(LogParser.class)
public class OrderPreservingLogParser extends SharedTCLogParser {
public OrderPreservingLogParser() {
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java
index e573aec218..6ffef587c3 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SessionFilter.java
@@ -37,9 +37,12 @@ import org.apache.oro.text.regex.Perl5Matcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Provides Session Filtering for the AccessLog Sampler.
*/
+@AutoService(Filter.class)
public class SessionFilter implements Filter, Serializable, TestCloneable,ThreadListener {
private static final java.util.regex.Pattern IP_PATTERN = java.util.regex.Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
private static final long serialVersionUID = 233L;
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java
index 9e429e03e1..d070216956 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/SharedTCLogParser.java
@@ -23,6 +23,9 @@ import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.testelement.TestCloneable;
import org.apache.jmeter.testelement.TestElement;
+import com.google.auto.service.AutoService;
+
+@AutoService(LogParser.class)
public class SharedTCLogParser extends TCLogParser implements TestCloneable {
public SharedTCLogParser() {
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java
index ce79dd0bab..72a45b25d2 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/util/accesslog/TCLogParser.java
@@ -36,6 +36,8 @@ import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Description:<br>
* <br>
@@ -72,6 +74,7 @@ import org.slf4j.LoggerFactory;
*/
@SuppressWarnings("InconsistentCapitalization")
+@AutoService(LogParser.class)
public class TCLogParser implements LogParser {
protected static final Logger log = LoggerFactory.getLogger(TCLogParser.class);
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/visualizers/RequestViewHTTP.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/visualizers/RequestViewHTTP.java
index 0a0790ee02..5eb198062e 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/visualizers/RequestViewHTTP.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/visualizers/RequestViewHTTP.java
@@ -56,9 +56,12 @@ import org.apache.jorphan.reflect.Functor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* Specializer panel to view a HTTP request parsed
*/
+@AutoService(RequestView.class)
public class RequestViewHTTP implements RequestView {
private static final Logger log = LoggerFactory.getLogger(RequestViewHTTP.class);
diff --git a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java
index 53b128ce78..ea25256a6e 100644
--- a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java
+++ b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java
@@ -18,12 +18,13 @@
package org.apache.jmeter.protocol.java.config.gui;
import java.awt.BorderLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.ServiceLoader;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
@@ -47,7 +48,7 @@ import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.JFactory;
import org.apache.jorphan.gui.JLabeledChoice;
-import org.apache.jorphan.reflect.ClassFinder;
+import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -136,21 +137,15 @@ public class JavaConfigGui extends AbstractConfigGui implements ChangeListener {
* @return a panel containing the relevant components
*/
private JPanel createClassnamePanel() {
- List<String> possibleClasses = new ArrayList<>();
-
- try {
- // Find all the classes which implement the JavaSamplerClient
- // interface.
- possibleClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),
- new Class[] { JavaSamplerClient.class });
-
- // Remove the JavaConfig class from the list since it only
- // implements the interface for error conditions.
-
- possibleClasses.remove(JavaSampler.class.getName() + "$ErrorSamplerClient");
- } catch (Exception e) {
- log.debug("Exception getting interfaces.", e);
- }
+ List<String> possibleClasses = JMeterUtils.loadServicesAndScanJars(
+ JavaSamplerClient.class,
+ ServiceLoader.load(JavaSamplerClient.class),
+ Thread.currentThread().getContextClassLoader(),
+ new LogAndIgnoreServiceLoadExceptionHandler(log)
+ ).stream()
+ .map(s -> s.getClass().getName())
+ .sorted()
+ .collect(Collectors.toList());
classNameLabeledChoice = new JLabeledChoice(
JMeterUtils.getResString("protocol_java_classname"),
diff --git a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/sampler/JavaSamplerClient.java b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/sampler/JavaSamplerClient.java
index ea884511a0..fdfff46b6f 100644
--- a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/sampler/JavaSamplerClient.java
+++ b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/sampler/JavaSamplerClient.java
@@ -19,6 +19,7 @@ package org.apache.jmeter.protocol.java.sampler;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jorphan.reflect.JMeterService;
/**
* This interface defines the interactions between the JavaSampler and external
@@ -59,6 +60,7 @@ import org.apache.jmeter.samplers.SampleResult;
* how to implement this interface.
*
*/
+@JMeterService
public interface JavaSamplerClient {
/**
* Do any initialization required by this client. It is generally
diff --git a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/JavaTest.java b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/JavaTest.java
index 83ee435a79..5efcb1fe18 100644
--- a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/JavaTest.java
+++ b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/JavaTest.java
@@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
+import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.Interruptible;
import org.apache.jmeter.samplers.SampleResult;
@@ -30,6 +31,8 @@ import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* The <code>JavaTest</code> class is a simple sampler which is intended for
* use when developing test plans. The sampler generates results internally, so
@@ -66,7 +69,7 @@ import org.slf4j.LoggerFactory;
* Note: this class was derived from {@link SleepTest}.
*
*/
-
+@AutoService(JavaSamplerClient.class)
public class JavaTest extends AbstractJavaSamplerClient implements Serializable, Interruptible {
private static final Logger LOG = LoggerFactory.getLogger(JavaTest.class);
diff --git a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/SleepTest.java b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/SleepTest.java
index 25ddbbdadf..3a8ee15084 100644
--- a/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/SleepTest.java
+++ b/src/protocol/java/src/main/java/org/apache/jmeter/protocol/java/test/SleepTest.java
@@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
+import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.Interruptible;
import org.apache.jmeter.samplers.SampleResult;
@@ -30,6 +31,8 @@ import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
/**
* The <code>SleepTest</code> class is a simple example class for a JMeter
* Java protocol client. The class implements the <code>JavaSamplerClient</code>
@@ -47,6 +50,7 @@ import org.slf4j.LoggerFactory;
* time.
*
*/
+@AutoService(JavaSamplerClient.class)
public class SleepTest extends AbstractJavaSamplerClient implements Serializable, Interruptible {
private static final Logger LOG = LoggerFactory.getLogger(SleepTest.class);
diff --git a/src/protocol/junit/src/main/java/org/apache/jmeter/protocol/java/control/gui/JUnitTestSamplerGui.java b/src/protocol/junit/src/main/java/org/apache/jmeter/protocol/java/control/gui/JUnitTestSamplerGui.java
index da8a0540ed..b2dc6e8175 100644
--- a/src/protocol/junit/src/main/java/org/apache/jmeter/protocol/java/control/gui/JUnitTestSamplerGui.java
+++ b/src/protocol/junit/src/main/java/org/apache/jmeter/protocol/java/control/gui/JUnitTestSamplerGui.java
@@ -171,10 +171,14 @@ implements ChangeListener, ActionListener, ItemListener
if(initialize) {
synchronized (IS_INITILIAZED) {
if(IS_INITILIAZED.compareAndSet(false, true)) {
- annotatedTestClasses = ClassFinder.findAnnotatedClasses(SPATHS,
- new Class[] {Test.class}, false);
- junitTestClasses = ClassFinder.findClassesThatExtend(SPATHS,
- new Class[] { TestCase.class });
+ @SuppressWarnings("deprecation")
+ List<String> annotatedClasses = ClassFinder.findAnnotatedClasses(SPATHS,
+ new Class[]{Test.class}, false);
+ annotatedTestClasses = annotatedClasses;
+ @SuppressWarnings("deprecation")
+ List<String> junitClasses = ClassFinder.findClassesThatExtend(SPATHS,
+ new Class[]{TestCase.class});
+ junitTestClasses = junitClasses;
}
if (junit4.isSelected()){
classList = annotatedTestClasses;
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/src/test-services/build.gradle.kts
similarity index 86%
copy from build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
copy to src/test-services/build.gradle.kts
index 534f894dfc..9efcceed31 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
+++ b/src/test-services/build.gradle.kts
@@ -16,6 +16,10 @@
*/
plugins {
- id("build-logic.java")
- id("java-library")
+ id("build-logic.jvm-library")
+}
+
+dependencies {
+ implementation(projects.src.jorphan)
+ implementation(projects.src.core)
}
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/AbstractService.kt
similarity index 76%
copy from build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
copy to src/test-services/src/main/kotlin/org/apache/jmeter/util/services/AbstractService.kt
index 534f894dfc..b3cc906b97 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
+++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/AbstractService.kt
@@ -15,7 +15,11 @@
* limitations under the License.
*/
-plugins {
- id("build-logic.java")
- id("java-library")
-}
+package org.apache.jmeter.util.services
+
+import com.google.auto.service.AutoService
+
+@AutoService(AbstractServiceInterface::class)
+public abstract class AbstractService : AbstractServiceInterface
+
+public interface AbstractServiceInterface
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceNotImplementingInterface.kt
similarity index 78%
copy from build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
copy to src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceNotImplementingInterface.kt
index 534f894dfc..e70ff8f82c 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
+++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceNotImplementingInterface.kt
@@ -15,7 +15,11 @@
* limitations under the License.
*/
-plugins {
- id("build-logic.java")
- id("java-library")
-}
+package org.apache.jmeter.util.services
+
+import com.google.auto.service.AutoService
+
+@AutoService(NotImplementedInterface::class)
+public class ServiceNotImplementingInterface
+
+public interface NotImplementedInterface
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceThrowingException.kt
similarity index 64%
copy from src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
copy to src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceThrowingException.kt
index f2c8d99012..7c16a54ae6 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/RemoteThreadsLifeCycleListener.java
+++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceThrowingException.kt
@@ -15,23 +15,17 @@
* limitations under the License.
*/
-package org.apache.jmeter.threads;
+package org.apache.jmeter.util.services
-/**
- * Interface notified when number of active threads changes
- * @since 2.10
- */
-public interface RemoteThreadsLifeCycleListener {
-
- /**
- *
- * @param numberOfThreads number of active threads
- */
- void threadNumberIncreased(int numberOfThreads);
+import com.google.auto.service.AutoService
- /**
- *
- * @param numberOfThreads number of active threads
- */
- void threadNumberDecreased(int numberOfThreads);
+@AutoService(ServiceThrowingExceptionInterface::class)
+public class ServiceThrowingException : ServiceThrowingExceptionInterface {
+ init {
+ throw ServiceFailureException("exception in constructor for test purposes")
+ }
}
+
+public class ServiceFailureException(message: String) : Throwable(message)
+
+public interface ServiceThrowingExceptionInterface
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceWithPrivateConstructor.kt
similarity index 72%
copy from build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
copy to src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceWithPrivateConstructor.kt
index 534f894dfc..2710984ea7 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts
+++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceWithPrivateConstructor.kt
@@ -15,7 +15,11 @@
* limitations under the License.
*/
-plugins {
- id("build-logic.java")
- id("java-library")
-}
+package org.apache.jmeter.util.services
+
+import com.google.auto.service.AutoService
+
+@AutoService(ServiceWithPrivateConstructorInterface::class)
+public class ServiceWithPrivateConstructor private constructor() : ServiceWithPrivateConstructorInterface
+
+public interface ServiceWithPrivateConstructorInterface
diff --git a/build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/WorkableService.kt
similarity index 69%
copy from build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts
copy to src/test-services/src/main/kotlin/org/apache/jmeter/util/services/WorkableService.kt
index cabe6e0282..af134427d2 100644
--- a/build-logic/jvm/src/main/kotlin/build-logic.jvm-library.gradle.kts
+++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/WorkableService.kt
@@ -15,15 +15,17 @@
* limitations under the License.
*/
-plugins {
- id("build-logic.java")
- id("java-library")
-}
+package org.apache.jmeter.util.services
+
+import com.google.auto.service.AutoService
+
+@AutoService(WorkableServiceInterface::class)
+public class WorkableService : WorkableServiceInterface {
+ override val name: String = "test service"
-if (file("src/main/groovy").isDirectory || file("src/test/groovy").isDirectory) {
- apply(plugin = "build-logic.groovy")
+ override fun toString(): String = name
}
-if (file("src/main/kotlin").isDirectory || file("src/test/kotlin").isDirectory) {
- apply(plugin = "build-logic.kotlin")
+public interface WorkableServiceInterface {
+ public val name: String
}
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/loadServices.kt
similarity index 62%
copy from src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
copy to src/test-services/src/main/kotlin/org/apache/jmeter/util/services/loadServices.kt
index 82acc7183b..81102d7205 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/MachineIP.java
+++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/loadServices.kt
@@ -15,29 +15,18 @@
* limitations under the License.
*/
-package org.apache.jmeter.functions;
+package org.apache.jmeter.util.services
-import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jmeter.util.JMeterUtils
+import org.apache.jorphan.reflect.ServiceLoadExceptionHandler
+import java.util.ServiceLoader
-/**
- * Return Machine IP
- * @since 2.6
- */
-public class MachineIP extends AbstractHostIPName {
-
- private static final String KEY = "__machineIP"; //$NON-NLS-1$
-
- public MachineIP() {
- }
-
- @Override
- protected String compute() {
- return JMeterUtils.getLocalHostIP();
- }
-
- /** {@inheritDoc} */
- @Override
- public String getReferenceKey() {
- return KEY;
- }
-}
+public inline fun <reified S : Any> loadServices(
+ exceptionHandler: ServiceLoadExceptionHandler<S>
+): Collection<S> =
+ JMeterUtils.loadServicesAndScanJars(
+ S::class.java,
+ ServiceLoader.load(S::class.java),
+ Thread.currentThread().contextClassLoader,
+ exceptionHandler
+ )
diff --git a/src/test-services/src/test/kotlin/ServiceLoaderTest.kt b/src/test-services/src/test/kotlin/ServiceLoaderTest.kt
new file mode 100644
index 0000000000..3c4d1d1957
--- /dev/null
+++ b/src/test-services/src/test/kotlin/ServiceLoaderTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.jmeter.util.services.AbstractServiceInterface
+import org.apache.jmeter.util.services.NotImplementedInterface
+import org.apache.jmeter.util.services.ServiceThrowingExceptionInterface
+import org.apache.jmeter.util.services.ServiceWithPrivateConstructorInterface
+import org.apache.jmeter.util.services.WorkableServiceInterface
+import org.apache.jmeter.util.services.loadServices
+import org.apache.jorphan.reflect.CollectServiceLoadExceptionHandler
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertAll
+
+class ServiceLoaderTest {
+ @Test
+ fun `service without public constructor`() {
+ assertServiceLoad<ServiceWithPrivateConstructorInterface>(
+ "[]",
+ "[service: org.apache.jmeter.util.services.ServiceWithPrivateConstructorInterface, className: org.apache.jmeter.util.services.ServiceWithPrivateConstructor, exceptionClass: java.util.ServiceConfigurationError, causeClass: java.lang.NoSuchMethodException]"
+ )
+ }
+
+ @Test
+ fun `service not implementing interface`() {
+ assertServiceLoad<NotImplementedInterface>(
+ "[]",
+ "[service: org.apache.jmeter.util.services.NotImplementedInterface, className: org.apache.jmeter.util.services.ServiceNotImplementingInterface, exceptionClass: java.util.ServiceConfigurationError, causeClass: null]"
+ )
+ }
+
+ @Test
+ fun `service failing in constructor`() {
+ assertServiceLoad<ServiceThrowingExceptionInterface>(
+ "[]",
+ "[service: org.apache.jmeter.util.services.ServiceThrowingExceptionInterface, className: org.apache.jmeter.util.services.ServiceThrowingException, exceptionClass: java.util.ServiceConfigurationError, causeClass: org.apache.jmeter.util.services.ServiceFailureException]"
+ )
+ }
+
+ @Test
+ fun `abstract service`() {
+ assertServiceLoad<AbstractServiceInterface>(
+ "[]",
+ "[service: org.apache.jmeter.util.services.AbstractServiceInterface, className: org.apache.jmeter.util.services.AbstractService, exceptionClass: java.util.ServiceConfigurationError, causeClass: java.lang.InstantiationException]"
+ )
+ }
+
+ @Test
+ fun `service loads`() {
+ assertServiceLoad<WorkableServiceInterface>(
+ "[test service]",
+ "[]"
+ )
+ }
+
+ private inline fun <reified S : Any> assertServiceLoad(
+ successMessage: String,
+ failureMessage: String,
+ ) {
+ val failures = CollectServiceLoadExceptionHandler<S>()
+ val successes = loadServices<S>(failures)
+ val allFailures = failures.toCollection()
+ assertAll(
+ {
+ assertEquals(successMessage, successes.toString()) {
+ "Successfully loaded services for ${S::class.java.name}"
+ }
+ },
+ {
+ assertEquals(
+ failureMessage,
+ allFailures.map {
+ "service: ${it.service.name}, " +
+ "className: ${it.className}, " +
+ "exceptionClass: ${it.throwable::class.java.name}, " +
+ "causeClass: ${it.throwable.cause?.let { it::class.java.name }}"
+ }.toString(),
+ ) {
+ "All failures when loading service ${S::class.java.name} are $allFailures"
+ }
+ }
+ )
+ }
+}
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index 4ea3946381..f212ecfbf5 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -103,6 +103,7 @@ Summary
<li><pr>5792</pr>Add KeyStroke for start_no_timers (Start no pauses: CRTL+SHIFT+n)</li>
<li><pr>5899</pr>Speed up CPU-bound tests by skipping <code>recoverRunningVersion</code> for elements that are shared between threads (the ones that implement <code>NoThreadClone</code>)</li>
<li><pr>5914</pr>Use <code>Locale.ROOT</code> instead of default locale for <code>toUpperCase</code>, and <code>toLowerCase</code> to avoid surprises with dotless I in <code>tr_TR</code> locale</li>
+ <li><pr>5885</pr>Use Java's <code>ServiceLoader</code> for loading plugins instead of classpath scanning. It enables faster startup</li>
</ul>
<ch_section>Non-functional changes</ch_section>
diff --git a/xdocs/usermanual/jmeter_tutorial.xml b/xdocs/usermanual/jmeter_tutorial.xml
index f32a8d0f88..04dfec7578 100644
--- a/xdocs/usermanual/jmeter_tutorial.xml
+++ b/xdocs/usermanual/jmeter_tutorial.xml
@@ -168,7 +168,49 @@ write.
</subsection>
-<subsection name="§-num;.2 JMeter Gui – TestElement Contract" anchor="testelement-contract">
+ <subsection name="§-num;.2 JMeter Gui – Service lookup" anchor="service-lookup">
+ <p>
+ JMeter uses classpath scanning to detect plugins (e.g. functions, components, views). However, classpath scanning
+ is expensive,
+ so JMeter also provides a service lookup mechanism to allow plugins to be found without scanning the classpath.
+ That is if a plugin registers a Java service with <code>META-INF/services</code>, then JMeter won't need to scan
+ the classpath to find it.
+ </p>
+
+ <p>
+ Some of the existing features already use the new service lookup mechanism, but it is not yet used for all
+ features.
+ The interfaces that are supported for service loading are marked with
+ <code>org.apache.jorphan.reflect.JMeterService</code>
+ annotation.
+ </p>
+
+ <p>
+ Implementing a service the same as regular interface implementation, except you need to register the service
+ in a <code>META-INF/services/fully.qualified.interface.name</code>.
+ For instance you could use <code>@AutoService</code> to generate the file automatically at the build time.
+ Here's how <code>__counter</code> function is declared in JMeter itself:
+<source>
+import com.google.auto.service.AutoService;
+
+@AutoService(Function.class)
+public class IterationCounter extends AbstractFunction implements ThreadListener {
+</source>
+ </p>
+
+ <p>
+ For backward compatibility reasons, JMeter would still try searching the implementations with classpath scanning,
+ so you don't have to use <code>META-INF/services</code> for registering services. However, service lookup is much
+ faster, so exposing services would improve startup time, especially when there are many plugins.
+ </p>
+
+ <p>
+ As you add <code>META-INF/services</code> to your plugins, you can add <code>JMeter-Skip-Class-Scanning: true</code>
+ manifest attribute so JMeter knows there's no need to scan the jar as it provides all the plugins via services.
+ </p>
+ </subsection>
+
+<subsection name="§-num;.3 JMeter Gui – TestElement Contract" anchor="testelement-contract">
<p>
When writing any JMeter component, there are certain contracts you must be aware of – ways a
@@ -327,7 +369,7 @@ Writing Visualizers is somewhat of a special case, however.
</subsection>
-<subsection name="§-num;.3 Writing a Visualizer" anchor="visualizer">
+<subsection name="§-num;.4 Writing a Visualizer" anchor="visualizer">
<note>Load Testing in GUI mode being a bad practice, you should not develop such plugin. Have
a look at more up to date components like:
<ul>
@@ -537,7 +579,7 @@ added so that users can save a screen capture of any given visualizer.
</subsection>
-<subsection name="§-num;.4 GraphListener" anchor="graphlistener">
+<subsection name="§-num;.5 GraphListener" anchor="graphlistener">
<note>Load Testing in GUI mode being a bad practice, you should not develop such plugin. Have
a look at more up to date components like:
<ul>
@@ -571,7 +613,7 @@ implement the interface, please look at <code>GraphVisualizer</code>.
</subsection>
-<subsection name="§-num;.5 Writing Custom Graphs" anchor="custom-graphs">
+<subsection name="§-num;.6 Writing Custom Graphs" anchor="custom-graphs">
<note>Load Testing in GUI mode being a bad practice, you should not develop such plugin. Have
a look at more up to date components like:
<ul>
@@ -715,7 +757,7 @@ and predictable.
</subsection>
-<subsection name="§-num;.6 Making a TestBean Plugin For JMeter" anchor="testbean">
+<subsection name="§-num;.7 Making a TestBean Plugin For JMeter" anchor="testbean">
<p>
In this part, we will go through the process of creating a simple component for JMeter that uses
@@ -860,7 +902,7 @@ p.setPropertyEditorClass(FileEditor.class);
</subsection>
-<subsection name="§-num;.6 Building JMeter" anchor="building">
+<subsection name="§-num;.8 Building JMeter" anchor="building">
<p>
JMeter uses Gradle to compile and build the distribution. JMeter has