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

step.expressions.ExpressionHandler Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (C) 2020, exense GmbH
 *  
 * This file is part of STEP
 *  
 * STEP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * STEP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *  
 * You should have received a copy of the GNU Affero General Public License
 * along with STEP.  If not, see .
 ******************************************************************************/
package step.expressions;

import java.util.Map;
import java.util.Map.Entry;

import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import groovy.lang.Binding;
import groovy.lang.MissingPropertyException;
import groovy.lang.Script;

public class ExpressionHandler implements AutoCloseable {
		
	private static Logger logger = LoggerFactory.getLogger(ExpressionHandler.class);
	
	private final GroovyPool groovyPool;
	
	private final Integer executionTimeWarningTreshold;
	
	private final String scriptBaseClass;
	
	public ExpressionHandler() {
		this(null);
	}
	
	public ExpressionHandler(String scriptBaseClass) {
		this(scriptBaseClass, null, 1000, -1);
	}
	
	public ExpressionHandler(String scriptBaseClass, Integer executionTimeWarningTreshold, int poolMaxTotal, int poolMaxIdle) {
		super();
		this.scriptBaseClass = scriptBaseClass;
		this.groovyPool = new GroovyPool(scriptBaseClass, poolMaxTotal, poolMaxIdle);
		this.executionTimeWarningTreshold = executionTimeWarningTreshold;
	}

	public Object evaluateGroovyExpression(String expression, Map bindings) {
		try {
			Object result;
			try {			
				if(logger.isDebugEnabled()) {
					logger.debug("Groovy evaluation:\n" + expression);
				}
				
				Binding binding = new Binding(); 
				
				if(bindings!=null) {
					for(Entry varEntry : bindings.entrySet()) {
						Object value =  varEntry.getValue();
						binding.setVariable(varEntry.getKey(), value);
					}				
				}
				
				long t1 = System.currentTimeMillis();	
				try {
					GroovyPoolEntry entry = groovyPool.borrowShell(expression);
					try {
						Script script = entry.getScript();
						script.setBinding(binding);
						result = script.run();
					} finally {
						if(entry!=null && entry.getScript()!=null) {
							// Release bindings to avoid references to be kept by the pool
							entry.getScript().setBinding(new Binding());							
						}
						groovyPool.returnShell(entry);
					}
				} catch (MultipleCompilationErrorsException e) {
					for (Object error : e.getErrorCollector().getErrors()) {
						if(error instanceof SyntaxErrorMessage) {
							String message = ((SyntaxErrorMessage) error).getCause().getMessage();
							if(message != null && message.contains("unable to resolve class") && scriptBaseClass != null && message.contains(scriptBaseClass)) {
								throw new Exception("Unable to resolve groovy macro class '" + scriptBaseClass + 
										"'. Please ensure that the groovy script containing your custom macros is available in the classpath.", e);
							}
						}
					}
					throw e;
				} catch (Exception e) {
					throw e;
				}
				long duration = System.currentTimeMillis()-t1;
				
				Integer warnThreshold = executionTimeWarningTreshold;
				if(warnThreshold!=null && duration > warnThreshold) {
					logger.warn("Groovy-Evaluation of following expression took " + duration + ".ms: "+ expression);
				} else {
					if(logger.isDebugEnabled()) {
						logger.debug("Groovy-Evaluation of following expression took " + duration + ".ms: "+ expression);
					}
				}
				
				if(logger.isDebugEnabled()) {
					logger.debug("Groovy result:\n" + result);
				}
				
				return result;
			} catch (CompilationFailedException cfe) {
				throw new RuntimeException(
						"Error while compiling groovy expression: '" + expression + "'", cfe);
			} catch (MissingPropertyException mpe) {
				throw new RuntimeException(
						"Error while resolving groovy properties in expression: '" + expression + "'. The property '" + mpe.getProperty() + "' could not be found (or accessed). Make sure that the property is defined as variable or parameter and accesible in current scope.", mpe);
			} catch (Exception e){
				throw new RuntimeException(
						"Error while running groovy expression: '" + expression + "'", e);
			}
		} catch (Exception e) {
			if(logger.isDebugEnabled()) {
				logger.error("An error occurred while evaluation groovy expression " + expression, e);
			}
			throw e;
		}
	}

	@Override
	public void close() {
		groovyPool.close();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy