• jmeter系列一(jmeter界面相关无TestBean)


    这系列文章是自己学习研究jmeter源码后的结果,这篇文章介绍的是jmeter界面以及事件响应相关的东西,写下来分享也利于回头看,

    下图是jmeter源码导入neatbeans

    涉及到跟gui相关的内容大体都在core里面的gui包下

    涉及到的类主要有

    AbstractJMeterGuiComponent

    GUIFactory

    GuiPackage

    MainFrame

    MenuFactory 动态加载类路径下继承了AbstractJmeterGuiComponent或者实现了TestBean接口的类,他们就是MainFrame中右边的Frame

    实现TestBean接口的类会有个BeanInfo于之相对应,这是javabean规范,重点是用到了Introspector

    gui.action包下面

    Command和各种实现了Command接口的类

    ActionRouter 动态加载实现了Command接口的类,通过ActionEvent的name来匹配是哪个类响应MainFrame中左边树的相关事件

    gui.tree包下面

    MainFrame左边的树,重点是TreeNode存了各种testelement,比如controller、sampler、preprocess、config etc.当运行测试的时候,程序就读取里面的各种testelement元素来执行测试流程

    界面涉及相关流程

    实例化树

    MainFrame实例化的时候,会通过调用makeTree方法传入JMeterTreeModel和JMeterTreeListener构建一个JTree,初始化有两个节点

     1 private void initTree(TestElement tp, TestElement wb) {
     2         // Insert the test plan node
     3         insertNodeInto(new JMeterTreeNode(tp, this), (JMeterTreeNode) getRoot(), 0);
     4         // Insert the workbench node
     5         insertNodeInto(new JMeterTreeNode(wb, this), (JMeterTreeNode) getRoot(), 1);
     6         // Let others know that the tree content has changed.
     7         // This should not be necessary, but without it, nodes are not shown when the user
     8         // uses the Close menu item
     9         nodeStructureChanged((JMeterTreeNode)getRoot());
    10     }

    这是JMeterTreeModel中的方法,这个方法会被JMeterTreeModel构造方法调用

    1 public JMeterTreeModel() {
    2         this(new TestPlanGui().createTestElement(),new WorkBenchGui().createTestElement());
    3 
    4     }

    TestPlanGui和WorkBeanchGui都是继承了AbstractJMeterGuiComponent的类,这是MainFrame中右边界面元素都要继承的类
    里面有通用发放,界面执行流程会调用到

    tree响应鼠标右键事件弹出菜单

    看JMeterTreeListener类,实现了TreeSelectionListener, MouseListener, KeyListener接口

    因此响应鼠标右键的事件就是在这处理的

     1 @Override
     2     public void mousePressed(MouseEvent e) {
     3         // Get the Main Frame.
     4         MainFrame mainFrame = GuiPackage.getInstance().getMainFrame();
     5         // Close any Main Menu that is open
     6         mainFrame.closeMenu();
     7         int selRow = tree.getRowForLocation(e.getX(), e.getY());
     8         if (tree.getPathForLocation(e.getX(), e.getY()) != null) {
     9             log.debug("mouse pressed, updating currentPath");
    10             currentPath = tree.getPathForLocation(e.getX(), e.getY());
    11         }
    12         if (selRow != -1) {
    13             // updateMainMenu(((JMeterGUIComponent)
    14             // getCurrentNode().getUserObject()).createPopupMenu());
    15             if (isRightClick(e)) {
    16                 if (tree.getSelectionCount() < 2) {
    17                     tree.setSelectionPath(currentPath);
    18                 }
    19                 log.debug("About to display pop-up");
    20                 displayPopUp(e);//这里
    21             }
    22         }
    23     }
    1 private void displayPopUp(MouseEvent e) {
    2         JPopupMenu pop = getCurrentNode().createPopupMenu();
    3         GuiPackage.getInstance().displayPopUp(e, pop);
    4     }

    JMeterTreeNode中的createPopupMenu方法

    1 public JPopupMenu createPopupMenu() {
    2         try {
    3             return GuiPackage.getInstance().getGui(getTestElement()).createPopupMenu();
    4         } catch (Exception e) {
    5             log.error("Can't get popup menu for gui", e);
    6             return null;
    7         }
    8     }

    最后shift+鼠标来到了JMeterGUIComponent接口,恩好吧,这是IDE

    找一个实现了这个接口的类,AbstractControllerGui....

    1 @Override
    2     public JPopupMenu createPopupMenu() {
    3         return MenuFactory.getDefaultControllerMenu();
    4     }

    看MenuFactory类

    有个static块,调用了静态方法initializeMenus

     1 List<String> guiClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] {
     2                     JMeterGUIComponent.class, TestBean.class });
     3             Collections.sort(guiClasses);
     4             for (String name : guiClasses) {
     5 
     6                 /*
     7                  * JMeterTreeNode and TestBeanGUI are special GUI classes, and
     8                  * aren't intended to be added to menus
     9                  *
    10                  * TODO: find a better way of checking this
    11                  */
    12                 if (name.endsWith("JMeterTreeNode") // $NON-NLS-1$
    13                         || name.endsWith("TestBeanGUI")) {// $NON-NLS-1$
    14                     continue;// Don't try to instantiate these
    15                 }
    16 
    17                 if (elementsToSkip.contains(name)) { // No point instantiating class
    18                     log.info("Skipping " + name);
    19                     continue;
    20                 }
    21 
    22                 boolean hideBean = false; // Should the TestBean be hidden?
    23 
    24                 JMeterGUIComponent item;
    25                 try {
    26                     Class<?> c = Class.forName(name);
    27                     if (TestBean.class.isAssignableFrom(c)) {
    28                         TestBeanGUI tbgui = new TestBeanGUI(c);
    29                         hideBean = tbgui.isHidden() || (tbgui.isExpert() && !JMeterUtils.isExpertMode());
    30                         item = tbgui;
    31                     } else {
    32                         item = (JMeterGUIComponent) c.newInstance();
    33                     }
    34                 } catch (NoClassDefFoundError e) {
    35                     log.warn("Missing jar? Could not create " + name + ". " + e);
    36                     continue;
    37                 } catch (Throwable e) {
    38                     log.warn("Could not instantiate " + name, e);
    39                     if (e instanceof Error){
    40                         throw (Error) e;
    41                     }
    42                     if (e instanceof RuntimeException){
    43                         if (!(e instanceof HeadlessException)) { // Allow headless testing
    44                             throw (RuntimeException) e;
    45                         }
    46                     }
    47                     continue;
    48                 }

    会扫描实现了JMeterGUIComponent的接口或者实现了TestBean接口(上面提到的利用了JavaBean规范,BeanInfo哦)的类,

    首先Class.forName他们

    对于实现接口TestBean的类,会用TestBeanGUI进行包装(我能说这是装饰模式吗?)

    然后调用getMenuCategories方法(实现了JMeterGUIComponent接口都会实现这个方法),

    这个方法就是告诉当在currentNode上右键鼠标,针对于这个node会弹出的菜单即所谓的PopupMenu

     1 if (categories == null) {
     2                     log.debug(name + " participates in no menus.");
     3                     continue;
     4                 }
     5                 if (categories.contains(THREADS)) {
     6                     threads.add(new MenuInfo(item, name));
     7                 }
     8                 if (categories.contains(FRAGMENTS)) {
     9                     fragments.add(new MenuInfo(item, name));
    10                 }
    11                 if (categories.contains(TIMERS)) {
    12                     timers.add(new MenuInfo(item, name));
    13                 }
    14 
    15                 if (categories.contains(POST_PROCESSORS)) {
    16                     postProcessors.add(new MenuInfo(item, name));
    17                 }
    18 
    19                 if (categories.contains(PRE_PROCESSORS)) {
    20                     preProcessors.add(new MenuInfo(item, name));
    21                 }
    22 
    23                 if (categories.contains(CONTROLLERS)) {
    24                     controllers.add(new MenuInfo(item, name));
    25                 }
    26 
    27                 if (categories.contains(SAMPLERS)) {
    28                     samplers.add(new MenuInfo(item, name));
    29                 }
    30 
    31                 if (categories.contains(NON_TEST_ELEMENTS)) {
    32                     nonTestElements.add(new MenuInfo(item, name));
    33                 }
    34 
    35                 if (categories.contains(LISTENERS)) {
    36                     listeners.add(new MenuInfo(item, name));
    37                 }
    38 
    39                 if (categories.contains(CONFIG_ELEMENTS)) {
    40                     configElements.add(new MenuInfo(item, name));
    41                 }
    42                 if (categories.contains(ASSERTIONS)) {
    43                     assertions.add(new MenuInfo(item, name));
    44                 }

    然后对应TestElement的gui类中的createPopUp方法就会在这里来取他们对应的菜单项目

    弹出菜单项的响应鼠标单击事件

    看MenuFactory中的各种makeMenus方法,makeMenuItem是最小项即创建每个弹出菜单最后一项内容

     1 public static JMenuItem makeMenuItem(String label, String name, String actionCommand) {
     2         JMenuItem newMenuChoice = new JMenuItem(label);
     3         newMenuChoice.setName(name);
     4         newMenuChoice.addActionListener(ActionRouter.getInstance());
     5         if (actionCommand != null) {
     6             newMenuChoice.setActionCommand(actionCommand);
     7         }
     8 
     9         return newMenuChoice;
    10     }

    newMenuChoice.addActionListener(ActionRouter.getInstance());

    这句话设置了每个菜单项响应事件的监听器,已经actioncommand即该菜单项的command名字

    定位到ActionRouter类,他是一个单例类,看他的方法populateCommandMap

     1 List<String> listClasses = ClassFinder.findClassesThatExtend(
     2                     JMeterUtils.getSearchPaths(), // strPathsOrJars - pathnames or jarfiles to search for classes
     3                     // classNames - required parent class(es) or annotations
     4                     new Class[] {Class.forName("org.apache.jmeter.gui.action.Command") }, // $NON-NLS-1$
     5                     false, // innerClasses - should we include inner classes?
     6                     null, // contains - classname should contain this string
     7                     // Ignore the classes which are specific to the reporting tool
     8                     "org.apache.jmeter.report.gui", // $NON-NLS-1$ // notContains - classname should not contain this string
     9                     false); // annotations - true if classnames are annotations
    10             commands = new HashMap<String, Set<Command>>(listClasses.size());
    11             if (listClasses.isEmpty()) {
    12                 log.fatalError("!!!!!Uh-oh, didn't find any action handlers!!!!!");
    13                 throw new JMeterError("No action handlers found - check JMeterHome and libraries");
    14             }
    15             for (String strClassName : listClasses) {
    16                 Class<?> commandClass = Class.forName(strClassName);
    17                 Command command = (Command) commandClass.newInstance();
    18                 for (String commandName : command.getActionNames()) {
    19                     Set<Command> commandObjects = commands.get(commandName);
    20                     if (commandObjects == null) {
    21                         commandObjects = new HashSet<Command>();
    22                         commands.put(commandName, commandObjects);
    23                     }
    24                     commandObjects.add(command);
    25                 }
    26             }

    搜集实现了Command接口的各种类,这些类就是响应菜单单击事件的类,他们都在org.apache.jmeter.gui.action包下,当然我们可以实现我们自己的Command,

    简单的看其中一个 AboutCommand

     1 static {
     2         HashSet<String> commands = new HashSet<String>();
     3         commands.add(ActionNames.ABOUT);
     4         commandSet = Collections.unmodifiableSet(commands);
     5     }
     6 
     7     /**
     8      * Handle the "about" action by displaying the "About Apache JMeter..."
     9      * dialog box. The Dialog Box is NOT modal, because those should be avoided
    10      * if at all possible.
    11      */
    12     @Override
    13     public void doAction(ActionEvent e) {
    14         if (e.getActionCommand().equals(ActionNames.ABOUT)) {
    15             this.about();
    16         }
    17     }
    18 
    19     /**
    20      * Provide the list of Action names that are available in this command.
    21      */
    22     @Override
    23     public Set<String> getActionNames() {
    24         return AboutCommand.commandSet;
    25     }

    doAction方法处理单击事件的地方

    getActionNames方法是ActionRouter用来调用的,

    ActionRouter中收集Command的代码

    1 for (String commandName : command.getActionNames()) {
    2                     Set<Command> commandObjects = commands.get(commandName);
    3                     if (commandObjects == null) {
    4                         commandObjects = new HashSet<Command>();
    5                         commands.put(commandName, commandObjects);
    6                     }
    7                     commandObjects.add(command);
    8                 }
  • 相关阅读:
    MXF文件结构浅析
    Windows FFMPEG开发环境配置
    从文件中读取yuv和h264数据
    TS封装格式
    H.264学习笔记
    【Web Shell】- 技术剖析中国菜刀
    【Web Shell】- 技术剖析中国菜刀 – Part I
    【硬件】- 显卡命名规则
    【硬件】- 英特尔CPU命名中的产品线后缀
    【硬件】- 英特尔CPU命名规则
  • 原文地址:https://www.cnblogs.com/liliqiang/p/4161124.html
Copyright © 2020-2023  润新知