feat: use ServiceLoader to find implementations instead of searching classes in jars (#5885)

ServiceLoader is Java standard approach for locating implementaitons,
and it allows pluggability without relying on a filesystem layout.

Fixes https://github.com/apache/jmeter/issues/5883
This commit is contained in:
Vladimir Sitnikov 2023-05-11 16:20:17 +03:00 committed by GitHub
parent 5542be18d8
commit eedabdd6c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
186 changed files with 1547 additions and 286 deletions

View File

@ -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")
}

View File

@ -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.
*/
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<JavaCompile> {
// 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))
}
}
}
tasks.configureEach<Jar> {
manifest {
attributes["JMeter-Skip-Class-Scanning"] = "true"
}
}

View File

@ -18,4 +18,5 @@
plugins {
id("build-logic.java")
id("java-library")
id("build-logic.autoservice")
}

View File

@ -59,6 +59,7 @@ dependencies {
}
findProject(":src:bom-thirdparty")?.let{
api(platform(it))
annotationProcessor(platform(it))
}
}

View File

@ -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) {

View File

@ -25,6 +25,7 @@ plugins {
id("build-logic.test-base")
id("com.github.autostyle")
kotlin("jvm")
kotlin("kapt") apply false
}
dependencies {

View File

@ -27,4 +27,5 @@ dependencies {
api("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.88")
api("org.nosphere.apache.rat:org.nosphere.apache.rat.gradle.plugin:0.8.0")
api("org.jetbrains.gradle.plugin.idea-ext:org.jetbrains.gradle.plugin.idea-ext.gradle.plugin:1.1.7")
api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.21")
}

View File

@ -26,6 +26,7 @@ plugins {
id("com.github.vlsi.ide")
id("org.nosphere.apache.rat")
id("org.jetbrains.gradle.plugin.idea-ext")
kotlin("jvm") apply false
}
ide {

View File

@ -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"
)
}
}

View File

@ -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

View File

@ -76,6 +76,7 @@ include(
"src:release",
"src:testkit",
"src:testkit-wiremock",
"src:test-services",
"src:dist",
"src:dist-check"
)

View File

@ -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")

View File

@ -19,6 +19,7 @@ package org.apache.jmeter.extractor.json.render;
import org.apache.jmeter.extractor.json.jmespath.JMESPathCache;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.ResultRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -26,11 +27,13 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for JMES Path tester
* @since 5.2
*/
@AutoService(ResultRenderer.class)
public class RenderAsJmesPathRenderer extends AbstractRenderAsJsonRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsJmesPathRenderer.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

View File

@ -21,13 +21,17 @@ import java.util.List;
import org.apache.jmeter.extractor.json.jsonpath.JSONManager;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.ResultRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for JSON Path tester
* @since 3.0
*/
@AutoService(ResultRenderer.class)
public class RenderAsJsonRenderer extends AbstractRenderAsJsonRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsJsonRenderer.class);

View File

@ -53,10 +53,16 @@ import org.apache.jorphan.gui.ComponentUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Export transactions names for web report
* @since 3.3
*/
@AutoService({
Command.class,
MenuCreator.class
})
public class ExportTransactionAndSamplerNames extends AbstractAction implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(ExportTransactionAndSamplerNames.class);

View File

