/*
* (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library 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
* Lesser General Public License for more details.
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.nuxeo.ecm.webengine.scripting;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import groovy.lang.Script;
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;
/**
* 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 IOException {
return eval(file, context == null ? new Binding() : new Binding(context));
}
public Object eval(File file, Binding context) throws IOException {
// 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 IOException {
GroovyCodeSource codeSource = new GroovyCodeSource(file);
// 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 IOException {
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;
}
}
}