/* * 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.gui.action; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.SwingUtilities; import org.apache.jmeter.exceptions.IllegalUserActionException; import org.apache.jmeter.gui.GuiPackage; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.reflect.ClassFinder; import org.apache.jorphan.util.JMeterError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class ActionRouter implements ActionListener { private static final Logger log = LoggerFactory.getLogger(ActionRouter.class); // This is cheap, so no need to resort to IODH or lazy init private static final ActionRouter INSTANCE = new ActionRouter(); private final Map<String, Set<Command>> commands = new HashMap<>(); private final Map<String, Set<ActionListener>> preActionListeners = new HashMap<>(); private final Map<String, Set<ActionListener>> postActionListeners = new HashMap<>(); private ActionRouter() { } @Override public void actionPerformed(final ActionEvent e) { SwingUtilities.invokeLater(() -> performAction(e)); } private void performAction(final ActionEvent e) { String actionCommand = e.getActionCommand(); try { try { GuiPackage.getInstance().updateCurrentGui(); } catch (Exception err){ log.error("performAction({}) updateCurrentGui() on{} caused", actionCommand, e, err); JMeterUtils.reportErrorToUser("Problem updating GUI - see log file for details"); } for (Command c : commands.get(actionCommand)) { try { preActionPerformed(c.getClass(), e); c.doAction(e); postActionPerformed(c.getClass(), e); } catch (IllegalUserActionException err) { String msg = err.getMessage(); if (msg == null) { msg = err.toString(); } Throwable t = err.getCause(); if (t != null) { String cause = t.getMessage(); if (cause == null) { cause = t.toString(); } msg = msg + "\n" + cause; } JMeterUtils.reportErrorToUser(msg); } catch (Exception err) { log.error("Error processing {}", c, err); } } } catch (NullPointerException er) { log.error("performAction({}) {} caused", actionCommand, e, er); JMeterUtils.reportErrorToUser("Sorry, this feature (" + actionCommand + ") not yet implemented"); } } /** * To execute an action immediately in the current thread. * * @param e * the action to execute */ public void doActionNow(ActionEvent e) { performAction(e); } /** * Get the set of {@link Command}s registered under the name * <code>actionName</code> * * @param actionName * The name the {@link Command}s were registered * @return a set with all registered {@link Command}s for * <code>actionName</code> */ public Set<Command> getAction(String actionName) { Set<Command> set = new HashSet<>(); for (Command c : commands.get(actionName)) { try { set.add(c); } catch (Exception err) { log.error("Could not add Command", err); } } return set; } /** * Get the {@link Command} registered under the name <code>actionName</code>, * that is of {@link Class} <code>actionClass</code> * * @param actionName * The name the {@link Command}s were registered * @param actionClass * The class the {@link Command}s should be equal to * @return The registered {@link Command} for <code>actionName</code>, or * <code>null</code> if none could be found */ public Command getAction(String actionName, Class<?> actionClass) { for (Command com : commands.get(actionName)) { if (com.getClass().equals(actionClass)) { return com; } } return null; } /** * Get the {@link Command} registered under the name <code>actionName</code> * , which class names are equal to <code>className</code> * * @param actionName * The name the {@link Command}s were registered * @param className * The name of the class the {@link Command}s should be equal to * @return The {@link Command} for <code>actionName</code> or * <code>null</code> if none could be found */ public Command getAction(String actionName, String className) { for (Command com : commands.get(actionName)) { if (com.getClass().getName().equals(className)) { return com; } } return null; } /** * Allows an ActionListener to receive notification of a command being * executed prior to the actual execution of the command. * * @param action * the Class of the command for which the listener will * notifications for. Class must extend * org.apache.jmeter.gui.action.Command. * @param listener * the ActionListener to receive the notifications */ public void addPreActionListener(Class<?> action, ActionListener listener) { addActionListener(action, listener, preActionListeners); } /** * Allows an ActionListener to be removed from receiving notifications of a * command being executed prior to the actual execution of the command. * * @param action * the Class of the command for which the listener will * notifications for. Class must extend * org.apache.jmeter.gui.action.Command. * @param listener * the ActionListener to receive the notifications */ public void removePreActionListener(Class<?> action, ActionListener listener) { removeActionListener(action, listener, preActionListeners); } /** * @param action {@link Class} * @param e {@link ActionListener} * @param actionListeners {@link Set} */ private void removeActionListener(Class<?> action, ActionListener listener, Map<String, Set<ActionListener>> actionListeners) { if (action != null) { Set<ActionListener> set = actionListeners.get(action.getName()); if (set != null) { set.remove(listener); actionListeners.put(action.getName(), set); } } } /** * Allows an ActionListener to receive notification of a command being * executed after the command has executed. * * @param action * the Class of the command for which the listener will * notifications for. Class must extend * org.apache.jmeter.gui.action.Command. * @param listener * The {@link ActionListener} to be registered */ public void addPostActionListener(Class<?> action, ActionListener listener) { addActionListener(action, listener, postActionListeners); } /** * @param action {@link Class} * @param list {@link ActionListener} * @param actionListeners {@link Set} */ private void addActionListener(Class<?> action, ActionListener listener, Map<String, Set<ActionListener>> actionListeners) { if (action != null) { Set<ActionListener> set = actionListeners.get(action.getName()); if (set == null) { set = new HashSet<>(); } set.add(listener); actionListeners.put(action.getName(), set); } } /** * Allows an ActionListener to be removed from receiving notifications of a * command being executed after the command has executed. * * @param action * the Class of the command for which the listener will * notifications for. Class must extend * org.apache.jmeter.gui.action.Command. * @param listener The {@link ActionListener} that should be deregistered */ public void removePostActionListener(Class<?> action, ActionListener listener) { removeActionListener(action, listener, postActionListeners); } /** * @param action {@link Class} * @param e {@link ActionEvent} */ protected void preActionPerformed(Class<? extends Command> action, ActionEvent e) { actionPerformed(action, e, preActionListeners); } /** * @param action {@link Class} * @param e {@link ActionEvent} */ protected void postActionPerformed(Class<? extends Command> action, ActionEvent e) { actionPerformed(action, e, postActionListeners); } /** * @param action {@link Class} * @param e {@link ActionEvent} * @param actionListeners {@link Set} */ private void actionPerformed(Class<? extends Command> action, ActionEvent e, Map<String, Set<ActionListener>> actionListeners) { if (action != null) { Set<ActionListener> listenerSet = actionListeners.get(action.getName()); if (listenerSet != null && listenerSet.size() > 0) { ActionListener[] listeners = listenerSet.toArray(new ActionListener[listenerSet.size()]); for (ActionListener listener : listeners) { listener.actionPerformed(e); } } } } /** * Only for use by the JMeter.startGui. * This method must not be called by getInstance() as was done previously. * See <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=58790">Bug 58790</a> */ public void populateCommandMap() { if (!commands.isEmpty()) { return; // already done } try { List<String> listClasses = ClassFinder.findClassesThatExtend( JMeterUtils.getSearchPaths(), // strPathsOrJars - pathNames or jarfiles to search for classes // classNames - required parent class(es) or annotations new Class[] {Class.forName("org.apache.jmeter.gui.action.Command") }, // $NON-NLS-1$ false, // innerClasses - should we include inner classes? null, // contains - className should contain this string // Ignore the classes which are specific to the reporting tool "org.apache.jmeter.report.gui", // $NON-NLS-1$ // notContains - className should not contain this string false); // annotations - true if classNames are annotations if (listClasses.isEmpty()) { log.error("!!!!!Uh-oh, didn't find any action handlers!!!!!"); throw new JMeterError("No action handlers found - check JMeterHome and libraries"); } for (String strClassName : listClasses) { Class<?> commandClass = Class.forName(strClassName); Command command = (Command) commandClass.newInstance(); for (String commandName : command.getActionNames()) { Set<Command> commandObjects = commands.get(commandName); if (commandObjects == null) { commandObjects = new HashSet<>(); commands.put(commandName, commandObjects); } commandObjects.add(command); } } } catch (HeadlessException e) { if (log.isWarnEnabled()) { log.warn("AWT headless exception occurred. {}", e.toString()); } } catch (Exception e) { log.error("exception finding action handlers", e); } } /** * Gets the Instance attribute of the ActionRouter class * * @return The Instance value */ public static ActionRouter getInstance() { return INSTANCE; } }