@ -41,9 +41,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.GuiUtils;
import org.apache.jorphan.gui.JLabeledTextField;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for Boundary Extractor tester
*/
@AutoService(ResultRenderer.class)
public class RenderAsBoundaryExtractor implements ResultRenderer, ActionListener {
private static final String BOUNDARY_EXTRACTOR_TESTER_COMMAND = "boundary_extractor_tester"; // $NON-NLS-1$

View File

@ -49,10 +49,13 @@ import org.apache.jorphan.gui.JLabeledChoice;
import org.apache.jorphan.gui.JLabeledTextField;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for CSS/JQuery tester
* @since 2.10
*/
@AutoService(ResultRenderer.class)
public class RenderAsCssJQuery implements ResultRenderer, ActionListener {
private static final String CSSJQUEY_TESTER_COMMAND = "cssjquery_tester"; // $NON-NLS-1$

View File

@ -23,6 +23,9 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsDocument extends SamplerResultTab implements ResultRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsDocument.class);

View File

@ -33,6 +33,9 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsHTML extends SamplerResultTab implements ResultRenderer {
private static final Logger log = LoggerFactory.getLogger(RenderAsHTML.class);

View File

@ -21,6 +21,9 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.jsoup.Jsoup;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsHTMLFormatted extends SamplerResultTab implements ResultRenderer {
/** {@inheritDoc} */

View File

@ -20,6 +20,9 @@ package org.apache.jmeter.visualizers;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsHTMLWithEmbedded extends RenderAsHTML
implements ResultRenderer {

View File

@ -29,6 +29,9 @@ import net.minidev.json.JSONStyle;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsJSON extends SamplerResultTab implements ResultRenderer {
private static final String TAB_SEPARATOR = " "; //$NON-NLS-1$

View File

@ -50,9 +50,12 @@ import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for Regexp tester
*/
@AutoService(ResultRenderer.class)
public class RenderAsRegexp implements ResultRenderer, ActionListener {
private static final String REGEXP_TESTER_COMMAND = "regexp_tester"; // $NON-NLS-1$

View File

@ -20,6 +20,9 @@ package org.apache.jmeter.visualizers;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsText extends SamplerResultTab implements ResultRenderer {
/** {@inheritDoc} */

View File

@ -53,6 +53,9 @@ import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
import org.xml.sax.SAXException;
import com.google.auto.service.AutoService;
@AutoService(ResultRenderer.class)
public class RenderAsXML extends SamplerResultTab
implements ResultRenderer {

View File

@ -59,10 +59,13 @@ import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for XPath tester
*/
@AutoService(ResultRenderer.class)
public class RenderAsXPath implements ResultRenderer, ActionListener {
private static final Logger log = LoggerFactory.getLogger(RenderAsXPath.class);

View File

@ -48,9 +48,12 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implement ResultsRender for XPath tester
*/
@AutoService(ResultRenderer.class)
public class RenderAsXPath2 implements ResultRenderer, ActionListener {
private static final Logger log = LoggerFactory.getLogger(RenderAsXPath2.class);

View File

@ -27,6 +27,8 @@ import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import com.google.auto.service.AutoService;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
@ -43,6 +45,7 @@ import javafx.scene.web.WebView;
* {@link ResultRenderer} implementation that uses JAVAFX WebEngine to render as browser do
* @since 3.2
*/
@AutoService(ResultRenderer.class)
public class RenderInBrowser extends SamplerResultTab implements ResultRenderer {
private JFXPanel jfxPanel;

View File

@ -18,11 +18,9 @@
package org.apache.jmeter.visualizers;
import java.awt.BorderLayout;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.ServiceLoader;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
@ -30,6 +28,7 @@ import javax.swing.SwingConstants;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -52,37 +51,23 @@ public class RequestPanel {
*/
public RequestPanel() {
listRequestView = new ArrayDeque<>();
List<String> classesToAdd = Collections.<String> emptyList();
try {
classesToAdd = JMeterUtils.findClassesThatExtend(RequestView.class);
} catch (IOException e1) {
// ignored
}
String rawTab = JMeterUtils.getResString(RequestViewRaw.KEY_LABEL); // $NON-NLS-1$
Object rawObject = null;
for (String clazz : classesToAdd) {
try {
// Instantiate requestview classes
final RequestView requestView = Class.forName(clazz)
.asSubclass(RequestView.class)
.getDeclaredConstructor().newInstance();
if (rawTab.equals(requestView.getLabel())) {
rawObject = requestView; // use later
} else {
listRequestView.add(requestView);
}
}
catch (NoClassDefFoundError e) {
log.error("Exception registering implementation: [{}] of interface: [{}], a dependency used by the plugin class is missing",
clazz, RequestView.class, e);
} catch (Exception e) {
log.error("Exception registering implementation: [{}] of interface: [{}], a jar is probably missing",
clazz, RequestView.class, e);
RequestView rawObject = null;
for (RequestView requestView : JMeterUtils.loadServicesAndScanJars(
RequestView.class,
ServiceLoader.load(RequestView.class),
Thread.currentThread().getContextClassLoader(),
new LogAndIgnoreServiceLoadExceptionHandler(log)
)) {
if (rawTab.equals(requestView.getLabel())) {
rawObject = requestView; // use later
} else {
listRequestView.add(requestView);
}
}
// place raw tab in first position (first tab)
if (rawObject != null) {
listRequestView.addFirst((RequestView) rawObject);
listRequestView.addFirst(rawObject);
}
// Prepare the Request tabbed pane

View File

@ -19,12 +19,15 @@ package org.apache.jmeter.visualizers;
import javax.swing.JPanel;
import org.apache.jorphan.reflect.JMeterService;
/**
* Interface for request panel in View Results Tree
* All classes which implements this interface is display
* on bottom tab in request panel
*
*/
@JMeterService
public interface RequestView {
/**

View File

@ -30,10 +30,13 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.GuiUtils;
import com.google.auto.service.AutoService;
/**
* (historical) Panel to view request data
*
*/
@AutoService(RequestView.class)
public class RequestViewRaw implements RequestView {
// Used by Request Panel

View File

@ -22,11 +22,13 @@ import java.awt.Color;
import javax.swing.JTabbedPane;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.reflect.JMeterService;
/**
* Interface to results render
*/
@JMeterService
public interface ResultRenderer {
void clearData();

View File

@ -25,7 +25,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@ -36,6 +35,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
@ -73,6 +73,7 @@ import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jorphan.gui.JMeterUIDefaults;
import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.apache.jorphan.util.StringWrap;
import org.apiguardian.api.API;
import org.slf4j.Logger;
@ -454,38 +455,24 @@ implements ActionListener, TreeSelectionListener, Clearable, ItemListener {
selectRenderPanel.addActionListener(this);
// if no results render in jmeter.properties, load Standard (default)
List<String> classesToAdd = Collections.<String>emptyList();
try {
classesToAdd = JMeterUtils.findClassesThatExtend(ResultRenderer.class);
} catch (IOException e1) {
// ignored
}
String defaultRenderer = expandToClassname(".RenderAsText"); // $NON-NLS-1$
if (VIEWERS_ORDER.length() > 0) {
defaultRenderer = expandToClassname(VIEWERS_ORDER.split(",", 2)[0]);
}
Object defaultObject = null;
Map<String, ResultRenderer> map = new HashMap<>(classesToAdd.size());
for (String clazz : classesToAdd) {
try {
// Instantiate render classes
final ResultRenderer renderer = Class.forName(clazz)
.asSubclass(ResultRenderer.class)
.getDeclaredConstructor().newInstance();
if (defaultRenderer.equals(clazz)) {
defaultObject=renderer;
}
renderer.setBackgroundColor(getBackground());
map.put(renderer.getClass().getName(), renderer);
} catch (NoClassDefFoundError e) { // NOSONAR See bug 60583
if (e.getMessage() != null && e.getMessage().contains("javafx")) {
log.info("Add JavaFX to your Java installation if you want to use renderer: {}", clazz);
} else {
log.warn("Error loading result renderer: {}", clazz, e);
}
} catch (Exception e) {
log.warn("Error loading result renderer: {}", clazz, e);
ResultRenderer defaultObject = null;
Map<String, ResultRenderer> map = new HashMap<>();
for (ResultRenderer renderer : JMeterUtils.loadServicesAndScanJars(
ResultRenderer.class,
ServiceLoader.load(ResultRenderer.class),
Thread.currentThread().getContextClassLoader(),
new LogAndIgnoreServiceLoadExceptionHandler(log)
)) {
// Instantiate render classes
if (defaultRenderer.equals(renderer.getClass().getName())) {
defaultObject = renderer;
}
renderer.setBackgroundColor(getBackground());
map.put(renderer.getClass().getName(), renderer);
}
if (VIEWERS_ORDER.length() > 0) {
Arrays.stream(VIEWERS_ORDER.split(","))

View File

@ -24,6 +24,8 @@ import org.apache.jmeter.config.Arguments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* An abstract implementation of the BackendListenerClient interface. This
* implementation provides default implementations of most of the methods in the
@ -49,6 +51,7 @@ import org.slf4j.LoggerFactory;
* @see BackendListener#sampleOccurred(org.apache.jmeter.samplers.SampleEvent)
* @since 2.13
*/
@AutoService(BackendListenerClient.class)
public abstract class AbstractBackendListenerClient implements BackendListenerClient {
private static final Logger log = LoggerFactory.getLogger(AbstractBackendListenerClient.class);

View File

@ -21,6 +21,7 @@ import java.util.List;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.reflect.JMeterService;
/**
* This interface defines the interactions between the {@link BackendListener}
@ -63,6 +64,7 @@ import org.apache.jmeter.samplers.SampleResult;
*
* @since 2.13
*/
@JMeterService
public interface BackendListenerClient {
/**

View File

@ -20,11 +20,10 @@ package org.apache.jmeter.visualizers.backend;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import javax.swing.ComboBoxModel;
@ -33,7 +32,6 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
@ -45,7 +43,7 @@ import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractListenerGui;
import org.apache.jorphan.reflect.ClassFinder;
import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -77,7 +75,6 @@ public class BackendListenerGui extends AbstractListenerGui implements ActionLis
/** The current className of the Backend listener **/
private String className;
/**
* Create a new BackendListenerGui as a standalone component.
*/
@ -117,25 +114,18 @@ public class BackendListenerGui extends AbstractListenerGui implements ActionLis
* @return a panel containing the relevant components
*/
private JPanel createClassnamePanel() {
List<String> possibleClasses = new ArrayList<>();
try {
// Find all the classes which implement the BackendListenerClient
// interface.
possibleClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),
new Class[] { BackendListenerClient.class });
// Remove the BackendListener class from the list since it only
// implements the interface for error conditions.
possibleClasses.remove(BackendListener.class.getName() + "$ErrorBackendListenerClient");
} catch (Exception e) {
log.debug("Exception getting interfaces.", e);
}
JLabel label = new JLabel(JMeterUtils.getResString("backend_listener_classname")); // $NON-NLS-1$
classnameCombo = new JComboBox<>(possibleClasses.toArray(ArrayUtils.EMPTY_STRING_ARRAY));
String[] listenerClasses = JMeterUtils.loadServicesAndScanJars(
BackendListenerClient.class,
ServiceLoader.load(BackendListenerClient.class),
Thread.currentThread().getContextClassLoader(),
new LogAndIgnoreServiceLoadExceptionHandler(log)
).stream()
.map(s -> s.getClass().getName())
.sorted()
.toArray(String[]::new);
classnameCombo = new JComboBox<>(listenerClasses);
classnameCombo.addActionListener(this);
classnameCombo.setEditable(false);
label.setLabelFor(classnameCombo);

View File

@ -37,17 +37,21 @@ import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.apache.jmeter.visualizers.backend.SamplerMetric;
import org.apache.jmeter.visualizers.backend.UserMetric;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Graphite based Listener using Pickle Protocol
* @see <a href="http://graphite.readthedocs.org/en/latest/overview.html">Graphite Overview</a>
* @since 2.13
*/
@AutoService(BackendListenerClient.class)
public class GraphiteBackendListenerClient extends AbstractBackendListenerClient implements Runnable {
//+ Argument names

View File

@ -29,6 +29,8 @@ import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implementation of {@link BackendListenerClient} to write the response times
* of every sample to InfluxDB. If more "raw" information is required in InfluxDB
@ -38,6 +40,7 @@ import org.slf4j.LoggerFactory;
*
* @since 5.3
*/
@AutoService(BackendListenerClient.class)
public class InfluxDBRawBackendListenerClient implements BackendListenerClient {
private static final Logger log = LoggerFactory.getLogger(InfluxDBRawBackendListenerClient.class);

View File

@ -36,6 +36,7 @@ import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import org.apache.jmeter.visualizers.backend.ErrorMetric;
import org.apache.jmeter.visualizers.backend.SamplerMetric;
@ -43,12 +44,15 @@ import org.apache.jmeter.visualizers.backend.UserMetric;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implementation of {@link AbstractBackendListenerClient} to write to InfluxDB
* using a custom schema; since JMeter 5.2, this also support the InfluxDB v2.
*
* @since 3.2
*/
@AutoService(BackendListenerClient.class)
public class InfluxdbBackendListenerClient extends AbstractBackendListenerClient implements Runnable {
private static final Logger log = LoggerFactory.getLogger(InfluxdbBackendListenerClient.class);

View File

@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import org.apache.jmeter.functions.Function;
import org.apache.jmeter.functions.InvalidVariableException;
@ -30,7 +31,7 @@ import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.reflect.ClassFinder;
import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -69,15 +70,15 @@ public class CompoundVariable implements Function {
log.info("Note: Function class names must not contain the string: '{}'", notContain);
}
List<String> classes = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(),
new Class[] { Function.class }, true, contain, notContain);
for (String clazzName : classes) {
Function tempFunc = Class.forName(clazzName)
.asSubclass(Function.class)
.getDeclaredConstructor().newInstance();
String referenceKey = tempFunc.getReferenceKey();
for (Function function : JMeterUtils.loadServicesAndScanJars(
Function.class,
ServiceLoader.load(Function.class),
Thread.currentThread().getContextClassLoader(),
new LogAndIgnoreServiceLoadExceptionHandler(log)
)) {
String referenceKey = function.getReferenceKey();
if (referenceKey.length() > 0) { // ignore self
functions.put(referenceKey, tempFunc.getClass());
functions.put(referenceKey, function.getClass());
}
}

View File

@ -23,18 +23,20 @@ import java.util.List;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jorphan.reflect.JMeterService;
/**
* Methods that a function must implement
*/
@JMeterService
public interface Function {
/**
* Given the previous SampleResult and the current Sampler, return a string
* to use as a replacement value for the function call. Assume
* "setParameter" was previously called.
*
* This method must be threadsafe - multiple threads will be using the same
* object.
* <p>This method must be thread-safe - multiple threads will be using the same
* object.</p>
* @param previousResult The previous {@link SampleResult}
* @param currentSampler The current {@link Sampler}
* @return The replacement value, which was generated by the function

View File

@ -30,9 +30,16 @@ import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.gui.action.AbstractAction;
import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.action.Command;
import org.apache.jmeter.gui.plugin.MenuCreator;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
@AutoService({
Command.class,
MenuCreator.class
})
public class HtmlReportAction extends AbstractAction implements MenuCreator {
private static final Set<String> commands = new HashSet<>();
private HtmlReportUI htmlReportPanel;

View File

@ -42,10 +42,13 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.util.EscapeDialog;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
/**
* About Command. It may be extended in the future to add a list of installed
* protocols, config options, etc.
*/
@AutoService(Command.class)
public class AboutCommand extends AbstractAction {
private static final Set<String> commandSet;

View File

@ -309,6 +309,7 @@ public final class ActionRouter implements ActionListener {
}
}
@SuppressWarnings("deprecation")
private static List<String> findClassesThatExtend(String className, String excluding, String[] searchPath) throws IOException, ClassNotFoundException {
return ClassFinder.findClassesThatExtend(

View File

@ -28,9 +28,12 @@ import org.apache.jmeter.testelement.TestElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implements the Add Parent menu command
*/
@AutoService(Command.class)
public class AddParent extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(AddParent.class);

View File

@ -33,10 +33,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Add ThinkTime (TestAction + UniformRandomTimer)
* @since 3.2
*/
@AutoService(Command.class)
public class AddThinkTimeBetweenEachStep extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(AddThinkTimeBetweenEachStep.class);

View File

@ -32,6 +32,9 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
@AutoService(Command.class)
public class AddToTree extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(AddToTree.class);

View File

@ -32,10 +32,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Allows to apply naming convention on nodes
* @since 3.2
*/
@AutoService(Command.class)
public class ApplyNamingConvention extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(ApplyNamingConvention.class);

View File

@ -28,9 +28,12 @@ import org.apache.jorphan.util.JMeterError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Change language
*/
@AutoService(Command.class)
public class ChangeLanguage extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();

View File

@ -38,9 +38,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Allows to change Controller implementation
*/
@AutoService(Command.class)
public class ChangeParent extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(ChangeParent.class);

View File

@ -33,10 +33,13 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Check if the TestPlan has been changed since it was last saved
*
*/
@AutoService(Command.class)
public class CheckDirty extends AbstractAction implements HashTreeTraverser, ActionListener {
private static final Logger log = LoggerFactory.getLogger(CheckDirty.class);

View File

@ -28,12 +28,15 @@ import org.apache.jmeter.samplers.Clearable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Handles the following actions:
* - Clear (Data)
* - Clear All (Data)
* - Reset (Clear GUI)
*/
@AutoService(Command.class)
public class Clear extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Clear.class);

View File

@ -29,11 +29,14 @@ import org.apache.jmeter.gui.util.FocusRequester;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
/**
* This command clears the existing test plan, allowing the creation of a New
* test plan.
*
*/
@AutoService(Command.class)
public class Close extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();

View File

@ -25,10 +25,13 @@ import javax.swing.JTree;
import org.apache.jmeter.gui.GuiPackage;
import com.google.auto.service.AutoService;
/**
* Processes the Collapse All and Expand All options.
*
*/
@AutoService(Command.class)
public class CollapseExpand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -30,9 +30,12 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import com.google.auto.service.AutoService;
/**
* Processes the collapse and expand of a tree branch
*/
@AutoService(Command.class)
public class CollapseExpandTreeBranch extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -38,10 +38,16 @@ import org.apache.jorphan.collections.HashTreeTraverser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Compile JSR223 Test Element that use Compilable script language
* @since 5.1
*/
@AutoService({
Command.class,
MenuCreator.class
})
public class CompileJSR223TestElements extends AbstractAction implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(CompileJSR223TestElements.class);

View File

@ -37,9 +37,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implements the Copy menu command
*/
@AutoService(Command.class)
public class Copy extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Copy.class);

View File

@ -23,6 +23,9 @@ import java.util.Set;
import org.apache.jmeter.functions.gui.FunctionHelper;
import com.google.auto.service.AutoService;
@AutoService(Command.class)
public class CreateFunctionDialog extends AbstractAction {
private static final Set<String> commands;

View File

@ -24,9 +24,12 @@ import java.util.Set;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import com.google.auto.service.AutoService;
/**
* Implements the Cut menu item command
*/
@AutoService(Command.class)
public class Cut extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -26,9 +26,12 @@ import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import com.google.auto.service.AutoService;
/**
* Implements the Duplicate menu command
*/
@AutoService(Command.class)
public class Duplicate extends AbstractAction {
private static final HashSet<String> commands = new HashSet<>();

View File

@ -25,9 +25,12 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.JMeterGUIComponent;
import org.apache.jorphan.gui.ui.TextComponentUI;
import com.google.auto.service.AutoService;
/**
* Implements the Edit menu item.
*/
@AutoService(Command.class)
public class EditCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -26,9 +26,12 @@ import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implements the Enable menu item.
*/
@AutoService(Command.class)
public class EnableComponent extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(EnableComponent.class);

View File

@ -26,6 +26,9 @@ import javax.swing.JOptionPane;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
@AutoService(Command.class)
public class ExitCommand extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();

View File

@ -35,9 +35,12 @@ import org.apache.jorphan.gui.ComponentUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implements the Help menu item.
*/
@AutoService(Command.class)
public class Help extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Help.class);
private static final boolean USE_LOCAL_HELP =

View File

@ -42,6 +42,7 @@ import org.apache.jorphan.collections.HashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
@ -49,6 +50,7 @@ import com.thoughtworks.xstream.io.StreamException;
* Handles the Open (load a new file) and Merge commands.
*
*/
@AutoService(Command.class)
public class Load extends AbstractActionWithNoRunningTest {
private static final Logger log = LoggerFactory.getLogger(Load.class);

View File

@ -34,10 +34,13 @@ import java.util.stream.IntStream;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import com.google.auto.service.AutoService;
/**
* Handles the loading of recent files, and also the content and
* visibility of menu items for loading the recent files
*/
@AutoService(Command.class)
public class LoadRecentProject extends Load {
/** Prefix for the user preference key */
private static final String USER_PREFS_KEY = "recent_file_"; //$NON-NLS-1$

View File

@ -29,10 +29,13 @@ import org.apiguardian.api.API;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Implements log level setting menu item.
* @since 3.2
*/
@AutoService(Command.class)
public class LogLevelCommand extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(LogLevelCommand.class);

View File

@ -26,10 +26,13 @@ import javax.swing.UIManager;
import org.apache.jmeter.gui.GuiPackage;
import com.google.auto.service.AutoService;
/**
* Hide / unhide LoggerPanel.
*
*/
@AutoService(Command.class)
public class LoggerPanelEnableDisable extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -40,10 +40,12 @@ import org.apache.jorphan.gui.JFactory;
import com.github.weisj.darklaf.LafManager;
import com.github.weisj.darklaf.theme.DarculaTheme;
import com.github.weisj.darklaf.theme.Theme;
import com.google.auto.service.AutoService;
/**
* Implements the Look and Feel menu item.
*/
@AutoService(Command.class)
public class LookAndFeelCommand extends AbstractAction {
private static final String JMETER_LAF = "jmeter.laf"; // $NON-NLS-1$

View File

@ -33,10 +33,13 @@ import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import com.google.auto.service.AutoService;
/**
* Move a node up/down/left/right
*
*/
@AutoService(Command.class)
public class Move extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -29,6 +29,9 @@ import javax.swing.JOptionPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
@AutoService(Command.class)
public class OpenLinkAction extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(OpenLinkAction.class);

View File

@ -33,9 +33,12 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Places a copied JMeterTreeNode under the selected node.
*/
@AutoService(Command.class)
public class Paste extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Paste.class);

View File

@ -33,6 +33,9 @@ import org.apache.jmeter.threads.RemoteThreadsListenerTestElement;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import com.google.auto.service.AutoService;
@AutoService(Command.class)
public class RemoteStart extends AbstractAction {
private static final String LOCAL_HOST = "127.0.0.1"; // NOSONAR $NON-NLS-1$

View File

@ -29,9 +29,12 @@ import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
/**
* Implements the Remove menu item.
*/
@AutoService(Command.class)
public class Remove extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -26,9 +26,12 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.Searchable;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import com.google.auto.service.AutoService;
/**
* Reset Search
*/
@AutoService(Command.class)
public class ResetSearchCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -39,11 +39,17 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Restart JMeter
* Based on https://dzone.com/articles/programmatically-restart-java
* @since 5.0
*/
@AutoService({
Command.class,
MenuCreator.class
})
public class Restart extends AbstractActionWithNoRunningTest implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(Restart.class);

View File

@ -27,10 +27,13 @@ import javax.swing.JOptionPane;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.JMeterUtils;
import com.google.auto.service.AutoService;
/**
* Handles the Revert Project command.
*
*/
@AutoService(Command.class)
public class RevertProject extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();

View File

@ -32,6 +32,8 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.SSLManager;
import com.google.auto.service.AutoService;
//
/**
* SSL Manager Command. The SSL Manager provides a mechanism to change your
@ -52,6 +54,7 @@ import org.apache.jmeter.util.SSLManager;
* already defined via the property.
*
*/
@AutoService(Command.class)
public class SSLManagerCommand extends AbstractAction {
private static final Set<String> commandSet;
static {

View File

@ -56,12 +56,15 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Save the current test plan; implements:
* Save
* Save TestPlan As
* Save (Selection) As
*/
@AutoService(Command.class)
public class Save extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Save.class);

View File

@ -23,11 +23,14 @@ import java.util.Set;
import org.apache.jmeter.gui.GuiPackage;
import com.google.auto.service.AutoService;
/**
* Save Before Run Action To save test plan before GUI execution
*
* @since 4.0
*/
@AutoService(Command.class)
public class SaveBeforeRun extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -34,6 +34,8 @@ import org.apache.jmeter.save.SaveGraphicsService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.Printable;
import com.google.auto.service.AutoService;
/**
* SaveGraphics action is meant to be a generic reusable Action. The class will
* use GUIPackage to get the current gui. Once it does, it checks to see if the
@ -42,6 +44,7 @@ import org.apache.jmeter.visualizers.Printable;
* file if no extension is provided. If either .png or .tif is in the filename,
* it will call SaveGraphicsService to save in the format.
*/
@AutoService(Command.class)
public class SaveGraphics extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -47,10 +47,16 @@ import org.apache.jorphan.collections.HashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Schematic view of Test Plan
* @since 5.1
*/
@AutoService({
Command.class,
MenuCreator.class
})
public class SchematicView extends AbstractAction implements MenuCreator {
private static final Logger log = LoggerFactory.getLogger(SchematicView.class);
private static final String DEFAULT_XSL_FILE =

View File

@ -23,10 +23,13 @@ import java.util.Set;
import javax.swing.JFrame;
import com.google.auto.service.AutoService;
/**
* Search nodes for a text
* TODO Enhance search dialog to select kind of nodes ....
*/
@AutoService(Command.class)
public class SearchTreeCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -45,6 +45,8 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* Set of Actions to:
* <ul>
@ -57,6 +59,7 @@ import org.slf4j.LoggerFactory;
* <li>Validate a set of Thread Groups with/without sleeping on the timers depending on jmeter properties</li>
* </ul>
*/
@AutoService(Command.class)
public class Start extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(Start.class);

View File

@ -26,10 +26,13 @@ import java.util.Set;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.Stoppable;
import com.google.auto.service.AutoService;
/**
* Stops stopables (Proxy, Mirror)
* @since 2.5.1
*/
@AutoService(Command.class)
public class StopStoppables extends AbstractAction implements ActionListener {
private static final Set<String> commands = new HashSet<>();

View File

@ -21,10 +21,13 @@ import java.awt.event.ActionEvent;
import java.util.HashSet;
import java.util.Set;
import com.google.auto.service.AutoService;
/**
* Open Templates
* @since 2.10
*/
@AutoService(Command.class)
public class TemplatesCommand extends AbstractActionWithNoRunningTest {
private static final Set<String> commands = new HashSet<>();

View File

@ -26,10 +26,13 @@ import org.apache.jmeter.exceptions.IllegalUserActionException;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jorphan.collections.HashTree;
import com.google.auto.service.AutoService;
/**
* Menu command to serve Undo/Redo
* @since 2.12
*/
@AutoService(Command.class)
public class UndoCommand extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -35,6 +35,8 @@ import org.apache.logging.log4j.core.config.Configurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
*
* Debug class to show details of the currently selected object
@ -43,6 +45,7 @@ import org.slf4j.LoggerFactory;
* Also enables/disables debug for the test element.
*
*/
@AutoService(Command.class)
public class What extends AbstractAction {
private static final Logger log = LoggerFactory.getLogger(What.class);

View File

@ -24,10 +24,13 @@ import java.util.Set;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.JMeterUIDefaults;
import com.google.auto.service.AutoService;
/**
* Zoom IN/OUT
* @since 3.2
*/
@AutoService(Command.class)
public class ZoomInOut extends AbstractAction {
private static final Set<String> commands = new HashSet<>();

View File

@ -21,9 +21,12 @@ import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.MenuElement;
import org.apache.jorphan.reflect.JMeterService;
/**
* @since 2.10
*/
@JMeterService
public interface MenuCreator {
enum MENU_LOCATION {
FILE,

View File

@ -19,14 +19,13 @@ package org.apache.jmeter.gui.util;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.ServiceLoader;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
@ -54,7 +53,7 @@ import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.LocaleChangeEvent;
import org.apache.jmeter.util.LocaleChangeListener;
import org.apache.jmeter.util.SSLManager;
import org.apache.jorphan.reflect.ClassFinder;
import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.logging.log4j.Level;
import org.slf4j.Logger;
@ -92,7 +91,13 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
private JMenu remoteExit;
private final Collection<JMenuItem> remoteEngineExit;
private JMenu searchMenu;
private List<MenuCreator> menuCreators;
private final Collection<MenuCreator> menuCreators =
JMeterUtils.loadServicesAndScanJars(
MenuCreator.class,
ServiceLoader.load(MenuCreator.class),
Thread.currentThread().getContextClassLoader(),
new LogAndIgnoreServiceLoadExceptionHandler(log)
);
public static final String SYSTEM_LAF = "System"; // $NON-NLS-1$
public static final String CROSS_PLATFORM_LAF = "CrossPlatform"; // $NON-NLS-1$
@ -192,9 +197,6 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
* should be defined in a file somewhere, but that is for later.
*/
public void createMenuBar() {
this.menuCreators = findMenuCreators();
makeFileMenu();
makeEditMenu();
makeRunMenu();
@ -217,35 +219,6 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
this.add(helpMenu);
}
private static List<MenuCreator> findMenuCreators() {
List<MenuCreator> creators = new ArrayList<>();
try {
List<String> listClasses = ClassFinder.findClassesThatExtend(
JMeterUtils.getSearchPaths(),
new Class[] {MenuCreator.class });
for (String strClassName : listClasses) {
try {
log.debug("Loading menu creator class: {}", strClassName);
Class<?> commandClass = Class.forName(strClassName);
if (!Modifier.isAbstract(commandClass.getModifiers())) {
log.debug("Instantiating: {}", commandClass);
MenuCreator creator = (MenuCreator) commandClass.getDeclaredConstructor().newInstance();
creators.add(creator);
}
} catch (NoClassDefFoundError e) {
log.error("Exception registering implementation: [{}] of interface: [{}], a dependency used by the plugin class is missing",
strClassName, MenuCreator.class, e);
} catch (Exception e) {
log.error("Exception registering implementation: [{}] of interface: [{}], a jar is probably missing",
strClassName, MenuCreator.class, e);
}
}
} catch (IOException e) {
log.error("Exception finding implementations of {}", MenuCreator.class, e);
}
return creators;
}
private void makeHelpMenu() {
helpMenu = makeMenuRes("help",'H'); //$NON-NLS-1$
@ -590,7 +563,7 @@ public class JMeterMenuBar extends JMenuBar implements LocaleChangeListener {
* @param menuCreators
* @param location
*/
private static void addPluginsMenuItems(JMenu menu, List<MenuCreator> menuCreators, MENU_LOCATION location) {
private static void addPluginsMenuItems(JMenu menu, Collection<MenuCreator> menuCreators, MENU_LOCATION location) {
for (MenuCreator menuCreator : menuCreators) {
JMenuItem[] menuItems = menuCreator.getMenuItemsAtLocation(location);
if (menuItems.length != 0) {

View File

@ -124,6 +124,8 @@ public final class MenuFactory {
private static void initializeMenus(
Map<String, List<MenuInfo>> menus, Set<String> elementsToSkip) {
try {
// TODO: migrate to ServiceLoader or something else
@SuppressWarnings("deprecation")
List<String> guiClasses = ClassFinder
.findClassesThatExtend(
JMeterUtils.getSearchPaths(),

View File

@ -17,10 +17,13 @@
package org.apache.jmeter.threads;
import org.apache.jorphan.reflect.JMeterService;
/**
* Interface notified when number of active threads changes
* @since 2.10
*/
@JMeterService
public interface RemoteThreadsLifeCycleListener {
/**

View File

@ -17,18 +17,16 @@
package org.apache.jmeter.threads;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.ServiceLoader;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.reflect.ClassFinder;
import org.apache.jorphan.reflect.LogAndIgnoreServiceLoadExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -39,7 +37,14 @@ import org.slf4j.LoggerFactory;
public class RemoteThreadsListenerImpl extends UnicastRemoteObject implements
RemoteThreadsListener, ThreadListener {
private static final Logger log = LoggerFactory.getLogger(RemoteThreadsListenerImpl.class);
private final List<RemoteThreadsLifeCycleListener> listeners = new ArrayList<>();
private final Collection<RemoteThreadsLifeCycleListener> listeners =
JMeterUtils.loadServicesAndScanJars(
RemoteThreadsLifeCycleListener.class,
ServiceLoader.load(RemoteThreadsLifeCycleListener.class),
Thread.currentThread().getContextClassLoader(),
new LogAndIgnoreServiceLoadExceptionHandler(log)
);
/**
*
@ -56,27 +61,6 @@ public class RemoteThreadsListenerImpl extends UnicastRemoteObject implements
*/
public RemoteThreadsListenerImpl() throws RemoteException {
super(DEFAULT_LOCAL_PORT, RmiUtils.createClientSocketFactory(), RmiUtils.createServerSocketFactory());
try {
List<String> listClasses = ClassFinder.findClassesThatExtend(
JMeterUtils.getSearchPaths(),
new Class[] {RemoteThreadsLifeCycleListener.class });
for (String strClassName : listClasses) {
try {
log.debug("Loading class: {}", strClassName);
Class<?> commandClass = Class.forName(strClassName);
if (!Modifier.isAbstract(commandClass.getModifiers())) {
log.debug("Instantiating: {}", commandClass);
RemoteThreadsLifeCycleListener listener = (RemoteThreadsLifeCycleListener) commandClass.getDeclaredConstructor().newInstance();
listeners.add(listener);
}
} catch (Exception e) {
log.error("Exception registering {} with implementation: {}", RemoteThreadsLifeCycleListener.class,
strClassName, e);
}
}
} catch (IOException e) {
log.error("Exception finding implementations of {}", RemoteThreadsLifeCycleListener.class, e);
}
}
private static int addOffset(int port, int offset) {

View File

@ -27,19 +27,25 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
@ -59,6 +65,7 @@ import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jorphan.gui.JFactory;
import org.apache.jorphan.gui.JMeterUIDefaults;
import org.apache.jorphan.reflect.ClassFinder;
import org.apache.jorphan.reflect.ServiceLoadExceptionHandler;
import org.apache.jorphan.test.UnitTestManager;
import org.apache.jorphan.util.JMeterError;
import org.apache.jorphan.util.JOrphanUtils;
@ -316,6 +323,71 @@ public class JMeterUtils implements UnitTestManager {
getProperties(file);
}
/**
* Loads services implementing a given interface and scans JMeter search path for the implementations.
* This is a transition replacement for {@link ClassFinder}, and JMeter would migrate to {@link ServiceLoader}-only
* lookup in the future.
* <p>Note: it is not always safe to cache the result as {@code search_paths} property might change over time</p>
*
* @param service interface that services should extend.
* @param serviceLoader ServiceLoader to fetch services.
* @param classLoader classLoader to use when searching for classes on the search path.
* @param exceptionHandler exception handler to use for services that fail to load.
* @return collection of services that load successfully
* @param <S> type of service (class or interface)
*/
@API(status = API.Status.DEPRECATED, since = "5.6")
public static <S> Collection<S> loadServicesAndScanJars(
@SuppressWarnings("BoundedWildcard") Class<S> service,
ServiceLoader<S> serviceLoader,
ClassLoader classLoader,
ServiceLoadExceptionHandler<? super S> exceptionHandler
) {
Collection<S> services = ClassFinder.loadServices(service, serviceLoader, exceptionHandler);
List<String> classesFromJars;
try (ClassFinder.Closeable ignored = ClassFinder.skipJarsWithJmeterSkipClassScanningAttribute()) {
classesFromJars = findClassesThatExtend(service);
} catch (IOException e) {
log.warn("Unable to lookup {} with ClassFinder.findClassesThatExtend. " +
"Will use only results from ServiceLoader ({} items found)", service, services.size(), e);
return services;
}
if (classesFromJars.isEmpty()) {
return services;
}
Set<String> loadedClasses = new HashSet<>((int) (services.size() / 0.75f) + 1);
for (S s : services) {
loadedClasses.add(s.getClass().getName());
}
List<S> result = new ArrayList<>(services.size() + classesFromJars.size());
result.addAll(services);
for (String className : classesFromJars) {
// Ignore classes that we loaded previously (e.g. with a ServiceLoader)
if (!loadedClasses.add(className)) {
continue;
}
try {
Class<? extends S> klass = Class.forName(className, false, classLoader)
.asSubclass(service);
if (!Modifier.isAbstract(klass.getModifiers())) {
continue;
}
result.add(klass.getDeclaredConstructor().newInstance());
} catch (Throwable e) {
if (e instanceof InvocationTargetException) {
//noinspection AssignmentToCatchBlockParameter
e = e.getCause();
}
exceptionHandler.handle(service, className, e);
}
}
return result;
}
/**
* Convenience method for
* {@link ClassFinder#findClassesThatExtend(String[], Class[], boolean)}
@ -325,7 +397,10 @@ public class JMeterUtils implements UnitTestManager {
* @param superClass - single class to search for
* @return List of Strings containing discovered class names.
* @throws IOException when the used {@link ClassFinder} throws one while searching for the class
* @deprecated use {@link #loadServicesAndScanJars(Class, ServiceLoader, ClassLoader, ServiceLoadExceptionHandler)} instead
*/
@API(status = API.Status.DEPRECATED, since = "5.6")
@Deprecated
public static List<String> findClassesThatExtend(Class<?> superClass)
throws IOException {
return ClassFinder.findClassesThatExtend(getSearchPaths(), new Class[]{superClass}, false);

View File

@ -321,8 +321,10 @@ public final class AllTests {
}
}
@SuppressWarnings("deprecation")
private static List<String> findJMeterJUnitTests(String searchPathString) throws IOException {
final String[] searchPaths = JOrphanUtils.split(searchPathString, ",");
// TODO: do we really need class searching here?
return ClassFinder.findClasses(searchPaths, new JunitTestFilter());
}

View File

@ -440,6 +440,7 @@ public class JMeterTest extends JMeterTestCaseJUnit implements Describable {
public static Collection<Object> getObjects(Class<?> extendsClass) throws Throwable {
String exName = extendsClass.getName();
@SuppressWarnings("deprecation")
Iterator<String> classes = ClassFinder
.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { extendsClass }).iterator();
List<Object> objects = new ArrayList<>();

View File

@ -171,6 +171,7 @@ public final class PackageTest extends JMeterTestCaseJUnit implements Describabl
public static Test suite() throws Exception {
TestSuite suite = new TestSuite("Bean Resource Test Suite");
@SuppressWarnings("deprecation")
List<String> testBeanClassNames = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { TestBean.class });
boolean errorDetected = false;

View File

@ -48,6 +48,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArray() throws IOException {
@SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Exception.class });
@ -56,6 +57,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayTrue() throws Exception {
@SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Object.class },
@ -66,6 +68,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayFalse() throws Exception {
@SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Exception.class },
@ -77,6 +80,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayBooleanStringString() throws Exception {
@SuppressWarnings("deprecation")
List<String> findClassesThatExtend = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { Exception.class },
@ -90,6 +94,7 @@ public class TestClassFinder {
@Test
public void testFindClassesThatExtendStringArrayClassOfQArrayBooleanStringStringTrue() throws Exception {
@SuppressWarnings("deprecation")
List<String> annotatedClasses = ClassFinder.findClassesThatExtend(
libDirs,
new Class<?>[] { java.beans.Transient.class },
@ -102,7 +107,7 @@ public class TestClassFinder {
@Test
public void testFindAnnotatedClasses() throws Exception {
@SuppressWarnings("unchecked")
@SuppressWarnings({"deprecation", "unchecked"})
List<String> annotatedClasses = ClassFinder.findAnnotatedClasses(
libDirs,
new Class[] { java.beans.Transient.class});
@ -111,7 +116,7 @@ public class TestClassFinder {
@Test
public void testFindAnnotatedInnerClasses() throws Exception {
@SuppressWarnings("unchecked")
@SuppressWarnings({"deprecation", "unchecked"})
List<String> annotatedClasses = ClassFinder.findAnnotatedClasses(libDirs,
new Class[] { java.lang.Deprecated.class}, true);
Assert.assertTrue(annotatedClasses.stream().anyMatch(s->s.contains("$")));
@ -119,12 +124,16 @@ public class TestClassFinder {
@Test
public void testFindClasses() throws IOException {
Assert.assertFalse(ClassFinder.findClasses(libDirs, className -> true).isEmpty());
@SuppressWarnings("deprecation")
List<String> classes = ClassFinder.findClasses(libDirs, className -> true);
Assert.assertFalse(classes.isEmpty());
}
@Test
public void testFindClassesNone() throws IOException {
Assert.assertTrue(ClassFinder.findClasses(libDirs, className -> false).isEmpty());
@SuppressWarnings("deprecation")
List<String> classes = ClassFinder.findClasses(libDirs, className -> false);
Assert.assertTrue(classes.isEmpty());
}
}

View File

@ -32,10 +32,13 @@ import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.auto.service.AutoService;
/**
* A function which understands BeanShell
* @since 1.X
*/
@AutoService(Function.class)
public class BeanShell extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(BeanShell.class);

Some files were not shown because too many files have changed in this diff Show More