diff --git a/build-logic/jvm/build.gradle.kts b/build-logic/jvm/build.gradle.kts index 0c8b9388a6c..7044d76d981 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.autoservice.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.autoservice.gradle.kts new file mode 100644 index 00000000000..0ee27ed50b5 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.autoservice.gradle.kts @@ -0,0 +1,44 @@ +/* + * 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 com.github.vlsi.gradle.dsl.configureEach + +plugins { + id("java-library") +} + +dependencies { + annotationProcessor("com.google.auto.service:auto-service") + compileOnlyApi("com.google.auto.service:auto-service-annotations") +} + +tasks.configureEach { + // Verify @AutoService annotations + options.compilerArgs.add("-Averify=true") +} + +plugins.withId("org.jetbrains.kotlin.jvm") { + apply(plugin = "org.jetbrains.kotlin.kapt") + + 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)) + } + } +} 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 534f894dfc1..b2d6967d718 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 175c9740af3..3f4a60691fb 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 cabe6e02820..7f5f0315fd0 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 65cb342e6d1..59a63eb01d1 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.gradle.kts b/build.gradle.kts index 91cb5261d7e..50ac4136f7d 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 3801fc003f3..eb14a438816 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 fd1b385e727..9543e6cfc7c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -74,6 +74,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 4953cf3e1dc..dd87ad068fc 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 d2131a3350e..575757cc483 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 @@ -17,8 +17,11 @@ package org.apache.jmeter.extractor.json.render; +import com.google.auto.service.AutoService; + 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; @@ -31,6 +34,7 @@ * 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 e7d856db82c..75eecbca23a 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 @@ -19,8 +19,11 @@ import java.util.List; +import com.google.auto.service.AutoService; + 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; @@ -28,6 +31,7 @@ * 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/visualizers/RenderAsBoundaryExtractor.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RenderAsBoundaryExtractor.java index e48002e246f..56cd46b0271 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 @@ -34,6 +34,8 @@ import javax.swing.border.Border; import javax.swing.border.EmptyBorder; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.extractor.BoundaryExtractor; import org.apache.jmeter.samplers.SampleResult; @@ -44,6 +46,7 @@ /** * 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 e802246feab..86ecbdef61f 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 @@ -37,6 +37,8 @@ import javax.swing.border.Border; import javax.swing.border.EmptyBorder; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.extractor.Extractor; import org.apache.jmeter.extractor.HtmlExtractor; @@ -53,6 +55,7 @@ * 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 d7fde8f6419..e8d5adfc455 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 @@ -17,12 +17,15 @@ package org.apache.jmeter.visualizers; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.Document; import org.apache.jmeter.util.JMeterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@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 043e7bc9f9e..9f51dbf6e06 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 @@ -28,11 +28,14 @@ import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLEditorKit; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@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 cfbb7d208e2..4c830722afc 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 @@ -17,10 +17,13 @@ package org.apache.jmeter.visualizers; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; import org.jsoup.Jsoup; +@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 d92f150120c..57d98c4ebeb 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 @@ -17,9 +17,12 @@ package org.apache.jmeter.visualizers; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; +@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 b1550a754b0..12f46707045 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 @@ -19,6 +19,8 @@ import java.io.IOException; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; @@ -29,6 +31,7 @@ import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; +@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 375d90299d5..d38c50b9bf0 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 @@ -37,6 +37,8 @@ import javax.swing.border.Border; import javax.swing.border.EmptyBorder; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; @@ -53,6 +55,7 @@ /** * 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 34616fe9824..c1601c47119 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 @@ -17,9 +17,12 @@ package org.apache.jmeter.visualizers; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; +@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 5c8b6af25bc..89f9acc9e6c 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 @@ -42,6 +42,8 @@ import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; import org.apache.jmeter.util.XPathUtil; @@ -53,6 +55,7 @@ import org.w3c.tidy.Tidy; import org.xml.sax.SAXException; +@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 df39c62e71f..f30c3ec4b8c 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 @@ -40,6 +40,8 @@ import javax.swing.border.EmptyBorder; import javax.xml.parsers.ParserConfigurationException; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.jmeter.assertions.gui.XMLConfPanel; @@ -63,6 +65,7 @@ /** * 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 e36484bf90f..987c09f09b8 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 @@ -36,6 +36,8 @@ import javax.swing.JTabbedPane; import javax.swing.JTextField; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.jmeter.extractor.XPath2Extractor; @@ -51,6 +53,7 @@ /** * 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 575313c359a..eab0bfd39ac 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.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 @@ * {@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 ac3a6a09e17..78aae69f400 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 @@ -20,9 +20,11 @@ import java.awt.BorderLayout; import java.io.IOException; import java.util.ArrayDeque; +import java.util.Collection; 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 +32,8 @@ import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler; +import org.apache.jorphan.reflect.ServiceLoadExceptionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,37 +56,23 @@ public class RequestPanel { */ public RequestPanel() { listRequestView = new ArrayDeque<>(); - List classesToAdd = Collections. 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.loadServices( + 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/RequestViewRaw.java b/src/components/src/main/java/org/apache/jmeter/visualizers/RequestViewRaw.java index c721a152d55..bc1d0bc566b 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 @@ -23,6 +23,8 @@ import javax.swing.JScrollPane; import javax.swing.JTabbedPane; +import com.google.auto.service.AutoService; + import org.apache.jmeter.gui.util.JSyntaxSearchToolBar; import org.apache.jmeter.gui.util.JSyntaxTextArea; import org.apache.jmeter.gui.util.JTextScrollPane; @@ -34,6 +36,7 @@ * (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/ViewResultsFullVisualizer.java b/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java index 329e3d21b46..64ea22f79df 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 @@ -36,6 +36,7 @@ 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; @@ -74,6 +75,7 @@ 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; @@ -455,38 +457,24 @@ private Component createComboRender() { selectRenderPanel.addActionListener(this); // if no results render in jmeter.properties, load Standard (default) - List classesToAdd = Collections.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 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 map = new HashMap<>(); + for (ResultRenderer renderer : JMeterUtils.loadServices( + 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/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 1059d4675da..5164562da5a 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,7 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.ServiceLoader; +import org.apache.jmeter.JMeter; import org.apache.jmeter.functions.Function; import org.apache.jmeter.functions.InvalidVariableException; import org.apache.jmeter.samplers.SampleResult; @@ -31,6 +33,7 @@ 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 +72,15 @@ public class CompoundVariable implements Function { log.info("Note: Function class names must not contain the string: '{}'", notContain); } - List 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.loadServices( + 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/util/JMeterUtils.java b/src/core/src/main/java/org/apache/jmeter/util/JMeterUtils.java index 3a8408bb2a7..43199d77575 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,27 @@ 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.Iterator; 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.ServiceConfigurationError; +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 +67,7 @@ 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 +325,100 @@ public void initializeProperties(String file) { getProperties(file); } + /** + * Loads services implementing a given interface. + * + * @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 type of service (class or interface) + */ + public static Collection loadServices( + @SuppressWarnings("BoundedWildcard") Class service, + ServiceLoader serviceLoader, + ClassLoader classLoader, + ServiceLoadExceptionHandler exceptionHandler + ) { + List result = new ArrayList<>(); + @SuppressWarnings("ForEachIterable") + Iterator 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); + } + } + + // TODO: eliminate scanning of the JMeter jars. For instance, we could scan only user-provided jars + // It might be nice to have an opt-out mechanism so users can opt out of the scanning as well + // For instance, we could add a special entry in META-INF/MANIFEST.MF to indicate that the jar + // does not need scanning, and it exposes all the services with the ServiceLoader mechanism. + List classesFromJars = null; + try { + classesFromJars = findClassesThatExtend(service); + } catch (IOException e) { + // ignore + return result; + } + + if (classesFromJars.isEmpty()) { + return result; + } + + Set loadedClasses = new HashSet<>((int) (result.size() / 0.75f) + 1); + for (S s : result) { + loadedClasses.add(s.getClass().getName()); + } + + for (String className : classesFromJars) { + // Ignore classes that we loaded previously (e.g. with a ServiceLoader) + if (!loadedClasses.add(className)) { + continue; + } + try { + Class 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 +428,10 @@ public void initializeProperties(String file) { * @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 #loadServices(Class, ServiceLoader, ClassLoader, ServiceLoadExceptionHandler)} instead */ + @API(status = API.Status.DEPRECATED, since = "5.6") + @Deprecated public static List findClassesThatExtend(Class superClass) throws IOException { return ClassFinder.findClassesThatExtend(getSearchPaths(), new Class[]{superClass}, false); 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 88d64cd8e6b..82faf678b2c 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -36,6 +38,7 @@ * 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 c9a80a34a5b..8acb71202d2 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -53,6 +55,7 @@ * {@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 2798ca48c5c..e63fb077a21 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 @@ -22,6 +22,8 @@ import java.util.EnumSet; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -44,6 +46,7 @@ * @since 4.0 * */ +@AutoService(Function.class) public class ChangeCase extends AbstractFunction { private static final Logger LOGGER = LoggerFactory.getLogger(ChangeCase.class); private static final List 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 94e5df40c12..a546d8395e4 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -32,6 +34,7 @@ * 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 c382ba17476..f1e36757d05 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 @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -38,6 +40,7 @@ * @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 70401906a8c..f3bfa2105ce 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 @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; @@ -43,6 +45,7 @@ * * @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 4b3d4186c81..5d1454606bb 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.text.StringEscapeUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -46,6 +48,7 @@ * @see StringEscapeUtils#escapeHtml4(String) (Commons Lang) * @since 2.3.3 */ +@AutoService(Function.class) public class EscapeHtml extends AbstractFunction { private static final List 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 91c8d99641b..35ce8431aec 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -34,6 +36,7 @@ * 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 4dce679a768..65582ba2789 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.text.StringEscapeUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -43,6 +45,7 @@ * @see StringEscapeUtils#escapeXml10(String) (Commons Lang) * @since 3.2 */ +@AutoService(Function.class) public class EscapeXml extends AbstractFunction { private static final List 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 33c90be05f4..59bb485c367 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -34,6 +36,7 @@ * Returns: the evaluated value * @since 2.3.1 */ +@AutoService(Function.class) public class EvalFunction extends AbstractFunction { private static final List 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 4d3521e81e3..25bc47b3442 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -37,6 +39,7 @@ * 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 519fb699193..5b8132faf17 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 @@ -23,6 +23,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.io.FileUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -50,6 +52,7 @@ * * @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 a26b482643f..f73561956f8 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 @@ -23,6 +23,8 @@ import java.util.Iterator; import java.util.Map; +import com.google.auto.service.AutoService; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +36,7 @@ * 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 5e23c7b5a36..25427d696b2 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 @@ -29,6 +29,8 @@ import javax.script.Bindings; import javax.script.ScriptEngine; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -46,6 +48,7 @@ * 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 92019be77c7..b740aee9cdb 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -33,6 +35,7 @@ * @see LongSum * @since 1.8.1 */ +@AutoService(Function.class) public class IntSum extends AbstractFunction { private static final List 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 eb86e50151f..2dcc99932db 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -31,6 +33,7 @@ * * @since 4.0 */ +@AutoService(Function.class) public class IsPropDefined extends AbstractFunction { private static final List 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 edb8515e6a9..fca7f30b54a 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -32,6 +34,7 @@ * * @since 4.0 */ +@AutoService(Function.class) public class IsVarDefined extends AbstractFunction { private static final List 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 a5c730b905f..9929c71fd3e 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 @@ -22,6 +22,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -34,6 +36,7 @@ * or globally. * @since 1.X */ +@AutoService(Function.class) public class IterationCounter extends AbstractFunction implements ThreadListener { private static final List 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 00ea98bc6f7..6f1ac530374 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 @@ -27,6 +27,8 @@ import javax.script.ScriptEngineManager; import javax.script.SimpleScriptContext; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -44,6 +46,7 @@ * 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 7b6c185b27e..12e0f35109b 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.jexl2.JexlContext; import org.apache.commons.jexl2.JexlEngine; import org.apache.commons.jexl2.MapContext; @@ -41,6 +43,7 @@ * @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 ac74281d479..506f43b3d7d 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.jexl3.JexlBuilder; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; @@ -42,6 +44,7 @@ * @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 cd729cb850d..1411a9fc040 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -45,6 +47,7 @@ * 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 9f45af1ae44..066cb24c395 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -43,6 +45,7 @@ * 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 37a8e8de517..e18976d2593 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -32,6 +34,7 @@ * @see IntSum * @since 2.3.2 */ +@AutoService(Function.class) public class LongSum extends AbstractFunction { private static final List 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 82acc7183b6..0bfda70457f 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 @@ -17,12 +17,15 @@ package org.apache.jmeter.functions; +import com.google.auto.service.AutoService; + import org.apache.jmeter.util.JMeterUtils; /** * 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 50898adff74..f675e5e45b6 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 @@ -17,12 +17,15 @@ package org.apache.jmeter.functions; +import com.google.auto.service.AutoService; + import org.apache.jmeter.util.JMeterUtils; /** * 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 b2168a307fa..273afde4927 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -41,6 +43,7 @@ * - the property name itself * @since 2.0 */ +@AutoService(Function.class) public class Property extends AbstractFunction { private static final List 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 701dd0fd46a..29a2f76ae2f 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -46,6 +48,7 @@ * not present - "1" (suitable for use in ThreadGroup GUI) * @since 2.0 */ +@AutoService(Function.class) public class Property2 extends AbstractFunction { private static final List 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 20921a187d1..b7cc6c1609d 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 @@ -22,6 +22,8 @@ import java.util.List; import java.util.concurrent.ThreadLocalRandom; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -33,6 +35,7 @@ * (first argument) and a max (second argument). * @since 1.9 */ +@AutoService(Function.class) public class Random extends AbstractFunction { private static final List 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 83243d76f4e..c560d15bdfd 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 @@ -30,6 +30,8 @@ import java.util.Locale; import java.util.concurrent.ThreadLocalRandom; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; @@ -59,6 +61,7 @@ * * @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 7a22beca1e8..7d3c9661b1d 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 @@ -22,6 +22,8 @@ import java.util.List; import java.util.concurrent.ThreadLocalRandom; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -41,6 +43,7 @@ * * @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 1dd00483928..718993f33bb 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; @@ -36,6 +38,7 @@ * 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 a3c50e2b4e6..a4c34e863b3 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 @@ -25,6 +25,8 @@ import java.util.regex.Matcher; import java.util.regex.PatternSyntaxException; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -46,6 +48,7 @@ // @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 eedb707b96f..3b94ce0b8bb 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -31,6 +33,7 @@ * 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 7811c5686c1..28546faa81e 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 @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -40,6 +42,7 @@ * 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 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 17e8bb10120..87765dc4317 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 @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -53,6 +55,7 @@ * * @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 fc2207feeeb..4adaca7ea4e 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 @@ -26,6 +26,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.io.IOUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -70,6 +72,7 @@ * 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 f93b7538979..ec0eaaf0aa3 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 @@ -31,6 +31,8 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; +import com.google.auto.service.AutoService; + import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; @@ -54,6 +56,7 @@ * * @since 5.2 */ +@AutoService(Function.class) public class StringToFile extends AbstractFunction { private static final Logger log = LoggerFactory.getLogger(StringToFile.class); private static final List 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 c0ab9e8a943..d5dccfc4771 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -30,6 +32,7 @@ * Returns Test Plan name * @since 2.6 */ +@AutoService(Function.class) public class TestPlanName extends AbstractFunction { private static final List 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 e465f000ae7..2e99bb2fe0f 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 @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; import org.apache.jmeter.threads.AbstractThreadGroup; @@ -31,6 +33,7 @@ * * @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 0c353c32e6c..d19f5d9295b 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 @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -29,6 +31,7 @@ * 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 65e6c01a9dd..44c0a74896c 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 @@ -27,6 +27,8 @@ import java.util.Map; import java.util.regex.Pattern; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -41,6 +43,7 @@ * __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 131d360daea..c90ad27e7f9 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 @@ -31,6 +31,8 @@ import java.util.List; import java.util.Locale; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.engine.util.CompoundVariable; @@ -65,6 +67,7 @@ * * @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 910c4bb48f6..e7c5750a834 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 @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.text.StringEscapeUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -36,6 +38,7 @@ * @see StringEscapeUtils#unescapeJava(String) * @since 2.3.3 */ +@AutoService(Function.class) public class UnEscape extends AbstractFunction { private static final List 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 97a8bafa9c7..3dc47032158 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 @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.commons.text.StringEscapeUtils; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; @@ -42,6 +44,7 @@ * @see StringEscapeUtils#unescapeHtml4(String) * @since 2.3.3 */ +@AutoService(Function.class) public class UnEscapeHtml extends AbstractFunction { private static final List 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 a31f1aa2a40..70341d1137e 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 @@ -25,6 +25,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -35,6 +37,7 @@ * * @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 410ac89a585..6817e4cfc71 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 @@ -25,6 +25,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -35,6 +37,7 @@ * * @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 16461968ccd..83e9bca3708 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 @@ -22,6 +22,8 @@ import java.util.List; import java.util.UUID; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -36,6 +38,7 @@ * - A pseudo random UUID 4 * @since 2.9 */ +@AutoService(Function.class) public class Uuid extends AbstractFunction { private static final List 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 18ee52db415..ae6290bbc89 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 @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -39,6 +41,7 @@ * - 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 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 1200ccc55a1..a792999f8ee 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 @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; +import com.google.auto.service.AutoService; + import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; @@ -45,6 +47,7 @@ * 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 00000000000..9e3e38ebecc --- /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.* + +class FunctionServicesTest { + @Test + fun `__counter loads`() { + val functions = JMeterUtils.loadServices( + 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 7d04e1374e0..651e87e6866 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 @@ -352,7 +352,7 @@ private static String fixClassName(String strClassName) { } - private static void findClassesInOnePath(File file, Set listClasses, ClassFilter filter) throws IOException { + private static void findClassesInOnePath(File file, Set listClasses, ClassFilter filter) { if (file.isDirectory()) { findClassesInPathsDir(file.getAbsolutePath(), file, listClasses, filter); } else if (file.exists()) { @@ -371,7 +371,7 @@ private static void findClassesInOnePath(File file, Set listClasses, Cla } - private static void findClassesInPathsDir(String strPathElement, File dir, Set listClasses, ClassFilter filter) throws IOException { + private static void findClassesInPathsDir(String strPathElement, File dir, Set listClasses, ClassFilter filter) { File[] list = dir.listFiles(); if (list == null) { log.warn("{} is not a folder", dir.getAbsolutePath()); diff --git a/src/jorphan/src/main/java/org/apache/jorphan/reflect/CollectServiceLoadExceptionHandler.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/CollectServiceLoadExceptionHandler.java new file mode 100644 index 00000000000..ef0fae94074 --- /dev/null +++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/CollectServiceLoadExceptionHandler.java @@ -0,0 +1,36 @@ +/* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class CollectServiceLoadExceptionHandler implements ServiceLoadExceptionHandler { + private final List> failures = new ArrayList<>(); + + @Override + public void handle(Class service, String className, Throwable throwable) { + failures.add(new ServiceLoadFailure<>(service, className, throwable)); + } + + public Collection> toCollection() { + return Collections.unmodifiableCollection(failures); + } +} diff --git a/src/jorphan/src/main/java/org/apache/jorphan/reflect/IgnoreServiceLoadExceptionHandler.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/IgnoreServiceLoadExceptionHandler.java new file mode 100644 index 00000000000..fe7b4456cae --- /dev/null +++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/IgnoreServiceLoadExceptionHandler.java @@ -0,0 +1,24 @@ +/* + * 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; + +public class IgnoreServiceLoadExceptionHandler implements ServiceLoadExceptionHandler { + @Override + public void handle(Class service, String className, Throwable throwable) { + } +} 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 00000000000..712ef0d76b0 --- /dev/null +++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/LogAndIgnoreServiceLoadExceptionHandler.java @@ -0,0 +1,50 @@ +/* + * 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; + +public class LogAndIgnoreServiceLoadExceptionHandler implements ServiceLoadExceptionHandler { + 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/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadExceptionHandler.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadExceptionHandler.java new file mode 100644 index 00000000000..08799e63575 --- /dev/null +++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadExceptionHandler.java @@ -0,0 +1,23 @@ +/* + * 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; + +@FunctionalInterface +public interface ServiceLoadExceptionHandler { + void handle(Class service, String className, Throwable throwable); +} diff --git a/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadFailure.java b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadFailure.java new file mode 100644 index 00000000000..825f39aeb6f --- /dev/null +++ b/src/jorphan/src/main/java/org/apache/jorphan/reflect/ServiceLoadFailure.java @@ -0,0 +1,51 @@ +/* + * 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; + +public class ServiceLoadFailure { + private final Class service; + private final String className; + private final Throwable throwable; + + public ServiceLoadFailure(Class service, String className, Throwable throwable) { + this.service = service; + this.className = className; + this.throwable = throwable; + } + + public Class getService() { + return service; + } + + public String getClassName() { + return className; + } + + public Throwable getThrowable() { + return throwable; + } + + @Override + public String toString() { + return "ServiceLoadFailure{" + + "service=" + service + + ", className='" + className + '\'' + + ", throwable=" + throwable + + '}'; + } +} 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 0a0790ee02d..1461c5b5953 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 @@ -36,6 +36,8 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; +import com.google.auto.service.AutoService; + import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.config.Argument; import org.apache.jmeter.gui.util.HeaderAsPropertyRenderer; @@ -59,6 +61,7 @@ /** * 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/test-services/build.gradle.kts b/src/test-services/build.gradle.kts new file mode 100644 index 00000000000..9efcceed316 --- /dev/null +++ b/src/test-services/build.gradle.kts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +plugins { + id("build-logic.jvm-library") +} + +dependencies { + implementation(projects.src.jorphan) + implementation(projects.src.core) +} diff --git a/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceNotImplementingInterface.kt b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceNotImplementingInterface.kt new file mode 100644 index 00000000000..e70ff8f82c0 --- /dev/null +++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceNotImplementingInterface.kt @@ -0,0 +1,25 @@ +/* + * 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.util.services + +import com.google.auto.service.AutoService + +@AutoService(NotImplementedInterface::class) +public class ServiceNotImplementingInterface + +public interface NotImplementedInterface diff --git a/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceThrowingException.kt b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceThrowingException.kt new file mode 100644 index 00000000000..7c16a54ae6c --- /dev/null +++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceThrowingException.kt @@ -0,0 +1,31 @@ +/* + * 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.util.services + +import com.google.auto.service.AutoService + +@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/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceWithPrivateConstructor.kt b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceWithPrivateConstructor.kt new file mode 100644 index 00000000000..2710984ea71 --- /dev/null +++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/ServiceWithPrivateConstructor.kt @@ -0,0 +1,25 @@ +/* + * 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.util.services + +import com.google.auto.service.AutoService + +@AutoService(ServiceWithPrivateConstructorInterface::class) +public class ServiceWithPrivateConstructor private constructor() : ServiceWithPrivateConstructorInterface + +public interface ServiceWithPrivateConstructorInterface diff --git a/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/WorkableService.kt b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/WorkableService.kt new file mode 100644 index 00000000000..af134427d2d --- /dev/null +++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/WorkableService.kt @@ -0,0 +1,31 @@ +/* + * 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.util.services + +import com.google.auto.service.AutoService + +@AutoService(WorkableServiceInterface::class) +public class WorkableService : WorkableServiceInterface { + override val name: String = "test service" + + override fun toString(): String = name +} + +public interface WorkableServiceInterface { + public val name: String +} diff --git a/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/loadServices.kt b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/loadServices.kt new file mode 100644 index 00000000000..63dc7118af8 --- /dev/null +++ b/src/test-services/src/main/kotlin/org/apache/jmeter/util/services/loadServices.kt @@ -0,0 +1,33 @@ +/* + * 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.util.services + +import org.apache.jmeter.util.JMeterUtils +import org.apache.jorphan.reflect.ServiceLoadExceptionHandler +import java.util.ServiceLoader + +public inline fun loadServices( + exceptionHandler: ServiceLoadExceptionHandler +): Collection = + JMeterUtils.loadServices( + 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 00000000000..46b91630a16 --- /dev/null +++ b/src/test-services/src/test/kotlin/ServiceLoaderTest.kt @@ -0,0 +1,89 @@ +/* + * 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.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( + "[]", + "[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( + "[]", + "[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( + "[]", + "[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 `service loads`() { + assertServiceLoad( + "[test service]", + "[]" + ) + } + + private inline fun assertServiceLoad( + successMessage: String, + failureMessage: String, + ) { + val failures = CollectServiceLoadExceptionHandler() + val successes = loadServices(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" + } + } + ) + } +}