diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties index a592bc64ba..11ede70e68 100644 --- a/src/core/org/apache/jmeter/resources/messages.properties +++ b/src/core/org/apache/jmeter/resources/messages.properties @@ -442,6 +442,7 @@ ja=Japanese jar_file=Jar Files java_request=Java Request java_request_defaults=Java Request Defaults +java_request_warning=Classname not found in classpath, ensure you add the required jar javascript_expression=JavaScript expression to evaluate jexl_expression=JEXL expression to evaluate jms_auth_required=Required diff --git a/src/core/org/apache/jmeter/resources/messages_fr.properties b/src/core/org/apache/jmeter/resources/messages_fr.properties index 21535b999d..c5bd78f224 100644 --- a/src/core/org/apache/jmeter/resources/messages_fr.properties +++ b/src/core/org/apache/jmeter/resources/messages_fr.properties @@ -435,6 +435,7 @@ ja=Japonais jar_file=Fichiers .jar java_request=Requ\u00EAte Java java_request_defaults=Requ\u00EAte Java par d\u00E9faut +java_request_warning=Class introuvable dans le classpath, veuillez ajouter le jar la contenant javascript_expression=Expression JavaScript \u00E0 \u00E9valuer jexl_expression=Expression JEXL \u00E0 \u00E9valuer jms_auth_required=Obligatoire diff --git a/src/protocol/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java b/src/protocol/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java index baf1ce76dc..761237d881 100644 --- a/src/protocol/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java +++ b/src/protocol/java/org/apache/jmeter/protocol/java/config/gui/JavaConfigGui.java @@ -19,31 +19,35 @@ package org.apache.jmeter.protocol.java.config.gui; import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.Color; +import java.awt.Font; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import javax.swing.ComboBoxModel; -import javax.swing.JComboBox; +import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import org.apache.commons.lang3.ArrayUtils; import org.apache.jmeter.config.Argument; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.gui.AbstractConfigGui; import org.apache.jmeter.config.gui.ArgumentsPanel; -import org.apache.jmeter.gui.util.HorizontalPanel; +import org.apache.jmeter.gui.util.VerticalPanel; import org.apache.jmeter.protocol.java.config.JavaConfig; import org.apache.jmeter.protocol.java.sampler.JavaSampler; import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.property.JMeterProperty; import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.gui.JLabeledChoice; import org.apache.jorphan.logging.LoggingManager; import org.apache.jorphan.reflect.ClassFinder; import org.apache.log.Logger; @@ -53,14 +57,14 @@ import org.apache.log.Logger; * {@link JavaConfig} object. * */ -public class JavaConfigGui extends AbstractConfigGui implements ActionListener { - private static final long serialVersionUID = 240L; +public class JavaConfigGui extends AbstractConfigGui implements ChangeListener { + private static final long serialVersionUID = 241L; /** Logging */ private static final Logger log = LoggingManager.getLoggerForClass(); /** A combo box allowing the user to choose a test class. */ - private JComboBox classnameCombo; + private JLabeledChoice classNameLabeledChoice; /** * Indicates whether or not the name of this component should be displayed @@ -72,6 +76,12 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { /** A panel allowing the user to set arguments for this test. */ private ArgumentsPanel argsPanel; + /** + * Used in case the referenced class is not in classpath or does not implement {@link JavaSamplerClient} + */ + private final JLabel warningLabel; + + /** * Create a new JavaConfigGui as a standalone component. */ @@ -90,6 +100,8 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { */ public JavaConfigGui(boolean displayNameField) { this.displayName = displayNameField; + ImageIcon image = JMeterUtils.getImage("warning.png"); + warningLabel = new JLabel(JMeterUtils.getResString("java_request_warning"), image, SwingConstants.LEFT); // $NON-NLS-1$ init(); } @@ -102,7 +114,7 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { /** * Initialize the GUI components and layout. */ - private void init() {// called from ctor, so must not be overridable + private final void init() {// called from ctor, so must not be overridable setLayout(new BorderLayout(0, 5)); if (displayName) { @@ -140,31 +152,31 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { log.debug("Exception getting interfaces.", e); } - JLabel label = new JLabel(JMeterUtils.getResString("protocol_java_classname")); // $NON-NLS-1$ + classNameLabeledChoice = new JLabeledChoice(JMeterUtils.getResString("protocol_java_classname"), possibleClasses.toArray(ArrayUtils.EMPTY_STRING_ARRAY), true, false); + classNameLabeledChoice.addChangeListener(this); - classnameCombo = new JComboBox<>(possibleClasses.toArray(ArrayUtils.EMPTY_STRING_ARRAY)); - classnameCombo.addActionListener(this); - classnameCombo.setEditable(false); - label.setLabelFor(classnameCombo); - - HorizontalPanel panel = new HorizontalPanel(); - panel.add(label); - panel.add(classnameCombo); + warningLabel.setForeground(Color.RED); + Font font = warningLabel.getFont(); + warningLabel.setFont(new Font(font.getFontName(), Font.BOLD, (int)(font.getSize()*1.1))); + warningLabel.setVisible(false); + VerticalPanel panel = new VerticalPanel(); + panel.add(classNameLabeledChoice); + panel.add(warningLabel); return panel; } /** - * Handle action events for this component. This method currently handles - * events for the classname combo box. + * Handle change events for this component. This method currently handles + * events for the classname JLabeledChoice * * @param evt * the ActionEvent to be handled */ @Override - public void actionPerformed(ActionEvent evt) { - if (evt.getSource() == classnameCombo) { - String className = ((String) classnameCombo.getSelectedItem()).trim(); + public void stateChanged(ChangeEvent evt) { + if (evt.getSource() == classNameLabeledChoice) { + String className = classNameLabeledChoice.getText().trim(); try { JavaSamplerClient client = (JavaSamplerClient) Class.forName(className, true, Thread.currentThread().getContextClassLoader()).newInstance(); @@ -204,8 +216,10 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { } argsPanel.configure(newArgs); + warningLabel.setVisible(false); } catch (Exception e) { log.error("Error getting argument list for " + className, e); + warningLabel.setVisible(true); } } } @@ -229,25 +243,42 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { argsPanel.configure((Arguments) config.getProperty(JavaSampler.ARGUMENTS).getObjectValue()); String className = config.getPropertyAsString(JavaSampler.CLASSNAME); - if(checkContainsClassName(classnameCombo.getModel(), className)) { - classnameCombo.setSelectedItem(className); - } else { + if(!checkContainsClassName(classNameLabeledChoice, className)) { + classNameLabeledChoice.addValue(className); + } + + if(!classOk(className)) { log.error("Error setting class:'"+className+"' in JavaSampler "+getName()+", check for a missing jar in your jmeter 'search_paths' and 'plugin_dependency_paths' properties"); + warningLabel.setVisible(true); + } else { + warningLabel.setVisible(false); + } + classNameLabeledChoice.setText(className); + } + + /** + * + * @param className String class name + * @return true if class is ok (exist in classpath and instanceof {@link JavaSamplerClient} + */ + private static boolean classOk(String className) { + try { + JavaSamplerClient client = (JavaSamplerClient) Class.forName(className, true, + Thread.currentThread().getContextClassLoader()).newInstance(); + return client instanceof JavaSamplerClient; + } catch (Exception ex) { + return false; } } /** * Check combo contains className - * @param model ComboBoxModel + * @param classnameChoice ComboBoxModel * @param className String class name * @return boolean */ - private static boolean checkContainsClassName(ComboBoxModel model, String className) { - int size = model.getSize(); - Set set = new HashSet<>(size); - for (int i = 0; i < size; i++) { - set.add(model.getElementAt(i)); - } + private static boolean checkContainsClassName(JLabeledChoice classnameChoice, String className) { + Set set = new HashSet<>(Arrays.asList(classnameChoice.getItems())); return set.contains(className); } @@ -264,7 +295,7 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { public void modifyTestElement(TestElement config) { configureTestElement(config); ((JavaConfig) config).setArguments((Arguments) argsPanel.createTestElement()); - ((JavaConfig) config).setClassname(String.valueOf(classnameCombo.getSelectedItem())); + ((JavaConfig) config).setClassname(classNameLabeledChoice.getText().trim()); } /* (non-Javadoc) @@ -274,7 +305,8 @@ public class JavaConfigGui extends AbstractConfigGui implements ActionListener { public void clearGui() { super.clearGui(); this.displayName = true; + this.warningLabel.setVisible(false); argsPanel.clearGui(); - classnameCombo.setSelectedIndex(0); + classNameLabeledChoice.setSelectedIndex(0); } } diff --git a/xdocs/changes.xml b/xdocs/changes.xml index 2f66b85e3f..1636a2f9e0 100644 --- a/xdocs/changes.xml +++ b/xdocs/changes.xml @@ -190,6 +190,7 @@ JMeter now requires Java 8. Ensure you use most up to date version.

Other Samplers

  • 603982Guard Exception handler of the JDBCSampler against null messages
  • +
  • 55652JavaSampler silently resets classname if class can not be found

Controllers