I only gave a few snippets of the code base to give some ideas for the kind of framework that could be built.
It's actually a very large app, and for example, includes runtime configurable validation expressions through a custom Struts' validator, which populates the bsh namespace and runs a series of validations. Elsewhere the same namespace is used for other calulations and a rules engine.
The technique is indeed to set all values into the namespace before evaluation. I have to run a series of expressions, so it's more economical to set the namespace vars, run all the expressions, and then extract the vars back into the main code afterwards, rather than worrying about each expression individually.

The Spring application context is set once at start up, to be "bsh.shared.applicationContext". This means it is accessible accross namespaces. When a new Calculator is initialized, it is a convenience to eval "context = bsh.shared.applicationContext"
A bean is then accessed using context.getBean("beanname").

2008/6/23 Brent Easton <b.easton@exemail.com.au>:
Hi Stewart,
 
The variables to be evaluated are set up in different parts of the application. Some are system defined, some are user defined. While it is not possible to list them all in advance and use set() to allocate values, any given value can be obtained by through a single getProperty(key) call in the main application.
 
I am actually happy now with the implementation I have for the Expression evaluator. I evaluate once, find the undefined variables, set those with values from the application, the evaluate again to get a result.
 
What I need for the general purpose script handling is a way to programatically callback into my application to determine the value of a given property.
 
I don't fully understand your example, but it is tantalizing. I feel I am missing something. Am I right that you are basically 'Settting' all available values into the NameSpace prior to evaluation? How do you reference a Spring value in a BeanShell?
 
Thanks,
Brent.
 

*********** REPLY SEPARATOR ***********


On 23/06/2008 at 12:31 PM Stewart Cambridge wrote:

I'm not sure if I understood your requirements correctly.

It seems to me that if your scripts are truly and totally dynamic, then you cannot know before hand what variables will need to be populated into the interpreter context - the user could write anything.

I once used beanshell to create a perfectly workable expression calculator and rules engine for a financial services application.
I had such mechanisms as:
-------------
public interface Populatable extends Dynamic
{
  void setCode(String code);
  Class getContextClass();
}
-------------
public interface Calculatable extends Populatable, Checkable
{
    String getCalculation();
}
-------------
public interface CalculatorContext
{
    Populatable[] getPopulatables();
}
-------------
/**
 * Populates the Spring ApplicationContext into the Beanshell's shared namespace.
 */
public class ApplicationContextAwareCalculatorPopulator implements ApplicationContextAware
{
  public void setApplicationContext( ApplicationContext cxt )
    throws BeansException
  {
    try
    {
      Interpreter i = new Interpreter();
      i.set( Calculator.APPLICATION_CONTEXT, cxt);
      i.eval("bsh.shared." + Calculator.APPLICATION_CONTEXT + " = " + Calculator.APPLICATION_CONTEXT);
    }
    catch(EvalError e)
    {
.......
    }
  }
}
-------------
//Calculator class
  public Calculator( CalculatorHolder holder )
    throws EvalError
  {
    this.interpreter = new Interpreter();
interpreter.getNameSpace().importCommands( Calculator.class.getPackage().getName() ); // pre-defined calculator functions
// gives access to spring beans via bsh.shared
    interpreter.eval( APPLICATION_CONTEXT + " = bsh.shared." + APPLICATION_CONTEXT );
.......
}

  public void init( CalculatorContext populateFromContext )
    throws EvalError
  {
    Populatable[] populatables = populateFromContext.getPopulatables();
    for ( int i = 0; i < populatables.length; i++ )
    {
........etc
-------------

I have absolutely no idea if this is helpful to you or not.
The calculation expressions and "populatables" were configured through a database, so that ultimately there was a well defined set of expression variables. The expressions could be changed at runtime by the user and did reference objects (the "populatables") fetched via hibernate and injected into the calculator context, as well as all the Spring beans my app was built upon.
If the user tried to reference a populatable or bean that didn't exist, I'd just catch the EvalError and report it to the user.
Ultimately, what else can you do? If the user doesn't set "xpos", then it's an error, plain and simple.
You've constructed a list of unset variable that the user needs to set before the expression is run, but if the user doesn't set one, what can you do that's different to a general EvalError? I suppose you can pinpoint specifically what is not set, and include that in your friendly error message.



____________________________________________________________
Brent Easton                      
Analyst/Programmer                              
University of Western Sydney                                  
Email:
b.easton@uws.edu.au