blob: 12f7a401ed338c9ad1e1960bd945af6d15d9c678 [file] [log] [blame]
/*
* 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.util;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Vector;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import org.apache.commons.io.IOUtils;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.reflect.ClassFinder;
import org.apache.jorphan.test.UnitTestManager;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.log.Logger;
import org.apache.oro.text.MalformedCachePatternException;
import org.apache.oro.text.PatternCacheLRU;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.xml.sax.XMLReader;
/**
* This class contains the static utility methods used by JMeter.
*
*/
public class JMeterUtils implements UnitTestManager {
private static final Logger log = LoggingManager.getLoggerForClass();
// Note: cannot use a static variable here, because that would be processed before the JMeter properties
// have been defined (Bug 52783)
private static class LazyPatternCacheHolder {
public static final PatternCacheLRU INSTANCE = new PatternCacheLRU(
getPropDefault("oro.patterncache.size",1000), // $NON-NLS-1$
new Perl5Compiler());
}
private static final String EXPERT_MODE_PROPERTY = "jmeter.expertMode"; // $NON-NLS-1$
private static final String ENGLISH_LANGUAGE = Locale.ENGLISH.getLanguage();
private static volatile Properties appProperties;
private static final Vector<LocaleChangeListener> localeChangeListeners = new Vector<>();
private static volatile Locale locale;
private static volatile ResourceBundle resources;
// What host am I running on?
//@GuardedBy("this")
private static String localHostIP = null;
//@GuardedBy("this")
private static String localHostName = null;
//@GuardedBy("this")
private static String localHostFullName = null;
private static volatile boolean ignoreResorces = false; // Special flag for use in debugging resources
private static final ThreadLocal<Perl5Matcher> localMatcher = new ThreadLocal<Perl5Matcher>() {
@Override
protected Perl5Matcher initialValue() {
return new Perl5Matcher();
}
};
/**
* Gets Perl5Matcher for this thread.
* @return the {@link Perl5Matcher} for this thread
*/
public static Perl5Matcher getMatcher() {
return localMatcher.get();
}
/**
* This method is used by the init method to load the property file that may
* even reside in the user space, or in the classpath under
* org.apache.jmeter.jmeter.properties.
*
* The method also initialises logging and sets up the default Locale
*
* TODO - perhaps remove?
* [still used
*
* @param file
* the file to load
* @return the Properties from the file
* @see #getJMeterProperties()
* @see #loadJMeterProperties(String)
* @see #initLogging()
* @see #initLocale()
*/
public static Properties getProperties(String file) {
loadJMeterProperties(file);
initLogging();
initLocale();
return appProperties;
}
/**
* Initialise JMeter logging
*/
public static void initLogging() {
LoggingManager.initializeLogging(appProperties);
}
/**
* Initialise the JMeter Locale
*/
public static void initLocale() {
String loc = appProperties.getProperty("language"); // $NON-NLS-1$
if (loc != null) {
String []parts = JOrphanUtils.split(loc,"_");// $NON-NLS-1$
if (parts.length==2) {
setLocale(new Locale(parts[0], parts[1]));
} else {
setLocale(new Locale(loc, "")); // $NON-NLS-1$
}
} else {
setLocale(Locale.getDefault());
}
}
/**
* Load the JMeter properties file; if not found, then
* default to "org/apache/jmeter/jmeter.properties" from the classpath
*
* <p>
* c.f. loadProperties
*
* @param file Name of the file from which the JMeter properties should be loaded
*/
public static void loadJMeterProperties(String file) {
Properties p = new Properties(System.getProperties());
InputStream is = null;
try {
File f = new File(file);
is = new FileInputStream(f);
p.load(is);
} catch (IOException e) {
try {
is =
ClassLoader.getSystemResourceAsStream("org/apache/jmeter/jmeter.properties"); // $NON-NLS-1$
if (is == null) {
throw new RuntimeException("Could not read JMeter properties file:"+file);
}
p.load(is);
} catch (IOException ex) {
// JMeter.fail("Could not read internal resource. " +
// "Archive is broken.");
}
} finally {
JOrphanUtils.closeQuietly(is);
}
appProperties = p;
}
/**
* This method loads a property file that may reside in the user space, or
* in the classpath
*
* @param file
* the file to load
* @return the Properties from the file, may be null (e.g. file not found)
*/
public static Properties loadProperties(String file) {
return loadProperties(file, null);
}
/**
* This method loads a property file that may reside in the user space, or
* in the classpath
*
* @param file
* the file to load
* @param defaultProps a set of default properties
* @return the Properties from the file; if it could not be processed, the defaultProps are returned.
*/
public static Properties loadProperties(String file, Properties defaultProps) {
Properties p = new Properties(defaultProps);
InputStream is = null;
try {
File f = new File(file);
is = new FileInputStream(f);
p.load(is);
} catch (IOException e) {
try {
final URL resource = JMeterUtils.class.getClassLoader().getResource(file);
if (resource == null) {
log.warn("Cannot find " + file);
return defaultProps;
}
is = resource.openStream();
if (is == null) {
log.warn("Cannot open " + file);
return defaultProps;
}
p.load(is);
} catch (IOException ex) {
log.warn("Error reading " + file + " " + ex.toString());
return defaultProps;
}
} finally {
JOrphanUtils.closeQuietly(is);
}
return p;
}
public static PatternCacheLRU getPatternCache() {
return LazyPatternCacheHolder.INSTANCE;
}
/**
* Get a compiled expression from the pattern cache (READ_ONLY).
*
* @param expression regular expression to be looked up
* @return compiled pattern
*
* @throws MalformedCachePatternException (Runtime)
* This should be caught for expressions that may vary (e.g. user input)
*
*/
public static Pattern getPattern(String expression) throws MalformedCachePatternException {
return getPattern(expression, Perl5Compiler.READ_ONLY_MASK);
}
/**
* Get a compiled expression from the pattern cache.
*
* @param expression RE
* @param options e.g. {@link Perl5Compiler#READ_ONLY_MASK READ_ONLY_MASK}
* @return compiled pattern
*
* @throws MalformedCachePatternException (Runtime)
* This should be caught for expressions that may vary (e.g. user input)
*
*/
public static Pattern getPattern(String expression, int options) throws MalformedCachePatternException {
return LazyPatternCacheHolder.INSTANCE.getPattern(expression, options);
}
@Override
public void initializeProperties(String file) {
System.out.println("Initializing Properties: " + file);
getProperties(file);
}
/**
* Convenience method for
* {@link ClassFinder#findClassesThatExtend(String[], Class[], boolean)}
* with the option to include inner classes in the search set to false
* and the path list is derived from JMeterUtils.getSearchPaths().
*
* @param superClass - single class to search for
* @return List of Strings containing discovered class names.
* @throws IOException when the used {@link ClassFinder} throws one while searching for the class
*/
public static List<String> findClassesThatExtend(Class<?> superClass)
throws IOException {
return ClassFinder.findClassesThatExtend(getSearchPaths(), new Class[]{superClass}, false);
}
/**
* Generate a list of paths to search.
* The output array always starts with
* JMETER_HOME/lib/ext
* and is followed by any paths obtained from the "search_paths" JMeter property.
*
* @return array of path strings
*/
public static String[] getSearchPaths() {
String p = JMeterUtils.getPropDefault("search_paths", null); // $NON-NLS-1$
String[] result = new String[1];
if (p != null) {
String[] paths = p.split(";"); // $NON-NLS-1$
result = new String[paths.length + 1];
System.arraycopy(paths, 0, result, 1, paths.length);
}
result[0] = getJMeterHome() + "/lib/ext"; // $NON-NLS-1$
return result;
}
/**
* Provide random numbers
*
* @param r -
* the upper bound (exclusive)
* @return a random <code>int</code>
*/
public static int getRandomInt(int r) {
return ThreadLocalRandom.current().nextInt(r);
}
/**
* Changes the current locale: re-reads resource strings and notifies
* listeners.
*
* @param loc -
* new locale
*/
public static void setLocale(Locale loc) {
log.info("Setting Locale to " + loc.toString());
/*
* See bug 29920. getBundle() defaults to the property file for the
* default Locale before it defaults to the base property file, so we
* need to change the default Locale to ensure the base property file is
* found.
*/
Locale def = null;
boolean isDefault = false; // Are we the default language?
if (loc.getLanguage().equals(ENGLISH_LANGUAGE)) {
isDefault = true;
def = Locale.getDefault();
// Don't change locale from en_GB to en
if (!def.getLanguage().equals(ENGLISH_LANGUAGE)) {
Locale.setDefault(Locale.ENGLISH);
} else {
def = null; // no need to reset Locale
}
}
if (loc.toString().equals("ignoreResources")){ // $NON-NLS-1$
log.warn("Resource bundles will be ignored");
ignoreResorces = true;
// Keep existing settings
} else {
ignoreResorces = false;
ResourceBundle resBund = ResourceBundle.getBundle("org.apache.jmeter.resources.messages", loc); // $NON-NLS-1$
resources = resBund;
locale = loc;
final Locale resBundLocale = resBund.getLocale();
if (isDefault || resBundLocale.equals(loc)) {// language change worked
// Check if we at least found the correct language:
} else if (resBundLocale.getLanguage().equals(loc.getLanguage())) {
log.info("Could not find resources for '"+loc.toString()+"', using '"+resBundLocale.toString()+"'");
} else {
log.error("Could not find resources for '"+loc.toString()+"'");
}
}
notifyLocaleChangeListeners();
/*
* Reset Locale if necessary so other locales are properly handled
*/
if (def != null) {
Locale.setDefault(def);
}
}
/**
* Gets the current locale.
*
* @return current locale
*/
public static Locale getLocale() {
return locale;
}
public static void addLocaleChangeListener(LocaleChangeListener listener) {
localeChangeListeners.add(listener);
}
public static void removeLocaleChangeListener(LocaleChangeListener listener) {
localeChangeListeners.remove(listener);
}
/**
* Notify all listeners interested in locale changes.
*
*/
private static void notifyLocaleChangeListeners() {
LocaleChangeEvent event = new LocaleChangeEvent(JMeterUtils.class, locale);
@SuppressWarnings("unchecked") // clone will produce correct type
// TODO but why do we need to clone the list?
// ANS: to avoid possible ConcurrentUpdateException when unsubscribing
// Could perhaps avoid need to clone by using a modern concurrent list
Vector<LocaleChangeListener> listeners = (Vector<LocaleChangeListener>) localeChangeListeners.clone();
for (LocaleChangeListener listener : listeners) {
listener.localeChanged(event);
}
}
/**
* Gets the resource string for this key.
*
* If the resource is not found, a warning is logged
*
* @param key
* the key in the resource file
* @return the resource string if the key is found; otherwise, return
* "[res_key="+key+"]"
*/
public static String getResString(String key) {
return getResStringDefault(key, RES_KEY_PFX + key + "]"); // $NON-NLS-1$
}
/**
* Gets the resource string for this key in Locale.
*
* If the resource is not found, a warning is logged
*
* @param key
* the key in the resource file
* @param forcedLocale Force a particular locale
* @return the resource string if the key is found; otherwise, return
* "[res_key="+key+"]"
* @since 2.7
*/
public static String getResString(String key, Locale forcedLocale) {
return getResStringDefault(key, RES_KEY_PFX + key + "]", // $NON-NLS-1$
forcedLocale);
}
public static final String RES_KEY_PFX = "[res_key="; // $NON-NLS-1$
/**
* Gets the resource string for this key.
*
* If the resource is not found, a warning is logged
*
* @param key
* the key in the resource file
* @param defaultValue -
* the default value
*
* @return the resource string if the key is found; otherwise, return the
* default
* @deprecated Only intended for use in development; use
* getResString(String) normally
*/
@Deprecated
public static String getResString(String key, String defaultValue) {
return getResStringDefault(key, defaultValue);
}
/*
* Helper method to do the actual work of fetching resources; allows
* getResString(S,S) to be deprecated without affecting getResString(S);
*/
private static String getResStringDefault(String key, String defaultValue) {
return getResStringDefault(key, defaultValue, null);
}
/*
* Helper method to do the actual work of fetching resources; allows
* getResString(S,S) to be deprecated without affecting getResString(S);
*/
private static String getResStringDefault(String key, String defaultValue, Locale forcedLocale) {
if (key == null) {
return null;
}
// Resource keys cannot contain spaces, and are forced to lower case
String resKey = key.replace(' ', '_'); // $NON-NLS-1$ // $NON-NLS-2$
resKey = resKey.toLowerCase(java.util.Locale.ENGLISH);
String resString = null;
try {
ResourceBundle bundle = resources;
if(forcedLocale != null) {
bundle = ResourceBundle.getBundle("org.apache.jmeter.resources.messages", forcedLocale); // $NON-NLS-1$
}
if (bundle.containsKey(resKey)) {
resString = bundle.getString(resKey);
} else {
log.warn("ERROR! Resource string not found: [" + resKey + "]");
resString = defaultValue;
}
if (ignoreResorces ){ // Special mode for debugging resource handling
return "["+key+"]";
}
} catch (MissingResourceException mre) {
if (ignoreResorces ){ // Special mode for debugging resource handling
return "[?"+key+"?]";
}
log.warn("ERROR! Resource string not found: [" + resKey + "]", mre);
resString = defaultValue;
}
return resString;
}
/**
* To get I18N label from properties file
*
* @param key
* in messages.properties
* @return I18N label without (if exists) last colon ':' and spaces
*/
public static String getParsedLabel(String key) {
String value = JMeterUtils.getResString(key);
return value.replaceFirst("(?m)\\s*?:\\s*$", ""); // $NON-NLS-1$ $NON-NLS-2$
}
/**
* Get the locale name as a resource.
* Does not log an error if the resource does not exist.
* This is needed to support additional locales, as they won't be in existing messages files.
*
* @param locale name
* @return the locale display name as defined in the current Locale or the original string if not present
*/
public static String getLocaleString(String locale){
// All keys in messages.properties are lowercase (historical reasons?)
String resKey = locale.toLowerCase(java.util.Locale.ENGLISH);
if (resources.containsKey(resKey)) {
return resources.getString(resKey);
}
return locale;
}
/**
* This gets the currently defined appProperties. It can only be called
* after the {@link #getProperties(String)} or {@link #loadJMeterProperties(String)}
* method has been called.
*
* @return The JMeterProperties value,
* may be null if {@link #loadJMeterProperties(String)} has not been called
* @see #getProperties(String)
* @see #loadJMeterProperties(String)
*/
public static Properties getJMeterProperties() {
return appProperties;
}
/**
* This looks for the requested image in the classpath under
* org.apache.jmeter.images.&lt;name&gt;
*
* @param name
* Description of Parameter
* @return The Image value
*/
public static ImageIcon getImage(String name) {
try {
URL url = JMeterUtils.class.getClassLoader().getResource(
"org/apache/jmeter/images/" + name.trim());
if(url != null) {
return new ImageIcon(url); // $NON-NLS-1$
} else {
log.warn("no icon for " + name);
return null;
}
} catch (NoClassDefFoundError | InternalError e) {// Can be returned by headless hosts
log.info("no icon for " + name + " " + e.getMessage());
return null;
}
}
/**
* This looks for the requested image in the classpath under
* org.apache.jmeter.images.<em>&lt;name&gt;</em>, and also sets the description
* of the image, which is useful if the icon is going to be placed
* on the clipboard.
*
* @param name
* the name of the image
* @param description
* the description of the image
* @return The Image value
*/
public static ImageIcon getImage(String name, String description) {
ImageIcon icon = getImage(name);
if(icon != null) {
icon.setDescription(description);
}
return icon;
}
public static String getResourceFileAsText(String name) {
BufferedReader fileReader = null;
try {
String lineEnd = System.getProperty("line.separator"); // $NON-NLS-1$
InputStream is = JMeterUtils.class.getClassLoader().getResourceAsStream(name);
if(is != null) {
fileReader = new BufferedReader(new InputStreamReader(is));
StringBuilder text = new StringBuilder();
String line;
while ((line = fileReader.readLine()) != null) {
text.append(line);
text.append(lineEnd);
}
// Done by finally block: fileReader.close();
return text.toString();
} else {
return ""; // $NON-NLS-1$
}
} catch (IOException e) {
return ""; // $NON-NLS-1$
} finally {
IOUtils.closeQuietly(fileReader);
}
}
/**
* Creates the vector of Timers plugins.
*
* @param properties
* Description of Parameter
* @return The Timers value
* @deprecated (3.0) not used + pre-java 1.2 collection
*/
@Deprecated
public static Vector<Object> getTimers(Properties properties) {
return instantiate(getVector(properties, "timer."), // $NON-NLS-1$
"org.apache.jmeter.timers.Timer"); // $NON-NLS-1$
}
/**
* Creates the vector of visualizer plugins.
*
* @param properties
* Description of Parameter
* @return The Visualizers value
* @deprecated (3.0) not used + pre-java 1.2 collection
*/
@Deprecated
public static Vector<Object> getVisualizers(Properties properties) {
return instantiate(getVector(properties, "visualizer."), // $NON-NLS-1$
"org.apache.jmeter.visualizers.Visualizer"); // $NON-NLS-1$
}
/**
* Creates a vector of SampleController plugins.
*
* @param properties
* The properties with information about the samplers
* @return The Controllers value
* @deprecated (3.0) not used + pre-java 1.2 collection
*/
// TODO - does not appear to be called directly
@Deprecated
public static Vector<Object> getControllers(Properties properties) {
String name = "controller."; // $NON-NLS-1$
Vector<Object> v = new Vector<>();
Enumeration<?> names = properties.keys();
while (names.hasMoreElements()) {
String prop = (String) names.nextElement();
if (prop.startsWith(name)) {
Object o = instantiate(properties.getProperty(prop),
"org.apache.jmeter.control.SamplerController"); // $NON-NLS-1$
v.addElement(o);
}
}
return v;
}
/**
* Create a string of class names for a particular SamplerController
*
* @param properties
* The properties with info about the samples.
* @param name
* The name of the sampler controller.
* @return The TestSamples value
* @deprecated (3.0) not used
*/
@Deprecated
public static String[] getTestSamples(Properties properties, String name) {
Vector<String> vector = getVector(properties, name + ".testsample"); // $NON-NLS-1$
return vector.toArray(new String[vector.size()]);
}
/**
* Create an instance of an org.xml.sax.Parser based on the default props.
*
* @return The XMLParser value
* @deprecated (3.0) was only called by UserParameterXMLParser.getXMLParameters which has been removed in 3.0
*/
@Deprecated
public static XMLReader getXMLParser() {
final String parserName = getPropDefault("xml.parser", // $NON-NLS-1$
"org.apache.xerces.parsers.SAXParser"); // $NON-NLS-1$
return (XMLReader) instantiate(parserName,
"org.xml.sax.XMLReader"); // $NON-NLS-1$
}
/**
* Creates the vector of alias strings.
* <p>
* The properties will be filtered by all values starting with
* <code>alias.</code>. The matching entries will be used for the new
* {@link Hashtable} while the prefix <code>alias.</code> will be stripped
* of the keys.
*
* @param properties
* the input values
* @return The Alias value
* @deprecated (3.0) not used
*/
@Deprecated
public static Hashtable<String, String> getAlias(Properties properties) {
return getHashtable(properties, "alias."); // $NON-NLS-1$
}
/**
* Creates a vector of strings for all the properties that start with a
* common prefix.
*
* @param properties
* Description of Parameter
* @param name
* Description of Parameter
* @return The Vector value
*/
public static Vector<String> getVector(Properties properties, String name) {
Vector<String> v = new Vector<>();
Enumeration<?> names = properties.keys();
while (names.hasMoreElements()) {
String prop = (String) names.nextElement();
if (prop.startsWith(name)) {
v.addElement(properties.getProperty(prop));
}
}
return v;
}
/**
* Creates a table of strings for all the properties that start with a
* common prefix.
* <p>
* So if you have {@link Properties} <code>prop</code> with two entries, say
* <ul>
* <li>this.test</li>
* <li>that.something</li>
* </ul>
* And would call this method with a <code>prefix</code> <em>this</em>, the
* result would be a new {@link Hashtable} with one entry, which key would
* be <em>test</em>.
*
* @param properties
* input to search
* @param prefix
* to match against properties
* @return a Hashtable where the keys are the original matching keys with
* the prefix removed
* @deprecated (3.0) not used
*/
@Deprecated
public static Hashtable<String, String> getHashtable(Properties properties, String prefix) {
Hashtable<String, String> t = new Hashtable<>();
Enumeration<?> names = properties.keys();
final int length = prefix.length();
while (names.hasMoreElements()) {
String prop = (String) names.nextElement();
if (prop.startsWith(prefix)) {
t.put(prop.substring(length), properties.getProperty(prop));
}
}
return t;
}
/**
* Get a int value with default if not present.
*
* @param propName
* the name of the property.
* @param defaultVal
* the default value.
* @return The PropDefault value
*/
public static int getPropDefault(String propName, int defaultVal) {
int ans;
try {
ans = Integer.parseInt(appProperties.getProperty(propName, Integer.toString(defaultVal)).trim());
} catch (Exception e) {
log.warn("Exception '"+ e.getMessage()+ "' occurred when fetching int property:'"+propName+"', defaulting to:"+defaultVal);
ans = defaultVal;
}
return ans;
}
/**
* Get a boolean value with default if not present.
*
* @param propName
* the name of the property.
* @param defaultVal
* the default value.
* @return The PropDefault value
*/
public static boolean getPropDefault(String propName, boolean defaultVal) {
boolean ans;
try {
String strVal = appProperties.getProperty(propName, Boolean.toString(defaultVal)).trim();
if (strVal.equalsIgnoreCase("true") || strVal.equalsIgnoreCase("t")) { // $NON-NLS-1$ // $NON-NLS-2$
ans = true;
} else if (strVal.equalsIgnoreCase("false") || strVal.equalsIgnoreCase("f")) { // $NON-NLS-1$ // $NON-NLS-2$
ans = false;
} else {
ans = Integer.parseInt(strVal) == 1;
}
} catch (Exception e) {
log.warn("Exception '"+ e.getMessage()+ "' occurred when fetching boolean property:'"+propName+"', defaulting to:"+defaultVal);
ans = defaultVal;
}
return ans;
}
/**
* Get a long value with default if not present.
*
* @param propName
* the name of the property.
* @param defaultVal
* the default value.
* @return The PropDefault value
*/
public static long getPropDefault(String propName, long defaultVal) {
long ans;
try {
ans = Long.parseLong(appProperties.getProperty(propName, Long.toString(defaultVal)).trim());
} catch (Exception e) {
log.warn("Exception '"+ e.getMessage()+ "' occurred when fetching long property:'"+propName+"', defaulting to:"+defaultVal);
ans = defaultVal;
}
return ans;
}
/**
* Get a String value with default if not present.
*
* @param propName
* the name of the property.
* @param defaultVal
* the default value.
* @return The PropDefault value
*/
public static String getPropDefault(String propName, String defaultVal) {
String ans = defaultVal;
try
{
String value = appProperties.getProperty(propName, defaultVal);
if(value != null) {
ans = value.trim();
}
} catch (Exception e) {
log.warn("Exception '"+ e.getMessage()+ "' occurred when fetching String property:'"+propName+"', defaulting to:"+defaultVal);
ans = defaultVal;
}
return ans;
}
/**
* Get the value of a JMeter property.
*
* @param propName
* the name of the property.
* @return the value of the JMeter property, or null if not defined
*/
public static String getProperty(String propName) {
String ans = null;
try {
ans = appProperties.getProperty(propName);
} catch (Exception e) {
log.warn("Exception '"+ e.getMessage()+ "' occurred when fetching String property:'"+propName+"'");
ans = null;
}
return ans;
}
/**
* Set a String value
*
* @param propName
* the name of the property.
* @param propValue
* the value of the property
* @return the previous value of the property
*/
public static Object setProperty(String propName, String propValue) {
return appProperties.setProperty(propName, propValue);
}
/**
* Sets the selection of the JComboBox to the Object 'name' from the list in
* namVec.
* NOTUSED?
* @param properties not used at the moment
* @param combo {@link JComboBox} to work on
* @param namVec List of names, which are displayed in <code>combo</code>
* @param name Name, that is to be selected. It has to be in <code>namVec</code>
*/
@Deprecated
public static void selJComboBoxItem(Properties properties, JComboBox<?> combo, Vector<?> namVec, String name) {
int idx = namVec.indexOf(name);
combo.setSelectedIndex(idx);
// Redisplay.
combo.updateUI();
}
/**
* Instatiate an object and guarantee its class.
*
* @param className
* The name of the class to instantiate.
* @param impls
* The name of the class it must be an instance of
* @return an instance of the class, or null if instantiation failed or the class did not implement/extend as required
* @deprecated (3.0) not used out of this class
*/
// TODO probably not needed
@Deprecated
public static Object instantiate(String className, String impls) {
if (className != null) {
className = className.trim();
}
if (impls != null) {
impls = impls.trim();
}
try {
Class<?> c = Class.forName(impls);
try {
Class<?> o = Class.forName(className);
Object res = o.newInstance();
if (c.isInstance(res)) {
return res;
}
throw new IllegalArgumentException(className + " is not an instance of " + impls);
} catch (ClassNotFoundException e) {
log.error("Error loading class " + className + ": class is not found");
} catch (IllegalAccessException e) {
log.error("Error loading class " + className + ": does not have access");
} catch (InstantiationException e) {
log.error("Error loading class " + className + ": could not instantiate");
} catch (NoClassDefFoundError e) {
log.error("Error loading class " + className + ": couldn't find class " + e.getMessage());
}
} catch (ClassNotFoundException e) {
log.error("Error loading class " + impls + ": was not found.");
}
return null;
}
/**
* Instantiate a vector of classes
*
* @param v
* Description of Parameter
* @param className
* Description of Parameter
* @return Description of the Returned Value
* @deprecated (3.0) not used out of this class
*/
@Deprecated
public static Vector<Object> instantiate(Vector<String> v, String className) {
Vector<Object> i = new Vector<>();
try {
Class<?> c = Class.forName(className);
Enumeration<String> elements = v.elements();
while (elements.hasMoreElements()) {
String name = elements.nextElement();
try {
Object o = Class.forName(name).newInstance();
if (c.isInstance(o)) {
i.addElement(o);
}
} catch (ClassNotFoundException e) {
log.error("Error loading class " + name + ": class is not found");
} catch (IllegalAccessException e) {
log.error("Error loading class " + name + ": does not have access");
} catch (InstantiationException e) {
log.error("Error loading class " + name + ": could not instantiate");
} catch (NoClassDefFoundError e) {
log.error("Error loading class " + name + ": couldn't find class " + e.getMessage());
}
}
} catch (ClassNotFoundException e) {
log.error("Error loading class " + className + ": class is not found");
}
return i;
}
/**
* Create a button with the netscape style
*
* @param name
* Description of Parameter
* @param listener
* Description of Parameter
* @return Description of the Returned Value
* @deprecated (3.0) not used
*/
@Deprecated
public static JButton createButton(String name, ActionListener listener) {
JButton button = new JButton(getImage(name + ".on.gif")); // $NON-NLS-1$
button.setDisabledIcon(getImage(name + ".off.gif")); // $NON-NLS-1$
button.setRolloverIcon(getImage(name + ".over.gif")); // $NON-NLS-1$
button.setPressedIcon(getImage(name + ".down.gif")); // $NON-NLS-1$
button.setActionCommand(name);
button.addActionListener(listener);
button.setRolloverEnabled(true);
button.setFocusPainted(false);
button.setBorderPainted(false);
button.setOpaque(false);
button.setPreferredSize(new Dimension(24, 24));
return button;
}
/**
* Create a button with the netscape style
*
* @param name
* Description of Parameter
* @param listener
* Description of Parameter
* @return Description of the Returned Value
* @deprecated (3.0) not used
*/
@Deprecated
public static JButton createSimpleButton(String name, ActionListener listener) {
JButton button = new JButton(getImage(name + ".gif")); // $NON-NLS-1$
button.setActionCommand(name);
button.addActionListener(listener);
button.setFocusPainted(false);
button.setBorderPainted(false);
button.setOpaque(false);
button.setPreferredSize(new Dimension(25, 25));
return button;
}
/**
* Report an error through a dialog box.
* Title defaults to "error_title" resource string
* @param errorMsg - the error message.
*/
public static void reportErrorToUser(String errorMsg) {
reportErrorToUser(errorMsg, JMeterUtils.getResString("error_title")); // $NON-NLS-1$
}
/**
* Report an error through a dialog box.
*
* @param errorMsg - the error message.
* @param titleMsg - title string
*/
public static void reportErrorToUser(String errorMsg, String titleMsg) {
if (errorMsg == null) {
errorMsg = "Unknown error - see log file";
log.warn("Unknown error", new Throwable("errorMsg == null"));
}
GuiPackage instance = GuiPackage.getInstance();
if (instance == null) {
System.out.println(errorMsg);
return; // Done
}
try {
JOptionPane.showMessageDialog(instance.getMainFrame(),
errorMsg,
titleMsg,
JOptionPane.ERROR_MESSAGE);
} catch (HeadlessException e) {
log.warn("reportErrorToUser(\"" + errorMsg + "\") caused", e);
}
}
/**
* Finds a string in an array of strings and returns the
*
* @param array
* Array of strings.
* @param value
* String to compare to array values.
* @return Index of value in array, or -1 if not in array.
* @deprecated (3.0) not used
*/
//TODO - move to JOrphanUtils?
@Deprecated
public static int findInArray(String[] array, String value) {
int count = -1;
int index = -1;
if (array != null && value != null) {
while (++count < array.length) {
if (array[count] != null && array[count].equals(value)) {
index = count;
break;
}
}
}
return index;
}
/**
* Takes an array of strings and a tokenizer character, and returns a string
* of all the strings concatenated with the tokenizer string in between each
* one.
*
* @param splittee
* Array of Objects to be concatenated.
* @param splitChar
* Object to unsplit the strings with.
* @return Array of all the tokens.
*/
//TODO - move to JOrphanUtils?
public static String unsplit(Object[] splittee, Object splitChar) {
StringBuilder retVal = new StringBuilder();
int count = -1;
while (++count < splittee.length) {
if (splittee[count] != null) {
retVal.append(splittee[count]);
}
if (count + 1 < splittee.length && splittee[count + 1] != null) {
retVal.append(splitChar);
}
}
return retVal.toString();
}
// End Method
/**
* Takes an array of strings and a tokenizer character, and returns a string
* of all the strings concatenated with the tokenizer string in between each
* one.
*
* @param splittee
* Array of Objects to be concatenated.
* @param splitChar
* Object to unsplit the strings with.
* @param def
* Default value to replace null values in array.
* @return Array of all the tokens.
*/
//TODO - move to JOrphanUtils?
public static String unsplit(Object[] splittee, Object splitChar, String def) {
StringBuilder retVal = new StringBuilder();
int count = -1;
while (++count < splittee.length) {
if (splittee[count] != null) {
retVal.append(splittee[count]);
} else {
retVal.append(def);
}
if (count + 1 < splittee.length) {
retVal.append(splitChar);
}
}
return retVal.toString();
}
/**
* Get the JMeter home directory - does not include the trailing separator.
*
* @return the home directory
*/
public static String getJMeterHome() {
return jmDir;
}
/**
* Get the JMeter bin directory - does not include the trailing separator.
*
* @return the bin directory
*/
public static String getJMeterBinDir() {
return jmBin;
}
public static void setJMeterHome(String home) {
jmDir = home;
jmBin = jmDir + File.separator + "bin"; // $NON-NLS-1$
}
// TODO needs to be synch? Probably not changed after threads have started
private static String jmDir; // JMeter Home directory (excludes trailing separator)
private static String jmBin; // JMeter bin directory (excludes trailing separator)
/**
* Gets the JMeter Version.
*
* @return the JMeter version string
*/
public static String getJMeterVersion() {
return JMeterVersion.getVERSION();
}
/**
* Gets the JMeter copyright.
*
* @return the JMeter copyright string
*/
public static String getJMeterCopyright() {
return JMeterVersion.getCopyRight();
}
/**
* Determine whether we are in 'expert' mode. Certain features may be hidden
* from user's view unless in expert mode.
*
* @return true iif we're in expert mode
*/
public static boolean isExpertMode() {
return JMeterUtils.getPropDefault(EXPERT_MODE_PROPERTY, false);
}
/**
* Find a file in the current directory or in the JMeter bin directory.
*
* @param fileName the name of the file to find
* @return File object
*/
public static File findFile(String fileName){
File f =new File(fileName);
if (!f.exists()){
f=new File(getJMeterBinDir(),fileName);
}
return f;
}
/**
* Returns the cached result from calling
* InetAddress.getLocalHost().getHostAddress()
*
* @return String representation of local IP address
*/
public static synchronized String getLocalHostIP(){
if (localHostIP == null) {
getLocalHostDetails();
}
return localHostIP;
}
/**
* Returns the cached result from calling
* InetAddress.getLocalHost().getHostName()
*
* @return local host name
*/
public static synchronized String getLocalHostName(){
if (localHostName == null) {
getLocalHostDetails();
}
return localHostName;
}
/**
* Returns the cached result from calling
* InetAddress.getLocalHost().getCanonicalHostName()
*
* @return local host name in canonical form
*/
public static synchronized String getLocalHostFullName(){
if (localHostFullName == null) {
getLocalHostDetails();
}
return localHostFullName;
}
private static void getLocalHostDetails(){
InetAddress localHost=null;
try {
localHost = InetAddress.getLocalHost();
} catch (UnknownHostException e1) {
log.error("Unable to get local host IP address.", e1);
return; // TODO - perhaps this should be a fatal error?
}
localHostIP=localHost.getHostAddress();
localHostName=localHost.getHostName();
localHostFullName=localHost.getCanonicalHostName();
}
/**
* Split line into name/value pairs and remove colon ':'
*
* @param headers
* multi-line string headers
* @return a map name/value for each header
*/
public static LinkedHashMap<String, String> parseHeaders(String headers) {
LinkedHashMap<String, String> linkedHeaders = new LinkedHashMap<>();
String[] list = headers.split("\n"); // $NON-NLS-1$
for (String header : list) {
int colon = header.indexOf(':'); // $NON-NLS-1$
if (colon <= 0) {
linkedHeaders.put(header, ""); // Empty value // $NON-NLS-1$
} else {
linkedHeaders.put(header.substring(0, colon).trim(), header
.substring(colon + 1).trim());
}
}
return linkedHeaders;
}
/**
* Run the runnable in AWT Thread if current thread is not AWT thread
* otherwise runs call {@link SwingUtilities#invokeAndWait(Runnable)}
* @param runnable {@link Runnable}
*/
public static void runSafe(Runnable runnable) {
runSafe(true, runnable);
}
/**
* Run the runnable in AWT Thread if current thread is not AWT thread
* otherwise runs call {@link SwingUtilities#invokeAndWait(Runnable)}
* @param synchronous flag, whether we will wait for the AWT Thread to finish its job.
* @param runnable {@link Runnable}
*/
public static void runSafe(boolean synchronous, Runnable runnable) {
if(SwingUtilities.isEventDispatchThread()) {
runnable.run();
} else {
if (synchronous) {
try {
SwingUtilities.invokeAndWait(runnable);
} catch (InterruptedException e) {
log.warn("Interrupted in thread "
+ Thread.currentThread().getName(), e);
} catch (InvocationTargetException e) {
throw new Error(e);
}
} else {
SwingUtilities.invokeLater(runnable);
}
}
}
/**
* Help GC by triggering GC and finalization
*/
public static void helpGC() {
System.gc();
System.runFinalization();
}
/**
* Hack to make matcher clean the two internal buffers it keeps in memory which size is equivalent to
* the unzipped page size
* @param matcher {@link Perl5Matcher}
* @param pattern Pattern
*/
public static void clearMatcherMemory(Perl5Matcher matcher, Pattern pattern) {
try {
if (pattern != null) {
matcher.matches("", pattern); // $NON-NLS-1$
}
} catch (Exception e) {
// NOOP
}
}
/**
* Provide info, whether we run in HiDPI mode
* @return {@code true} if we run in HiDPI mode, {@code false} otherwise
*/
public static boolean getHiDPIMode() {
return JMeterUtils.getPropDefault("jmeter.hidpi.mode", false); // $NON-NLS-1$
}
/**
* Provide info about the HiDPI scale factor
* @return the factor by which we should scale elements for HiDPI mode
*/
public static double getHiDPIScaleFactor() {
return Double.parseDouble(JMeterUtils.getPropDefault("jmeter.hidpi.scale.factor", "1.0")); // $NON-NLS-1$ $NON-NLS-2$
}
/**
* Apply HiDPI mode management to {@link JTable}
* @param table the {@link JTable} which should be adapted for HiDPI mode
*/
public static void applyHiDPI(JTable table) {
if (JMeterUtils.getHiDPIMode()) {
table.setRowHeight((int) Math.round(table.getRowHeight() * JMeterUtils.getHiDPIScaleFactor()));
}
}
}