All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jmeter.util.JSR223TestElement 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.util;

import io.metersphere.jmeter.LoadJarService;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.map.LRUMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.jmeter.NewDriver;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jorphan.util.JOrphanUtils;
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;

/**
 * Base class for JSR223 Test elements
 * 

* 将当前类加载器设置为 loader ,解决由系统类加载器加载的 JMeter 无法动态加载 jar 包问题 * 同时隔离在 beanshell 中访问由系统类加载器加载的其他类 */ public abstract class JSR223TestElement extends ScriptingTestElement implements Serializable, TestStateListener { private static final long serialVersionUID = 233L; private LoadJarService loadJarService; private static final Logger logger = LoggerFactory.getLogger(JSR223TestElement.class); /** * Cache of compiled scripts */ private static final Map compiledScriptsCache = Collections.synchronizedMap( new LRUMap<>(JMeterUtils.getPropDefault("jsr223.compiled_scripts_cache_size", 100))); /** * If not empty then script in ScriptText will be compiled and cached */ private String cacheKey = ""; /** md5 of the script, used as an unique key for the cache */ private String scriptMd5 = null; /** * Initialization On Demand Holder pattern */ private static class LazyHolder { private LazyHolder() { super(); } public static final ScriptEngineManager INSTANCE = new ScriptEngineManager(); } /** * @return ScriptEngineManager singleton */ public static ScriptEngineManager getInstance() { return LazyHolder.INSTANCE; } protected JSR223TestElement() { super(); } /** * @return {@link ScriptEngine} for language defaulting to groovy if language is not set * @throws ScriptException when no {@link ScriptEngine} could be found */ protected ScriptEngine getScriptEngine() throws ScriptException { String lang = getScriptLanguageWithDefault(); ScriptEngine scriptEngine = getInstance().getEngineByName(lang); if (scriptEngine == null) { throw new ScriptException("Cannot find engine named: '" + lang + "', ensure you set language field in JSR223 Test Element: "+getName()); } return scriptEngine; } /** * @return script language or DEFAULT_SCRIPT_LANGUAGE if none is set */ private String getScriptLanguageWithDefault() { String lang = getScriptLanguage(); if (StringUtils.isNotEmpty(lang)) { return lang; } return DEFAULT_SCRIPT_LANGUAGE; } /** * Populate variables to be passed to scripts * @param bindings Bindings */ protected void populateBindings(Bindings bindings) { final String label = getName(); final String fileName = getFilename(); final String scriptParameters = getParameters(); // Use actual class name for log final Logger elementLogger = LoggerFactory.getLogger(getClass().getName() + "."+getName()); bindings.put("log", elementLogger); // $NON-NLS-1$ (this name is fixed) bindings.put("Label", label); // $NON-NLS-1$ (this name is fixed) bindings.put("FileName", fileName); // $NON-NLS-1$ (this name is fixed) bindings.put("Parameters", scriptParameters); // $NON-NLS-1$ (this name is fixed) String[] args = JOrphanUtils.split(scriptParameters, " ");//$NON-NLS-1$ bindings.put("args", args); // $NON-NLS-1$ (this name is fixed) // Add variables for access to context and variables JMeterContext jmctx = JMeterContextService.getContext(); bindings.put("ctx", jmctx); // $NON-NLS-1$ (this name is fixed) JMeterVariables vars = jmctx.getVariables(); bindings.put("vars", vars); // $NON-NLS-1$ (this name is fixed) Properties props = JMeterUtils.getJMeterProperties(); bindings.put("props", props); // $NON-NLS-1$ (this name is fixed) // For use in debugging: bindings.put("OUT", System.out); // NOSONAR $NON-NLS-1$ (this name is fixed) // Most subclasses will need these: Sampler sampler = jmctx.getCurrentSampler(); bindings.put("sampler", sampler); // $NON-NLS-1$ (this name is fixed) SampleResult prev = jmctx.getPreviousResult(); bindings.put("prev", prev); // $NON-NLS-1$ (this name is fixed) } /** * This method will run inline script or file script with special behaviour for file script: * - If ScriptEngine implements Compilable script will be compiled and cached * - If not if will be run * @param scriptEngine ScriptEngine * @param pBindings {@link Bindings} might be null * @return Object returned by script * @throws IOException when reading the script fails * @throws ScriptException when compiling or evaluation of the script fails */ protected Object processFileOrScript(ScriptEngine scriptEngine, final Bindings pBindings) throws IOException, ScriptException { if (scriptEngine instanceof GroovyScriptEngineImpl) { try { GroovyScriptEngineImpl groovyScriptEngine = (GroovyScriptEngineImpl) scriptEngine; if (loadJarService == null) { loadJarService = Class.forName("io.metersphere.api.jmeter.MsGroovyLoadJarService", true, Thread.currentThread().getContextClassLoader()) .asSubclass(LoadJarService.class) .getDeclaredConstructor().newInstance(); if (loadJarService != null) { loadJarService.loadGroovyJar(groovyScriptEngine.getClassLoader()); } } } catch (ClassNotFoundException ignore) { // 忽略这个异常 } catch (Exception e) { LoggerUtil.error("加载Groovy jar失败:", e); } } Bindings bindings = pBindings; if (bindings == null) { bindings = scriptEngine.createBindings(); } populateBindings(bindings); File scriptFile = new File(getFilename()); // Hack: bsh-2.0b5.jar BshScriptEngine implements Compilable but throws // "java.lang.Error: unimplemented" boolean supportsCompilable = scriptEngine instanceof Compilable && !"bsh.engine.BshScriptEngine".equals(scriptEngine.getClass().getName()); // NOSONAR // $NON-NLS-1$ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // 将当前类加载器设置为 loader ,解决由系统类加载器加载的 JMeter 无法动态加载 jar 包问题 // 同时隔离在 beanshell 中访问由系统类加载器加载的其他类 if (MethodUtils.getAccessibleMethod(NewDriver.class, "setContextClassLoader") != null) { NewDriver.setContextClassLoader(); } try { if (!StringUtils.isEmpty(getFilename())) { if (scriptFile.exists() && scriptFile.canRead()) { if (supportsCompilable) { String newCacheKey = getScriptLanguage() + "#" + // $NON-NLS-1$ scriptFile.getAbsolutePath() + "#" + // $NON-NLS-1$ scriptFile.lastModified(); CompiledScript compiledScript = compiledScriptsCache.get(newCacheKey); if (compiledScript == null) { synchronized (compiledScriptsCache) { compiledScript = compiledScriptsCache.get(newCacheKey); if (compiledScript == null) { try (BufferedReader fileReader = Files.newBufferedReader(scriptFile.toPath())) { compiledScript = ((Compilable) scriptEngine).compile(fileReader); compiledScriptsCache.put(newCacheKey, compiledScript); } } } } return compiledScript.eval(bindings); } else { try (BufferedReader fileReader = Files.newBufferedReader(scriptFile.toPath())) { return scriptEngine.eval(fileReader, bindings); } } } else { throw new ScriptException("Script file '" + scriptFile.getAbsolutePath() + "' does not exist or is unreadable for element:" + getName()); } } else if (!StringUtils.isEmpty(getScript())) { if (supportsCompilable && !ScriptingBeanInfoSupport.FALSE_AS_STRING.equals(cacheKey)) { computeScriptMD5(); CompiledScript compiledScript = compiledScriptsCache.get(this.scriptMd5); if (compiledScript == null) { synchronized (compiledScriptsCache) { compiledScript = compiledScriptsCache.get(this.scriptMd5); if (compiledScript == null) { compiledScript = ((Compilable) scriptEngine).compile(getScript()); compiledScriptsCache.put(this.scriptMd5, compiledScript); } } } return compiledScript.eval(bindings); } else { return scriptEngine.eval(getScript(), bindings); } } else { throw new ScriptException("Both script file and script text are empty for element:" + getName()); } } catch (ScriptException ex) { Throwable rootCause = ex.getCause(); if (isStopCondition(rootCause)) { throw (RuntimeException) ex.getCause(); } else { throw ex; } } finally { // 将当前类加载器设置回来,避免加载无法加载到系统类加载加载类 Thread.currentThread().setContextClassLoader(contextClassLoader); } } /** * @return boolean true if element is not compilable or if compilation succeeds * @throws IOException if script is missing * @throws ScriptException if compilation fails */ public boolean compile() throws ScriptException, IOException { String lang = getScriptLanguageWithDefault(); ScriptEngine scriptEngine = getInstance().getEngineByName(lang); boolean supportsCompilable = scriptEngine instanceof Compilable && !"bsh.engine.BshScriptEngine".equals(scriptEngine.getClass().getName()); // NOSONAR // $NON-NLS-1$ if (!supportsCompilable) { return true; } if (!StringUtils.isEmpty(getScript())) { try { ((Compilable) scriptEngine).compile(getScript()); return true; } catch (ScriptException e) { // NOSONAR logger.error("Error compiling script for test element {}, error:{}", getName(), e.getMessage()); return false; } } else { File scriptFile = new File(getFilename()); try (BufferedReader fileReader = Files.newBufferedReader(scriptFile.toPath())) { try { ((Compilable) scriptEngine).compile(fileReader); return true; } catch (ScriptException e) { // NOSONAR logger.error("Error compiling script for test element {}, error:{}", getName(), e.getMessage()); return false; } } } } /** * compute MD5 if it is null */ private void computeScriptMD5() { // compute the md5 of the script if needed if (scriptMd5 == null) { scriptMd5 = DigestUtils.md5Hex(getScript()); } } /** * @return the cacheKey */ public String getCacheKey() { return cacheKey; } /** * @param cacheKey the cacheKey to set */ public void setCacheKey(String cacheKey) { this.cacheKey = cacheKey; } /** * @see org.apache.jmeter.testelement.TestStateListener#testStarted() */ @Override public void testStarted() { // NOOP } /** * @see org.apache.jmeter.testelement.TestStateListener#testStarted(java.lang.String) */ @Override public void testStarted(String host) { // NOOP } /** * @see org.apache.jmeter.testelement.TestStateListener#testEnded() */ @Override public void testEnded() { testEnded(""); } /** * @see org.apache.jmeter.testelement.TestStateListener#testEnded(java.lang.String) */ @Override public void testEnded(String host) { compiledScriptsCache.clear(); this.scriptMd5 = null; } public String getScriptLanguage() { return scriptLanguage; } public void setScriptLanguage(String s) { scriptLanguage = s; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy