blob: e8c8b25b2abd6236a29e26de83fd20a794f6367b [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.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JMeterError;
import org.apache.jorphan.util.JMeterException;
import org.apache.log.Logger;
/**
* BeanShell setup function - encapsulates all the access to the BeanShell
* Interpreter in a single class.
*
* The class uses dynamic class loading to access BeanShell, which means that
* all the source files can be built without needing access to the bsh jar.
*
* If the beanshell jar is not present at run-time, an error will be logged
*
*/
public class BeanShellInterpreter {
private static final Logger log = LoggingManager.getLoggerForClass();
private static final Method bshGet;
private static final Method bshSet;
private static final Method bshEval;
private static final Method bshSource;
private static final Class<?> bshClass;
private static final String BSH_INTERPRETER = "bsh.Interpreter"; //$NON-NLS-1$
static {
// Temporary copies, so can set the final ones
Method get = null, eval = null, set = null, source = null;
Class<?> clazz = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
clazz = loader.loadClass(BSH_INTERPRETER);
Class<String> string = String.class;
Class<Object> object = Object.class;
get = clazz.getMethod("get", //$NON-NLS-1$
new Class[] { string });
eval = clazz.getMethod("eval", //$NON-NLS-1$
new Class[] { string });
set = clazz.getMethod("set", //$NON-NLS-1$
new Class[] { string, object });
source = clazz.getMethod("source", //$NON-NLS-1$
new Class[] { string });
} catch (ClassNotFoundException|SecurityException | NoSuchMethodException e) {
log.error("Beanshell Interpreter not found", e);
} finally {
bshEval = eval;
bshGet = get;
bshSet = set;
bshSource = source;
bshClass = clazz;
}
}
// This class is not serialised
private Object bshInstance = null; // The interpreter instance for this class
private final String initFile; // Script file to initialize the Interpreter with
private final Logger logger; // Logger to use during initialization and script run
public BeanShellInterpreter() throws ClassNotFoundException {
this(null, null);
}
/**
*
* @param init initialisation file
* @param _log logger to pass to interpreter
* @throws ClassNotFoundException when beanshell can not be instantiated
*/
public BeanShellInterpreter(String init, Logger _log) throws ClassNotFoundException {
initFile = init;
logger = _log;
init();
}
// Called from ctor, so must be private (or final, but it does not seem useful elsewhere)
private void init() throws ClassNotFoundException {
if (bshClass == null) {
throw new ClassNotFoundException(BSH_INTERPRETER);
}
try {
bshInstance = bshClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
log.error("Can't instantiate BeanShell", e);
throw new ClassNotFoundException("Can't instantiate BeanShell", e);
}
if (logger != null) {// Do this before starting the script
try {
set("log", logger);//$NON-NLS-1$
} catch (JMeterException e) {
log.warn("Can't set logger variable", e);
}
}
if (initFile != null && initFile.length() > 0) {
String fileToUse=initFile;
// Check file so we can distinguish file error from script error
File in = new File(fileToUse);
if (!in.exists()){// Cannot find the file locally, so try the bin directory
fileToUse=JMeterUtils.getJMeterHome()
+File.separator+"bin" // $NON-NLS-1$
+File.separator+initFile;
in = new File(fileToUse);
if (!in.exists()) {
log.warn("Cannot find init file: "+initFile);
}
}
if (!in.canRead()) {
log.warn("Cannot read init file: "+fileToUse);
}
try {
source(fileToUse);
} catch (JMeterException e) {
log.warn("Cannot source init file: "+fileToUse,e);
}
}
}
/**
* Resets the BeanShell interpreter.
*
* @throws ClassNotFoundException if interpreter cannot be instantiated
*/
public void reset() throws ClassNotFoundException {
init();
}
private Object bshInvoke(Method m, Object[] o, boolean shouldLog) throws JMeterException {
Object r = null;
final String errorString = "Error invoking bsh method: ";
try {
r = m.invoke(bshInstance, o);
} catch (IllegalArgumentException | IllegalAccessException e) { // Programming error
final String message = errorString + m.getName();
log.error(message);
throw new JMeterError(message, e);
} catch (InvocationTargetException e) { // Can occur at run-time
// could be caused by the bsh Exceptions:
// EvalError, ParseException or TargetError
String message = errorString + m.getName();
Throwable cause = e.getCause();
if (cause != null) {
message += "\t" + cause.getLocalizedMessage();
}
if (shouldLog) {
log.error(message);
}
throw new JMeterException(message, e);
}
return r;
}
public Object eval(String s) throws JMeterException {
return bshInvoke(bshEval, new Object[] { s }, true);
}
public Object evalNoLog(String s) throws JMeterException {
return bshInvoke(bshEval, new Object[] { s }, false);
}
public Object set(String s, Object o) throws JMeterException {
return bshInvoke(bshSet, new Object[] { s, o }, true);
}
public Object set(String s, boolean b) throws JMeterException {
return bshInvoke(bshSet, new Object[] { s, Boolean.valueOf(b) }, true);
}
public Object source(String s) throws JMeterException {
return bshInvoke(bshSource, new Object[] { s }, true);
}
public Object get(String s) throws JMeterException {
return bshInvoke(bshGet, new Object[] { s }, true);
}
// For use by Unit Tests
public static boolean isInterpreterPresent(){
return bshClass != null;
}
}