org.apache.jmeter.control.ModuleController Maven / Gradle / Ivy
/*
* 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.control;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.swing.tree.TreeNode;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.NullProperty;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.apache.jorphan.util.JMeterStopTestException;
/**
* The goal of ModuleController is to add modularity to JMeter. The general idea
* is that web applications consist of small units of functionality (i.e. Logon,
* Create Account, Logoff...) which consist of requests that implement the
* functionality. These small units of functionality can be stored in
* SimpleControllers as modules that can be linked together quickly to form
* tests. ModuleController facilitates this by acting as a pointer to any
* controller that sits under the WorkBench. The controller and it's subelements
* will be substituted in place of the ModuleController at runtime. Config
* elements can be attached to the ModuleController to alter the functionality
* (which user logs in, which account is created, etc.) of the module.
*
*/
public class ModuleController extends GenericController implements ReplaceableController {
private static final long serialVersionUID = 240L;
private static final String NODE_PATH = "ModuleController.node_path";// $NON-NLS-1$
private transient JMeterTreeNode selectedNode = null;
/**
* No-arg constructor
*
* @see java.lang.Object#Object()
*/
public ModuleController() {
super();
}
@Override
public Object clone() {
ModuleController clone = (ModuleController) super.clone();
if (selectedNode == null) {
this.restoreSelected();
}
// TODO Should we clone instead the selectedNode?
clone.selectedNode = selectedNode;
return clone;
}
/**
* Sets the {@link JMeterTreeNode} which represents the controller which
* this object is pointing to. Used for building the test case upon
* execution.
*
* @param tn
* JMeterTreeNode
* @see org.apache.jmeter.gui.tree.JMeterTreeNode
*/
public void setSelectedNode(JMeterTreeNode tn) {
selectedNode = tn;
setNodePath();
}
/**
* Gets the {@link JMeterTreeNode} for the Controller
*
* @return JMeterTreeNode
*/
public JMeterTreeNode getSelectedNode() {
if (selectedNode == null){
restoreSelected();
}
return selectedNode;
}
private void setNodePath() {
List nodePath = new ArrayList<>();
if (selectedNode != null) {
TreeNode[] path = selectedNode.getPath();
for (TreeNode node : path) {
nodePath.add(((JMeterTreeNode) node).getName());
}
}
setProperty(new CollectionProperty(NODE_PATH, nodePath));
}
public List> getNodePath() {
JMeterProperty prop = getProperty(NODE_PATH);
if (!(prop instanceof NullProperty)) {
return (List>) prop.getObjectValue();
}
return null;
}
private void restoreSelected() {
GuiPackage gp = GuiPackage.getInstance();
if (gp != null) {
JMeterTreeNode root = (JMeterTreeNode) gp.getTreeModel().getRoot();
resolveReplacementSubTree(root);
}
}
/**
* {@inheritDoc}
*/
@Override
public void resolveReplacementSubTree(JMeterTreeNode context) {
if (selectedNode == null) {
List> nodePathList = getNodePath();
if (nodePathList != null && !nodePathList.isEmpty()) {
traverse(context, nodePathList, 1);
}
if(hasReplacementOccured() && selectedNode == null) {
throw new JMeterStopTestException("ModuleController:"
+ getName()
+ " has no selected Controller (did you rename some element in the path to target controller?), test was shutdown as a consequence");
}
}
}
/**
* In GUI Mode replacement occurs when test start
* In Non GUI Mode replacement occurs before test runs
* @return true if replacement occurred at the time method is called
*/
private boolean hasReplacementOccured() {
return GuiPackage.getInstance() == null || isRunningVersion();
}
private void traverse(JMeterTreeNode node, List> nodePath, int level) {
if (node != null && nodePath.size() > level) {
for (int i = 0; i < node.getChildCount(); i++) {
JMeterTreeNode cur = (JMeterTreeNode) node.getChildAt(i);
// Bug55375 - don't allow selectedNode to be a ModuleController as can cause recursion
if (!(cur.getTestElement() instanceof ModuleController)) {
if (cur.getName().equals(nodePath.get(level).toString())) {
if (nodePath.size() == (level + 1)) {
selectedNode = cur;
}
traverse(cur, nodePath, level + 1);
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public HashTree getReplacementSubTree() {
HashTree tree = new ListedHashTree();
if (selectedNode != null) {
// Use a local variable to avoid replacing reference by modified clone (see Bug 54950)
JMeterTreeNode nodeToReplace = selectedNode;
// We clone to avoid enabling existing node
if (!nodeToReplace.isEnabled()) {
nodeToReplace = cloneTreeNode(selectedNode);
nodeToReplace.setEnabled(true);
}
HashTree subtree = tree.add(nodeToReplace);
createSubTree(subtree, nodeToReplace);
}
return tree;
}
@SuppressWarnings("JdkObsolete")
private void createSubTree(HashTree tree, JMeterTreeNode node) {
Enumeration> e = node.children();
while (e.hasMoreElements()) {
JMeterTreeNode subNode = (JMeterTreeNode)e.nextElement();
tree.add(subNode);
createSubTree(tree.getTree(subNode), subNode);
}
}
private static JMeterTreeNode cloneTreeNode(JMeterTreeNode node) {
JMeterTreeNode treeNode = (JMeterTreeNode) node.clone();
treeNode.setUserObject(((TestElement) node.getUserObject()).clone());
cloneChildren(treeNode, node);
return treeNode;
}
@SuppressWarnings("JdkObsolete")
private static void cloneChildren(JMeterTreeNode to, JMeterTreeNode from) {
Enumeration> enumr = from.children();
while (enumr.hasMoreElements()) {
JMeterTreeNode child = (JMeterTreeNode) enumr.nextElement();
JMeterTreeNode childClone = (JMeterTreeNode) child.clone();
childClone.setUserObject(((TestElement) child.getUserObject()).clone());
to.add(childClone);
cloneChildren((JMeterTreeNode) to.getLastChild(), child);
}
}
}