/* * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * bstefanescu * * $Id$ */ package org.nuxeo.ecm.webengine.scripting; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.runtime.InvokerHelper; import groovy.lang.Binding; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyCodeSource; import groovy.lang.GroovyRuntimeException; import groovy.lang.Script; /** * For Groovy we are not using the javax.script API because we need more control over debug mode and script class * loader. Groovy scritps will be processed by this class * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class GroovyScripting { protected final GroovyClassLoader loader; // compiled script class cache protected Map<File, Entry> cache; public GroovyScripting(ClassLoader parent) { CompilerConfiguration cfg = new CompilerConfiguration(); loader = new GroovyClassLoader(parent, cfg); cache = new ConcurrentHashMap<File, Entry>(); } public GroovyScripting(ClassLoader parent, CompilerConfiguration cfg) { loader = new GroovyClassLoader(parent, cfg); cache = new ConcurrentHashMap<File, Entry>(); } public void addClasspath(String cp) { loader.addClasspath(cp); } public void addClasspathUrl(URL cp) { loader.addURL(cp); } public void clearCache() { cache = new ConcurrentHashMap<File, Entry>(); loader.clearCache(); } public Class<?> loadClass(String className) throws ClassNotFoundException { return loader.loadClass(className); } public GroovyClassLoader getGroovyClassLoader() { return loader; } // TODO add debug mode : return new GroovyShell(new Binding(args)).evaluate(script.getFile()); ? public Object eval(File file, Map<String, Object> context) throws GroovyRuntimeException { return eval(file, context == null ? new Binding() : new Binding(context)); } public Object eval(File file, Binding context) throws GroovyRuntimeException { // convenience out global variable (for compatibility with scripts on webengine 1.0 beta) context.setVariable("out", System.out); return getScript(file, context).run(); } public Class<?> compile(File file) throws GroovyRuntimeException { GroovyCodeSource codeSource; try { codeSource = new GroovyCodeSource(file); } catch (IOException e) { throw new GroovyRuntimeException(e); } // do not use cache - we are maintaining our proper cache - based on lastModified return loader.parseClass(codeSource, false); } public Script getScript(File file, Binding context) throws GroovyRuntimeException { Class<?> klass = null; long lastModified = file.lastModified(); Entry entry = cache.get(file); if (entry != null && entry.lastModified == lastModified) { // in cache - use it klass = entry.klass; } else { // not in cache or invalid klass = compile(file); // compile cache.put(file, new Entry(klass, lastModified)); // cache it } return InvokerHelper.createScript(klass, context); } static class Entry { final long lastModified; final Class<?> klass; Entry(Class<?> klass, long lastModified) { this.klass = klass; this.lastModified = lastModified; } } }