public Object build(Script script) { // this used to be synchronized, but we also used to remove the // metaclass. Since adding the metaclass is now a side effect, we // don't need to ensure the meta-class won't be observed and don't // need to hide the side effect. MetaClass scriptMetaClass = script.getMetaClass(); script.setMetaClass(new FactoryInterceptorMetaClass(scriptMetaClass, this)); script.setBinding(this); Object oldScriptName = getProxyBuilder().getVariables().get(SCRIPT_CLASS_NAME); try { getProxyBuilder().setVariable(SCRIPT_CLASS_NAME, script.getClass().getName()); return script.run(); } finally { if (oldScriptName != null) { getProxyBuilder().setVariable(SCRIPT_CLASS_NAME, oldScriptName); } else { getProxyBuilder().getVariables().remove(SCRIPT_CLASS_NAME); } } }
public Object eval(final Context context, final Bindings request, final Bindings... scopes) throws ScriptException { final Map<String, Object> bindings = mergeBindings(context, request, scopes); // Bindings so script has access to this environment. // Only initialize once. if (null == bindings.get("context")) { // add context to bindings // ctx.setAttribute("context", ctx); // direct output to ctx.getWriter // If we're wrapping with a PrintWriter here, // enable autoFlush because otherwise it might not get done! final Writer writer = engine.getWriter(); bindings.put("out", (writer instanceof PrintWriter) ? writer : new PrintWriter(writer, true)); // Not going to do this after all (at least for now). // Scripts can use context.{reader, writer, errorWriter}. // That is a modern version of System.{in, out, err} or // Console.{reader, writer}(). // // // New I/O names consistent with ScriptContext and // java.io.Console. // // ctx.setAttribute("writer", writer, ScriptContext.ENGINE_SCOPE); // // // Direct errors to ctx.getErrorWriter // final Writer errorWriter = ctx.getErrorWriter(); // ctx.setAttribute("errorWriter", (errorWriter instanceof // PrintWriter) ? // errorWriter : // new PrintWriter(errorWriter), // ScriptContext.ENGINE_SCOPE); // // // Get input from ctx.getReader // // We don't wrap with BufferedReader here because we expect that // if // // the host wants that they do it. Either way Groovy scripts will // // always have readLine because the GDK supplies it for Reader. // ctx.setAttribute("reader", ctx.getReader(), // ScriptContext.ENGINE_SCOPE); } // Fix for GROOVY-3669: Can't use several times the same JSR-223 // ScriptContext for differents groovy script // if (ctx.getWriter() != null) { // ctx.setAttribute("out", new PrintWriter(ctx.getWriter(), true), // DefaultScriptContext.REQUEST_SCOPE); // } try { Script scriptObject = engine.createScript(scriptName, new Binding(bindings)); // create a Map of MethodClosures from this new script object Method[] methods = scriptObject.getClass().getMethods(); final Map<String, Closure> closures = new HashMap<String, Closure>(); for (Method m : methods) { String name = m.getName(); closures.put(name, new MethodClosure(scriptObject, name)); } MetaClass oldMetaClass = scriptObject.getMetaClass(); /* * We override the MetaClass of this script object so that we can * forward calls to global closures (of previous or future "eval" * calls) This gives the illusion of working on the same "global" * scope. */ scriptObject.setMetaClass( new DelegatingMetaClass(oldMetaClass) { @Override public Object invokeMethod(Object object, String name, Object args) { if (args == null) { return invokeMethod(object, name, MetaClassHelper.EMPTY_ARRAY); } if (args instanceof Tuple) { return invokeMethod(object, name, ((Tuple) args).toArray()); } if (args instanceof Object[]) { return invokeMethod(object, name, (Object[]) args); } else { return invokeMethod(object, name, new Object[] {args}); } } @Override public Object invokeMethod(Object object, String name, Object[] args) { try { return super.invokeMethod(object, name, args); } catch (MissingMethodException mme) { return callGlobal(name, args, bindings); } } @Override public Object invokeStaticMethod(Object object, String name, Object[] args) { try { return super.invokeStaticMethod(object, name, args); } catch (MissingMethodException mme) { return callGlobal(name, args, bindings); } } private Object callGlobal(String name, Object[] args, Map<String, Object> ctx) { Closure closure = closures.get(name); if (closure != null) { return closure.call(args); } else { // Look for closure valued variable in the // given ScriptContext. If available, call it. Object value = ctx.get(name); if (value instanceof Closure) { return ((Closure) value).call(args); } // else fall thru.. } throw new MissingMethodException(name, getClass(), args); } }); try { return scriptObject.run(); } catch (Exception e) { throw new ScriptThrownException(e.getMessage(), e); } } catch (ScriptException e) { throw e; } catch (CompilationFailedException e) { throw new ScriptCompilationException(e); } catch (Exception e) { throw new ScriptException(e); } finally { // Fix for GROOVY-3669: Can't use several times the same JSR-223 // ScriptContext for different groovy script // Groovy's scripting engine implementation adds those two variables // in the binding // but should clean up afterwards // ctx.removeAttribute("context", // DefaultScriptContext.REQUEST_SCOPE); // ctx.removeAttribute("out", DefaultScriptContext.REQUEST_SCOPE); } }