0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.jmeter;
0020:
0021: import java.awt.event.ActionEvent;
0022: import java.io.File;
0023: import java.io.FileInputStream;
0024: import java.io.FileNotFoundException;
0025: import java.io.IOException;
0026: import java.net.Authenticator;
0027: import java.net.MalformedURLException;
0028: import java.text.SimpleDateFormat;
0029: import java.util.Date;
0030: import java.util.Enumeration;
0031: import java.util.Collection;
0032: import java.util.Iterator;
0033: import java.util.LinkedList;
0034: import java.util.List;
0035: import java.util.Locale;
0036: import java.util.Properties;
0037: import java.util.StringTokenizer;
0038:
0039: import javax.swing.JLabel;
0040:
0041: import org.apache.commons.cli.avalon.CLArgsParser;
0042: import org.apache.commons.cli.avalon.CLOption;
0043: import org.apache.commons.cli.avalon.CLOptionDescriptor;
0044: import org.apache.commons.cli.avalon.CLUtil;
0045: import org.apache.jmeter.control.ReplaceableController;
0046: import org.apache.jmeter.engine.ClientJMeterEngine;
0047: import org.apache.jmeter.engine.JMeterEngine;
0048: import org.apache.jmeter.engine.RemoteJMeterEngineImpl;
0049: import org.apache.jmeter.engine.StandardJMeterEngine;
0050: import org.apache.jmeter.engine.event.LoopIterationEvent;
0051: import org.apache.jmeter.exceptions.IllegalUserActionException;
0052: import org.apache.jmeter.gui.GuiPackage;
0053: import org.apache.jmeter.gui.MainFrame;
0054: import org.apache.jmeter.gui.action.ActionNames;
0055: import org.apache.jmeter.gui.action.ActionRouter;
0056: import org.apache.jmeter.gui.action.Load;
0057: import org.apache.jmeter.gui.tree.JMeterTreeListener;
0058: import org.apache.jmeter.gui.tree.JMeterTreeNode;
0059: import org.apache.jmeter.gui.tree.JMeterTreeModel;
0060: import org.apache.jmeter.plugin.JMeterPlugin;
0061: import org.apache.jmeter.plugin.PluginManager;
0062: import org.apache.jmeter.reporters.ResultCollector;
0063: import org.apache.jmeter.reporters.Summariser;
0064: import org.apache.jmeter.samplers.Remoteable;
0065: import org.apache.jmeter.save.SaveService;
0066: import org.apache.jmeter.services.FileServer;
0067: import org.apache.jmeter.testelement.TestElement;
0068: import org.apache.jmeter.testelement.TestListener;
0069: import org.apache.jmeter.util.BeanShellInterpreter;
0070: import org.apache.jmeter.util.BeanShellServer;
0071: import org.apache.jmeter.util.JMeterUtils;
0072: import org.apache.jorphan.collections.HashTree;
0073: import org.apache.jorphan.gui.ComponentUtil;
0074: import org.apache.jorphan.logging.LoggingManager;
0075: import org.apache.jorphan.collections.SearchByClass;
0076: import org.apache.jorphan.reflect.ClassTools;
0077: import org.apache.jorphan.util.JMeterException;
0078: import org.apache.jorphan.util.JOrphanUtils;
0079: import org.apache.log.Logger;
0080:
0081: import com.thoughtworks.xstream.converters.ConversionException;
0082:
0083: /**
0084: * Main JMeter class; processes options and starts the GUI, non-GUI or server as appropriate.
0085: */
0086: public class JMeter implements JMeterPlugin {
0087:
0088: private static final Logger log = LoggingManager
0089: .getLoggerForClass();
0090:
0091: public static final String HTTP_PROXY_PASS = "http.proxyPass"; // $NON-NLS-1$
0092:
0093: public static final String HTTP_PROXY_USER = "http.proxyUser"; // $NON-NLS-1$
0094:
0095: private static final int PROXY_PASSWORD = 'a';// $NON-NLS-1$
0096: private static final int JMETER_HOME_OPT = 'd';// $NON-NLS-1$
0097: private static final int HELP_OPT = 'h';// $NON-NLS-1$
0098: // jmeter.log
0099: private static final int JMLOGFILE_OPT = 'j';// $NON-NLS-1$
0100: // sample result log file
0101: private static final int LOGFILE_OPT = 'l';// $NON-NLS-1$
0102: private static final int NONGUI_OPT = 'n';// $NON-NLS-1$
0103: private static final int PROPFILE_OPT = 'p';// $NON-NLS-1$
0104: private static final int PROPFILE2_OPT = 'q';// $NON-NLS-1$
0105: private static final int REMOTE_OPT = 'r';// $NON-NLS-1$
0106: private static final int SERVER_OPT = 's';// $NON-NLS-1$
0107: private static final int TESTFILE_OPT = 't';// $NON-NLS-1$
0108: private static final int PROXY_USERNAME = 'u';// $NON-NLS-1$
0109: private static final int VERSION_OPT = 'v';// $NON-NLS-1$
0110:
0111: private static final int SYSTEM_PROPERTY = 'D';// $NON-NLS-1$
0112: private static final int JMETER_GLOBAL_PROP = 'G';// $NON-NLS-1$
0113: private static final int PROXY_HOST = 'H';// $NON-NLS-1$
0114: private static final int JMETER_PROPERTY = 'J';// $NON-NLS-1$
0115: private static final int LOGLEVEL = 'L';// $NON-NLS-1$
0116: private static final int NONPROXY_HOSTS = 'N';// $NON-NLS-1$
0117: private static final int PROXY_PORT = 'P';// $NON-NLS-1$
0118: private static final int REMOTE_OPT_PARAM = 'R';// $NON-NLS-1$
0119: private static final int SYSTEM_PROPFILE = 'S';// $NON-NLS-1$
0120: private static final int REMOTE_STOP = 'X';// $NON-NLS-1$
0121:
0122: /**
0123: * Define the understood options. Each CLOptionDescriptor contains:
0124: * <ul>
0125: * <li>The "long" version of the option. Eg, "help" means that "--help"
0126: * will be recognised.</li>
0127: * <li>The option flags, governing the option's argument(s).</li>
0128: * <li>The "short" version of the option. Eg, 'h' means that "-h" will be
0129: * recognised.</li>
0130: * <li>A description of the option.</li>
0131: * </ul>
0132: */
0133: private static final CLOptionDescriptor[] options = new CLOptionDescriptor[] {
0134: new CLOptionDescriptor("help",
0135: CLOptionDescriptor.ARGUMENT_DISALLOWED, HELP_OPT,
0136: "print usage information and exit"),
0137: new CLOptionDescriptor("version",
0138: CLOptionDescriptor.ARGUMENT_DISALLOWED,
0139: VERSION_OPT,
0140: "print the version information and exit"),
0141: new CLOptionDescriptor("propfile",
0142: CLOptionDescriptor.ARGUMENT_REQUIRED, PROPFILE_OPT,
0143: "the jmeter property file to use"),
0144: new CLOptionDescriptor("addprop",
0145: CLOptionDescriptor.ARGUMENT_REQUIRED
0146: | CLOptionDescriptor.DUPLICATES_ALLOWED,
0147: PROPFILE2_OPT, "additional JMeter property file(s)"),
0148: new CLOptionDescriptor("testfile",
0149: CLOptionDescriptor.ARGUMENT_REQUIRED, TESTFILE_OPT,
0150: "the jmeter test(.jmx) file to run"),
0151: new CLOptionDescriptor("logfile",
0152: CLOptionDescriptor.ARGUMENT_REQUIRED, LOGFILE_OPT,
0153: "the file to log samples to"),
0154: new CLOptionDescriptor("jmeterlogfile",
0155: CLOptionDescriptor.ARGUMENT_REQUIRED,
0156: JMLOGFILE_OPT, "jmeter run log file (jmeter.log)"),
0157: new CLOptionDescriptor("nongui",
0158: CLOptionDescriptor.ARGUMENT_DISALLOWED, NONGUI_OPT,
0159: "run JMeter in nongui mode"),
0160: new CLOptionDescriptor("server",
0161: CLOptionDescriptor.ARGUMENT_DISALLOWED, SERVER_OPT,
0162: "run the JMeter server"),
0163: new CLOptionDescriptor("proxyHost",
0164: CLOptionDescriptor.ARGUMENT_REQUIRED, PROXY_HOST,
0165: "Set a proxy server for JMeter to use"),
0166: new CLOptionDescriptor("proxyPort",
0167: CLOptionDescriptor.ARGUMENT_REQUIRED, PROXY_PORT,
0168: "Set proxy server port for JMeter to use"),
0169: new CLOptionDescriptor("nonProxyHosts",
0170: CLOptionDescriptor.ARGUMENT_REQUIRED,
0171: NONPROXY_HOSTS,
0172: "Set nonproxy host list (e.g. *.apache.org|localhost)"),
0173: new CLOptionDescriptor("username",
0174: CLOptionDescriptor.ARGUMENT_REQUIRED,
0175: PROXY_USERNAME,
0176: "Set username for proxy server that JMeter is to use"),
0177: new CLOptionDescriptor("password",
0178: CLOptionDescriptor.ARGUMENT_REQUIRED,
0179: PROXY_PASSWORD,
0180: "Set password for proxy server that JMeter is to use"),
0181: new CLOptionDescriptor("jmeterproperty",
0182: CLOptionDescriptor.DUPLICATES_ALLOWED
0183: | CLOptionDescriptor.ARGUMENTS_REQUIRED_2,
0184: JMETER_PROPERTY,
0185: "Define additional JMeter properties"),
0186: new CLOptionDescriptor("globalproperty",
0187: CLOptionDescriptor.DUPLICATES_ALLOWED
0188: | CLOptionDescriptor.ARGUMENTS_REQUIRED_2,
0189: JMETER_GLOBAL_PROP,
0190: "Define Global properties (sent to servers)"),
0191: new CLOptionDescriptor("systemproperty",
0192: CLOptionDescriptor.DUPLICATES_ALLOWED
0193: | CLOptionDescriptor.ARGUMENTS_REQUIRED_2,
0194: SYSTEM_PROPERTY,
0195: "Define additional system properties"),
0196: new CLOptionDescriptor("systemPropertyFile",
0197: CLOptionDescriptor.DUPLICATES_ALLOWED
0198: | CLOptionDescriptor.ARGUMENT_REQUIRED,
0199: SYSTEM_PROPFILE,
0200: "additional system property file(s)"),
0201: new CLOptionDescriptor("loglevel",
0202: CLOptionDescriptor.DUPLICATES_ALLOWED
0203: | CLOptionDescriptor.ARGUMENTS_REQUIRED_2,
0204: LOGLEVEL,
0205: "[category=]level e.g. jorphan=INFO or jmeter.util=DEBUG"),
0206: new CLOptionDescriptor("runremote",
0207: CLOptionDescriptor.ARGUMENT_DISALLOWED, REMOTE_OPT,
0208: "Start remote servers (as defined in remote_hosts)"),
0209: new CLOptionDescriptor("remotestart",
0210: CLOptionDescriptor.ARGUMENT_REQUIRED,
0211: REMOTE_OPT_PARAM,
0212: "Start these remote servers (overrides remote_hosts)"),
0213: new CLOptionDescriptor("homedir",
0214: CLOptionDescriptor.ARGUMENT_REQUIRED,
0215: JMETER_HOME_OPT, "the jmeter home directory to use"),
0216: new CLOptionDescriptor("remoteexit",
0217: CLOptionDescriptor.ARGUMENT_DISALLOWED,
0218: REMOTE_STOP,
0219: "Exit the remote servers at end of test (non-GUI)"), };
0220:
0221: public JMeter() {
0222: }
0223:
0224: // Hack to allow automated tests to find when test has ended
0225: //transient boolean testEnded = false;
0226:
0227: private JMeter parent;
0228:
0229: private Properties remoteProps; // Properties to be sent to remote servers
0230:
0231: private boolean remoteStop; // should remote engines be stopped at end of GUI test?
0232:
0233: /**
0234: * Starts up JMeter in GUI mode
0235: */
0236: private void startGui(CLOption testFile) {
0237:
0238: PluginManager.install(this , true);
0239: JMeterTreeModel treeModel = new JMeterTreeModel();
0240: JMeterTreeListener treeLis = new JMeterTreeListener(treeModel);
0241: treeLis.setActionHandler(ActionRouter.getInstance());
0242: // NOTUSED: GuiPackage guiPack =
0243: GuiPackage.getInstance(treeLis, treeModel);
0244: MainFrame main = new MainFrame(ActionRouter.getInstance(),
0245: treeModel, treeLis);
0246: ComponentUtil.centerComponentInWindow(main, 80);
0247: main.show();
0248: ActionRouter.getInstance().actionPerformed(
0249: new ActionEvent(main, 1, ActionNames.ADD_ALL));
0250: String arg;
0251: if (testFile != null && (arg = testFile.getArgument()) != null) {
0252: FileInputStream reader = null;
0253: try {
0254: File f = new File(arg);
0255: log.info("Loading file: " + f);
0256: reader = new FileInputStream(f);
0257: HashTree tree = SaveService.loadTree(reader);
0258:
0259: GuiPackage.getInstance().setTestPlanFile(
0260: f.getAbsolutePath());
0261:
0262: Load.insertLoadedTree(1, tree);
0263: } catch (ConversionException e) {
0264: log.error("Failure loading test file", e);
0265: JMeterUtils
0266: .reportErrorToUser(SaveService.CEtoString(e));
0267: } catch (Exception e) {
0268: log.error("Failure loading test file", e);
0269: JMeterUtils.reportErrorToUser(e.toString());
0270: } finally {
0271: JOrphanUtils.closeQuietly(reader);
0272: }
0273: }
0274: }
0275:
0276: /**
0277: * Takes the command line arguments and uses them to determine how to
0278: * startup JMeter.
0279: */
0280: public void start(String[] args) {
0281:
0282: CLArgsParser parser = new CLArgsParser(args, options);
0283: if (null != parser.getErrorString()) {
0284: System.err.println("Error: " + parser.getErrorString());
0285: System.out.println("Usage");
0286: System.out.println(CLUtil.describeOptions(options)
0287: .toString());
0288: return;
0289: }
0290: try {
0291: initializeProperties(parser); // Also initialises JMeter logging
0292:
0293: /*
0294: * The following is needed for HTTPClient.
0295: * (originally tried doing this in HTTPSampler2,
0296: * but it appears that it was done too late when running in GUI mode)
0297: * Set the commons logging default to Avalon Logkit, if not already defined
0298: */
0299: if (System.getProperty("org.apache.commons.logging.Log") == null) { // $NON-NLS-1$
0300: System.setProperty(
0301: "org.apache.commons.logging.Log" // $NON-NLS-1$
0302: ,
0303: "org.apache.commons.logging.impl.LogKitLogger"); // $NON-NLS-1$
0304: }
0305:
0306: log.info(JMeterUtils.getJMeterCopyright());
0307: log.info("Version " + JMeterUtils.getJMeterVersion());
0308: logProperty("java.version"); //$NON-NLS-1$
0309: logProperty("os.name"); //$NON-NLS-1$
0310: logProperty("os.arch"); //$NON-NLS-1$
0311: logProperty("os.version"); //$NON-NLS-1$
0312: logProperty("file.encoding"); // $NON-NLS-1$
0313: log.info("Default Locale="
0314: + Locale.getDefault().getDisplayName());// $NON-NLS-1$
0315: log.info("JMeter Locale="
0316: + JMeterUtils.getLocale().getDisplayName());// $NON-NLS-1$
0317: log.info("JMeterHome=" + JMeterUtils.getJMeterHome());// $NON-NLS-1$
0318: logProperty("user.dir", " ="); //$NON-NLS-1$
0319: log.info("PWD =" + new File(".").getCanonicalPath());//$NON-NLS-1$
0320: setProxy(parser);
0321:
0322: updateClassLoader();
0323: if (log.isDebugEnabled()) {
0324: String jcp = System.getProperty("java.class.path");// $NON-NLS-1$
0325: String bits[] = jcp.split(File.pathSeparator);
0326: log.debug("ClassPath");
0327: for (int i = 0; i < bits.length; i++) {
0328: log.debug(bits[i]);
0329: }
0330: log.debug(jcp);
0331: }
0332:
0333: // Set some (hopefully!) useful properties
0334: long now = System.currentTimeMillis();
0335: JMeterUtils.setProperty("START.MS", Long.toString(now));
0336: Date today = new Date(now); // so it agrees with above
0337: // TODO perhaps should share code with __time() function for this...
0338: JMeterUtils.setProperty("START.YMD", new SimpleDateFormat(
0339: "yyyyMMdd").format(today));
0340: JMeterUtils.setProperty("START.HMS", new SimpleDateFormat(
0341: "HHmmss").format(today));
0342:
0343: if (parser.getArgumentById(VERSION_OPT) != null) {
0344: System.out.println(JMeterUtils.getJMeterCopyright());
0345: System.out.println("Version "
0346: + JMeterUtils.getJMeterVersion());
0347: } else if (parser.getArgumentById(HELP_OPT) != null) {
0348: System.out
0349: .println(JMeterUtils
0350: .getResourceFileAsText("org/apache/jmeter/help.txt"));// $NON-NLS-1$
0351: } else if (parser.getArgumentById(SERVER_OPT) != null) {
0352: // We need to check if the JMeter home contains spaces in the path,
0353: // because then we will not be able to bind to RMI registry, see
0354: // Java bug id 4496398
0355: final String jmHome = JMeterUtils.getJMeterHome();
0356: if (jmHome.indexOf(" ") > -1) {// $NON-NLS-1$
0357: // Just warn user, and exit, no reason to continue, since we will
0358: // not be able to bind to RMI registry, until Java bug 4496398 is fixed
0359: log
0360: .error("JMeter path cannot contain spaces when run in server mode : "
0361: + jmHome);
0362: throw new RuntimeException(
0363: "JMeter path cannot contain spaces when run in server mode: "
0364: + jmHome);
0365: }
0366: // Start the server
0367: startServer(JMeterUtils
0368: .getPropDefault("server_port", 0));// $NON-NLS-1$
0369: startOptionalServers();
0370: } else if (parser.getArgumentById(NONGUI_OPT) == null) {
0371: startGui(parser.getArgumentById(TESTFILE_OPT));
0372: startOptionalServers();
0373: } else {
0374: CLOption rem = parser.getArgumentById(REMOTE_OPT_PARAM);
0375: if (rem == null)
0376: rem = parser.getArgumentById(REMOTE_OPT);
0377: startNonGui(parser.getArgumentById(TESTFILE_OPT),
0378: parser.getArgumentById(LOGFILE_OPT), rem);
0379: startOptionalServers();
0380: }
0381: } catch (IllegalUserActionException e) {
0382: System.out.println(e.getMessage());
0383: System.out.println("Incorrect Usage");
0384: System.out.println(CLUtil.describeOptions(options)
0385: .toString());
0386: } catch (Throwable e) {
0387: if (log != null) {
0388: log.fatalError("An error occurred: ", e);
0389: } else {
0390: e.printStackTrace();
0391: }
0392: System.out.println("An error occurred: " + e.getMessage());
0393: System.exit(1);
0394: }
0395: }
0396:
0397: // Update classloader if necessary
0398: private void updateClassLoader() {
0399: updatePath("search_paths", ";"); //$NON-NLS-1$//$NON-NLS-2$
0400: updatePath("user.classpath", File.pathSeparator);//$NON-NLS-1$
0401: }
0402:
0403: private void updatePath(String property, String sep) {
0404: String userpath = JMeterUtils.getPropDefault(property, "");// $NON-NLS-1$
0405: if (userpath.length() <= 0)
0406: return;
0407: log.info(property + "=" + userpath); //$NON-NLS-1$
0408: StringTokenizer tok = new StringTokenizer(userpath, sep);
0409: while (tok.hasMoreTokens()) {
0410: String path = tok.nextToken();
0411: File f = new File(path);
0412: if (!f.canRead() && !f.isDirectory()) {
0413: log.warn("Can't read " + path);
0414: } else {
0415: log.info("Adding to classpath: " + path);
0416: try {
0417: NewDriver.addPath(path);
0418: } catch (MalformedURLException e) {
0419: log.warn("Error adding: " + path + " "
0420: + e.getLocalizedMessage());
0421: }
0422: }
0423: }
0424: }
0425:
0426: /**
0427: *
0428: */
0429: private void startOptionalServers() {
0430: int bshport = JMeterUtils.getPropDefault(
0431: "beanshell.server.port", 0);// $NON-NLS-1$
0432: String bshfile = JMeterUtils.getPropDefault(
0433: "beanshell.server.file", "");// $NON-NLS-1$ $NON-NLS-2$
0434: if (bshport > 0) {
0435: log.info("Starting Beanshell server (" + bshport + ","
0436: + bshfile + ")");
0437: Runnable t = new BeanShellServer(bshport, bshfile);
0438: t.run();
0439: }
0440:
0441: // Should we run a beanshell script on startup?
0442: String bshinit = JMeterUtils.getProperty("beanshell.init.file");// $NON-NLS-1$
0443: if (bshinit != null) {
0444: log.info("Run Beanshell on file: " + bshinit);
0445: try {
0446: BeanShellInterpreter bsi = new BeanShellInterpreter();//bshinit,log);
0447: bsi.source(bshinit);
0448: } catch (ClassNotFoundException e) {
0449: log.warn("Could not start Beanshell: "
0450: + e.getLocalizedMessage());
0451: } catch (JMeterException e) {
0452: log.warn("Could not process Beanshell file: "
0453: + e.getLocalizedMessage());
0454: }
0455: }
0456:
0457: int mirrorPort = JMeterUtils.getPropDefault(
0458: "mirror.server.port", 0);// $NON-NLS-1$
0459: if (mirrorPort > 0) {
0460: log.info("Starting Mirror server (" + mirrorPort + ")");
0461: try {
0462: Object instance = ClassTools
0463: .construct(
0464: "org.apache.jmeter.protocol.http.control.HttpMirrorControl",// $NON-NLS-1$
0465: mirrorPort);
0466: ClassTools.invoke(instance, "startHttpMirror");
0467: } catch (JMeterException e) {
0468: log.warn("Could not start Mirror server", e);
0469: }
0470: }
0471: }
0472:
0473: /**
0474: * Sets a proxy server for the JVM if the command line arguments are
0475: * specified.
0476: */
0477: private void setProxy(CLArgsParser parser)
0478: throws IllegalUserActionException {
0479: if (parser.getArgumentById(PROXY_USERNAME) != null) {
0480: Properties jmeterProps = JMeterUtils.getJMeterProperties();
0481: if (parser.getArgumentById(PROXY_PASSWORD) != null) {
0482: String u, p;
0483: Authenticator.setDefault(new ProxyAuthenticator(
0484: u = parser.getArgumentById(PROXY_USERNAME)
0485: .getArgument(), p = parser
0486: .getArgumentById(PROXY_PASSWORD)
0487: .getArgument()));
0488: log.info("Set Proxy login: " + u + "/" + p);
0489: jmeterProps.setProperty(HTTP_PROXY_USER, u);//for Httpclient
0490: jmeterProps.setProperty(HTTP_PROXY_PASS, p);//for Httpclient
0491: } else {
0492: String u;
0493: Authenticator.setDefault(new ProxyAuthenticator(
0494: u = parser.getArgumentById(PROXY_USERNAME)
0495: .getArgument(), ""));
0496: log.info("Set Proxy login: " + u);
0497: jmeterProps.setProperty(HTTP_PROXY_USER, u);
0498: }
0499: }
0500: if (parser.getArgumentById(PROXY_HOST) != null
0501: && parser.getArgumentById(PROXY_PORT) != null) {
0502: String h = parser.getArgumentById(PROXY_HOST).getArgument();
0503: String p = parser.getArgumentById(PROXY_PORT).getArgument();
0504: System.setProperty("http.proxyHost", h);// $NON-NLS-1$
0505: System.setProperty("https.proxyHost", h);// $NON-NLS-1$
0506: System.setProperty("http.proxyPort", p);// $NON-NLS-1$
0507: System.setProperty("https.proxyPort", p);// $NON-NLS-1$
0508: log.info("Set http[s].proxyHost: " + h + " Port: " + p);
0509: } else if (parser.getArgumentById(PROXY_HOST) != null
0510: || parser.getArgumentById(PROXY_PORT) != null) {
0511: throw new IllegalUserActionException(JMeterUtils
0512: .getResString("proxy_cl_error"));// $NON-NLS-1$
0513: }
0514:
0515: if (parser.getArgumentById(NONPROXY_HOSTS) != null) {
0516: String n = parser.getArgumentById(NONPROXY_HOSTS)
0517: .getArgument();
0518: System.setProperty("http.nonProxyHosts", n);// $NON-NLS-1$
0519: System.setProperty("https.nonProxyHosts", n);// $NON-NLS-1$
0520: log.info("Set http[s].nonProxyHosts: " + n);
0521: }
0522: }
0523:
0524: private void initializeProperties(CLArgsParser parser) {
0525: if (parser.getArgumentById(PROPFILE_OPT) != null) {
0526: JMeterUtils.loadJMeterProperties(parser.getArgumentById(
0527: PROPFILE_OPT).getArgument());
0528: } else {
0529: JMeterUtils.loadJMeterProperties(NewDriver.getJMeterDir()
0530: + File.separator + "bin" + File.separator // $NON-NLS-1$
0531: + "jmeter.properties");// $NON-NLS-1$
0532: }
0533:
0534: if (parser.getArgumentById(JMLOGFILE_OPT) != null) {
0535: String jmlogfile = parser.getArgumentById(JMLOGFILE_OPT)
0536: .getArgument();
0537: JMeterUtils.setProperty(LoggingManager.LOG_FILE, jmlogfile);
0538: }
0539:
0540: JMeterUtils.initLogging();
0541: JMeterUtils.initLocale();
0542: // Bug 33845 - allow direct override of Home dir
0543: if (parser.getArgumentById(JMETER_HOME_OPT) == null) {
0544: JMeterUtils.setJMeterHome(NewDriver.getJMeterDir());
0545: } else {
0546: JMeterUtils.setJMeterHome(parser.getArgumentById(
0547: JMETER_HOME_OPT).getArgument());
0548: }
0549:
0550: Properties jmeterProps = JMeterUtils.getJMeterProperties();
0551: remoteProps = new Properties();
0552:
0553: // Add local JMeter properties, if the file is found
0554: String userProp = JMeterUtils.getPropDefault(
0555: "user.properties", ""); //$NON-NLS-1$
0556: if (userProp.length() > 0) { //$NON-NLS-1$
0557: FileInputStream fis = null;
0558: try {
0559: File file = JMeterUtils.findFile(userProp);
0560: if (file.canRead()) {
0561: log.info("Loading user properties from: "
0562: + file.getCanonicalPath());
0563: fis = new FileInputStream(file);
0564: Properties tmp = new Properties();
0565: tmp.load(fis);
0566: jmeterProps.putAll(tmp);
0567: LoggingManager.setLoggingLevels(tmp);//Do what would be done earlier
0568: }
0569: } catch (IOException e) {
0570: log.warn("Error loading user property file: "
0571: + userProp, e);
0572: } finally {
0573: JOrphanUtils.closeQuietly(fis);
0574: }
0575: }
0576:
0577: // Add local system properties, if the file is found
0578: String sysProp = JMeterUtils.getPropDefault(
0579: "system.properties", ""); //$NON-NLS-1$
0580: if (sysProp.length() > 0) {
0581: FileInputStream fis = null;
0582: try {
0583: File file = JMeterUtils.findFile(sysProp);
0584: if (file.canRead()) {
0585: log.info("Loading system properties from: "
0586: + file.getCanonicalPath());
0587: fis = new FileInputStream(file);
0588: System.getProperties().load(fis);
0589: }
0590: } catch (IOException e) {
0591: log.warn("Error loading system property file: "
0592: + sysProp, e);
0593: } finally {
0594: JOrphanUtils.closeQuietly(fis);
0595: }
0596: }
0597:
0598: // Process command line property definitions
0599: // These can potentially occur multiple times
0600:
0601: List clOptions = parser.getArguments();
0602: int size = clOptions.size();
0603:
0604: for (int i = 0; i < size; i++) {
0605: CLOption option = (CLOption) clOptions.get(i);
0606: String name = option.getArgument(0);
0607: String value = option.getArgument(1);
0608: FileInputStream fis = null;
0609:
0610: switch (option.getDescriptor().getId()) {
0611:
0612: // Should not have any text arguments
0613: case CLOption.TEXT_ARGUMENT:
0614: throw new IllegalArgumentException("Unknown arg: "
0615: + option.getArgument());
0616:
0617: case PROPFILE2_OPT: // Bug 33920 - allow multiple props
0618: try {
0619: fis = new FileInputStream(new File(name));
0620: Properties tmp = new Properties();
0621: tmp.load(fis);
0622: jmeterProps.putAll(tmp);
0623: LoggingManager.setLoggingLevels(tmp);//Do what would be done earlier
0624: } catch (FileNotFoundException e) {
0625: log.warn("Can't find additional property file: "
0626: + name, e);
0627: } catch (IOException e) {
0628: log.warn("Error loading additional property file: "
0629: + name, e);
0630: } finally {
0631: JOrphanUtils.closeQuietly(fis);
0632: }
0633: break;
0634: case SYSTEM_PROPFILE:
0635: log
0636: .info("Setting System properties from file: "
0637: + name);
0638: try {
0639: fis = new FileInputStream(new File(name));
0640: System.getProperties().load(fis);
0641: } catch (IOException e) {
0642: log.warn("Cannot find system property file "
0643: + e.getLocalizedMessage());
0644: } finally {
0645: JOrphanUtils.closeQuietly(fis);
0646: }
0647: break;
0648: case SYSTEM_PROPERTY:
0649: if (value.length() > 0) { // Set it
0650: log.info("Setting System property: " + name + "="
0651: + value);
0652: System.getProperties().setProperty(name, value);
0653: } else { // Reset it
0654: log.warn("Removing System property: " + name);
0655: System.getProperties().remove(name);
0656: }
0657: break;
0658: case JMETER_PROPERTY:
0659: if (value.length() > 0) { // Set it
0660: log.info("Setting JMeter property: " + name + "="
0661: + value);
0662: jmeterProps.setProperty(name, value);
0663: } else { // Reset it
0664: log.warn("Removing JMeter property: " + name);
0665: jmeterProps.remove(name);
0666: }
0667: break;
0668: case JMETER_GLOBAL_PROP:
0669: if (value.length() > 0) { // Set it
0670: log.info("Setting Global property: " + name + "="
0671: + value);
0672: remoteProps.setProperty(name, value);
0673: }
0674: break;
0675: case LOGLEVEL:
0676: if (value.length() > 0) { // Set category
0677: log.info("LogLevel: " + name + "=" + value);
0678: LoggingManager.setPriority(value, name);
0679: } else { // Set root level
0680: log.warn("LogLevel: " + name);
0681: LoggingManager.setPriority(name);
0682: }
0683: break;
0684: case REMOTE_STOP:
0685: remoteStop = true;
0686: break;
0687: }
0688: }
0689:
0690: }
0691:
0692: private void startServer(int port) {
0693: try {
0694: new RemoteJMeterEngineImpl(port);
0695: } catch (Exception ex) {
0696: log.error("Giving up, as server failed with:", ex);
0697: System.err.println("Server failed to start: " + ex);
0698: System.exit(1);// Give up
0699: }
0700: }
0701:
0702: private void startNonGui(CLOption testFile, CLOption logFile,
0703: CLOption remoteStart) throws IllegalUserActionException {
0704: // add a system property so samplers can check to see if JMeter
0705: // is running in NonGui mode
0706: System.setProperty("JMeter.NonGui", "true");// $NON-NLS-1$
0707: // Force the X11 display to be checked
0708: try {
0709: new JLabel();
0710: } catch (InternalError e) {
0711: // ignored
0712: }
0713: JMeter driver = new JMeter();// TODO - why does it create a new instance?
0714: driver.remoteProps = this .remoteProps;
0715: driver.remoteStop = this .remoteStop;
0716: driver.parent = this ;
0717: PluginManager.install(this , false);
0718:
0719: String remote_hosts_string = null;
0720: if (remoteStart != null) {
0721: remote_hosts_string = remoteStart.getArgument();
0722: if (remote_hosts_string == null) {
0723: remote_hosts_string = JMeterUtils.getPropDefault(
0724: "remote_hosts", //$NON-NLS-1$
0725: "127.0.0.1");//$NON-NLS-1$
0726: }
0727: }
0728: if (testFile == null) {
0729: throw new IllegalUserActionException();
0730: }
0731: String argument = testFile.getArgument();
0732: if (argument == null) {
0733: throw new IllegalUserActionException();
0734: }
0735: if (logFile == null) {
0736: driver.run(argument, null, remoteStart != null,
0737: remote_hosts_string);
0738: } else {
0739: driver.run(argument, logFile.getArgument(),
0740: remoteStart != null, remote_hosts_string);
0741: }
0742: }
0743:
0744: // run test in batch mode
0745: private void run(String testFile, String logFile,
0746: boolean remoteStart, String remote_hosts_string) {
0747: FileInputStream reader = null;
0748: try {
0749: File f = new File(testFile);
0750: if (!f.exists() || !f.isFile()) {
0751: println("Could not open " + testFile);
0752: return;
0753: }
0754: FileServer.getFileServer().setBasedir(f.getAbsolutePath());
0755:
0756: reader = new FileInputStream(f);
0757: log.info("Loading file: " + f);
0758:
0759: HashTree tree = SaveService.loadTree(reader);
0760:
0761: JMeterTreeModel treeModel = new JMeterTreeModel(
0762: new Object());// Create non-GUI version to avoid headless problems
0763: JMeterTreeNode root = (JMeterTreeNode) treeModel.getRoot();
0764: treeModel.addSubTree(tree, root);
0765:
0766: // Hack to resolve ModuleControllers in non GUI mode
0767: SearchByClass replaceableControllers = new SearchByClass(
0768: ReplaceableController.class);
0769: tree.traverse(replaceableControllers);
0770: Collection replaceableControllersRes = replaceableControllers
0771: .getSearchResults();
0772: for (Iterator iter = replaceableControllersRes.iterator(); iter
0773: .hasNext();) {
0774: ReplaceableController replaceableController = (ReplaceableController) iter
0775: .next();
0776: replaceableController.resolveReplacementSubTree(root);
0777: }
0778:
0779: // Remove the disabled items
0780: // For GUI runs this is done in Start.java
0781: convertSubTree(tree);
0782:
0783: if (logFile != null) {
0784: ResultCollector logger = new ResultCollector();
0785: logger.setFilename(logFile);
0786: tree.add(tree.getArray()[0], logger);
0787: }
0788: String summariserName = JMeterUtils.getPropDefault(
0789: "summariser.name", "");//$NON-NLS-1$
0790: if (summariserName.length() > 0) {
0791: log
0792: .info("Creating summariser <" + summariserName
0793: + ">");
0794: println("Creating summariser <" + summariserName + ">");
0795: Summariser summer = new Summariser(summariserName);
0796: tree.add(tree.getArray()[0], summer);
0797: }
0798: List engines = new LinkedList();
0799: tree.add(tree.getArray()[0], new ListenToTest(parent,
0800: (remoteStart && remoteStop) ? engines : null));
0801: println("Created the tree successfully");
0802: JMeterEngine engine = null;
0803: if (!remoteStart) {
0804: engine = new StandardJMeterEngine();
0805: engine.configure(tree);
0806: long now = System.currentTimeMillis();
0807: println("Starting the test @ " + new Date(now) + " ("
0808: + now + ")");
0809: engine.runTest();
0810: } else {
0811: java.util.StringTokenizer st = new java.util.StringTokenizer(
0812: remote_hosts_string, ",");//$NON-NLS-1$
0813: while (st.hasMoreElements()) {
0814: String el = (String) st.nextElement();
0815: println("Configuring remote engine for " + el);
0816: engines.add(doRemoteInit(el.trim(), tree));
0817: }
0818: println("Starting remote engines");
0819: long now = System.currentTimeMillis();
0820: println("Starting the test @ " + new Date(now) + " ("
0821: + now + ")");
0822: Iterator iter = engines.iterator();
0823: while (iter.hasNext()) {
0824: engine = (JMeterEngine) iter.next();
0825: engine.runTest();
0826: }
0827: println("Remote engines have been started");
0828: }
0829: } catch (Exception e) {
0830: System.out.println("Error in NonGUIDriver " + e.toString());
0831: log.error("", e);
0832: } finally {
0833: JOrphanUtils.closeQuietly(reader);
0834: }
0835: }
0836:
0837: /**
0838: * Refactored from AbstractAction.java
0839: *
0840: * @param tree
0841: */
0842: public static void convertSubTree(HashTree tree) {
0843: Iterator iter = new LinkedList(tree.list()).iterator();
0844: while (iter.hasNext()) {
0845: Object o = iter.next();
0846: if (o instanceof TestElement) {
0847: TestElement item = (TestElement) o;
0848: if (item.isEnabled()) {
0849: if (item instanceof ReplaceableController) {
0850: // HACK: force the controller to load its tree
0851: ReplaceableController rc = (ReplaceableController) item
0852: .clone();
0853: HashTree subTree = tree.getTree(item);
0854: if (subTree != null) {
0855: HashTree replacementTree = rc
0856: .getReplacementSubTree();
0857: if (replacementTree != null) {
0858: convertSubTree(replacementTree);
0859: tree.replace(item, rc);
0860: tree.set(rc, replacementTree);
0861: }
0862: } else {
0863: convertSubTree(tree.getTree(item));
0864: }
0865: } else {
0866: convertSubTree(tree.getTree(item));
0867: }
0868: } else
0869: tree.remove(item);
0870: } else {
0871: JMeterTreeNode item = (JMeterTreeNode) o;
0872: if (item.isEnabled()) {
0873: // Replacement only needs to occur when starting the engine
0874: // @see StandardJMeterEngine.run()
0875: if (item.getUserObject() instanceof ReplaceableController) {
0876: ReplaceableController rc = (ReplaceableController) item
0877: .getTestElement();
0878: HashTree subTree = tree.getTree(item);
0879:
0880: if (subTree != null) {
0881: HashTree replacementTree = rc
0882: .getReplacementSubTree();
0883: if (replacementTree != null) {
0884: convertSubTree(replacementTree);
0885: tree.replace(item, rc);
0886: tree.set(rc, replacementTree);
0887: }
0888: }
0889: } else {
0890: convertSubTree(tree.getTree(item));
0891: TestElement testElement = item.getTestElement();
0892: tree.replace(item, testElement);
0893: }
0894: } else {
0895: tree.remove(item);
0896: }
0897: }
0898: }
0899: }
0900:
0901: private JMeterEngine doRemoteInit(String hostName, HashTree testTree) {
0902: JMeterEngine engine = null;
0903: try {
0904: engine = new ClientJMeterEngine(hostName);
0905: } catch (Exception e) {
0906: log.fatalError("Failure connecting to remote host", e);
0907: System.err.println("Failure connecting to remote host" + e);
0908: System.exit(1);
0909: }
0910: engine.configure(testTree);
0911: engine.setProperties(remoteProps);
0912: return engine;
0913: }
0914:
0915: /*
0916: * Listen to test and handle tidyup after non-GUI test completes.
0917: * If running a remote test, then after waiting a few seconds for listeners to finish files,
0918: * it calls System.exit to deal with the Naming Timer Thread.
0919: */
0920: private static class ListenToTest implements TestListener,
0921: Runnable, Remoteable {
0922: private int started = 0; // keep track of remote tests
0923:
0924: //NOT YET USED private JMeter _parent;
0925:
0926: private List engines;
0927:
0928: public ListenToTest(JMeter parent, List engines) {
0929: //_parent = parent;
0930: this .engines = engines;
0931: }
0932:
0933: public synchronized void testEnded(String host) {
0934: started--;
0935: long now = System.currentTimeMillis();
0936: log
0937: .info("Finished remote host: " + host + " (" + now
0938: + ")");
0939: if (started <= 0) {
0940: Thread stopSoon = new Thread(this );
0941: stopSoon.start();
0942: }
0943: }
0944:
0945: public void testEnded() {
0946: long now = System.currentTimeMillis();
0947: println("Tidying up ... @ " + new Date(now) + " (" + now
0948: + ")");
0949: println("... end of run");
0950: }
0951:
0952: public synchronized void testStarted(String host) {
0953: started++;
0954: long now = System.currentTimeMillis();
0955: log
0956: .info("Started remote host: " + host + " (" + now
0957: + ")");
0958: }
0959:
0960: public void testStarted() {
0961: long now = System.currentTimeMillis();
0962: log
0963: .info(JMeterUtils.getResString("running_test") + " (" + now + ")");//$NON-NLS-1$
0964: }
0965:
0966: /**
0967: * This is a hack to allow listeners a chance to close their files. Must
0968: * implement a queue for sample responses tied to the engine, and the
0969: * engine won't deliver testEnded signal till all sample responses have
0970: * been delivered. Should also improve performance of remote JMeter
0971: * testing.
0972: */
0973: public void run() {
0974: long now = System.currentTimeMillis();
0975: println("Tidying up ... @ " + new Date(now) + " (" + now
0976: + ")");
0977: /*
0978: * Note: although it should not be necessary to call System.exit here, in the case
0979: * of a remote test, a Timer thread seems to be generated by the Naming.lookup()
0980: * method, and it does not die.
0981: */
0982: if (engines != null) { // it will be null unless remoteStop = true
0983: System.out.println("Exitting remote servers");
0984: Iterator it = engines.iterator();
0985: while (it.hasNext()) {
0986: JMeterEngine e = (JMeterEngine) it.next();
0987: e.exit();
0988: }
0989: }
0990: try {
0991: Thread.sleep(5000); // Allow listeners to close files
0992: } catch (InterruptedException ignored) {
0993: }
0994: println("... end of run");
0995: System.exit(0);
0996: }
0997:
0998: /**
0999: * @see TestListener#testIterationStart(LoopIterationEvent)
1000: */
1001: public void testIterationStart(LoopIterationEvent event) {
1002: // ignored
1003: }
1004: }
1005:
1006: private static void println(String str) {
1007: System.out.println(str);
1008: }
1009:
1010: private static final String[][] DEFAULT_ICONS = {
1011: {
1012: "org.apache.jmeter.control.gui.TestPlanGui", "org/apache/jmeter/images/beaker.gif" }, //$NON-NLS-1$ $NON-NLS-2$
1013: {
1014: "org.apache.jmeter.timers.gui.AbstractTimerGui", "org/apache/jmeter/images/timer.gif" }, //$NON-NLS-1$ $NON-NLS-2$
1015: {
1016: "org.apache.jmeter.threads.gui.ThreadGroupGui", "org/apache/jmeter/images/thread.gif" }, //$NON-NLS-1$ $NON-NLS-2$
1017: {
1018: "org.apache.jmeter.visualizers.gui.AbstractVisualizer", "org/apache/jmeter/images/meter.png" }, //$NON-NLS-1$ $NON-NLS-2$
1019: {
1020: "org.apache.jmeter.config.gui.AbstractConfigGui", "org/apache/jmeter/images/testtubes.png" }, //$NON-NLS-1$ $NON-NLS-2$
1021: {
1022: "org.apache.jmeter.processor.gui.AbstractPreProcessorGui", "org/apache/jmeter/images/leafnode.gif" }, //$NON-NLS-1$ $NON-NLS-2$
1023: {
1024: "org.apache.jmeter.processor.gui.AbstractPostProcessorGui", "org/apache/jmeter/images/leafnodeflip.gif" },//$NON-NLS-1$ $NON-NLS-2$
1025: {
1026: "org.apache.jmeter.control.gui.AbstractControllerGui", "org/apache/jmeter/images/knob.gif" }, //$NON-NLS-1$ $NON-NLS-2$
1027: {
1028: "org.apache.jmeter.control.gui.WorkBenchGui", "org/apache/jmeter/images/clipboard.gif" }, //$NON-NLS-1$ $NON-NLS-2$
1029: {
1030: "org.apache.jmeter.samplers.gui.AbstractSamplerGui", "org/apache/jmeter/images/pipet.png" }, //$NON-NLS-1$ $NON-NLS-2$
1031: {
1032: "org.apache.jmeter.assertions.gui.AbstractAssertionGui", "org/apache/jmeter/images/question.gif" } //$NON-NLS-1$ $NON-NLS-2$
1033: };
1034:
1035: public String[][] getIconMappings() {
1036: final String defaultIconProp = "org/apache/jmeter/images/icon.properties"; //$NON-NLS-1$
1037: String iconProp = JMeterUtils.getPropDefault(
1038: "jmeter.icons", defaultIconProp);//$NON-NLS-1$
1039: Properties p = JMeterUtils.loadProperties(iconProp);
1040: if (p == null && !iconProp.equals(defaultIconProp)) {
1041: log
1042: .info(iconProp + " not found - using "
1043: + defaultIconProp);
1044: iconProp = defaultIconProp;
1045: p = JMeterUtils.loadProperties(iconProp);
1046: }
1047: if (p == null) {
1048: log.info(iconProp + " not found - using inbuilt icon set");
1049: return DEFAULT_ICONS;
1050: }
1051: log.info("Loaded icon properties from " + iconProp);
1052: String[][] iconlist = new String[p.size()][3];
1053: Enumeration pe = p.keys();
1054: int i = 0;
1055: while (pe.hasMoreElements()) {
1056: String key = (String) pe.nextElement();
1057: String icons[] = JOrphanUtils
1058: .split(p.getProperty(key), " ");//$NON-NLS-1$
1059: iconlist[i][0] = key;
1060: iconlist[i][1] = icons[0];
1061: if (icons.length > 1)
1062: iconlist[i][2] = icons[1];
1063: i++;
1064: }
1065: return iconlist;
1066: }
1067:
1068: public String[][] getResourceBundles() {
1069: return new String[0][];
1070: }
1071:
1072: private void logProperty(String prop) {
1073: log.info(prop + "=" + System.getProperty(prop));//$NON-NLS-1$
1074: }
1075:
1076: private void logProperty(String prop, String separator) {
1077: log.info(prop + separator + System.getProperty(prop));//$NON-NLS-1$
1078: }
1079: }
|