/** * Copyright (C) 2010 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.jcr.ext.script.groovy; import org.exoplatform.commons.utils.SecurityHelper; import org.exoplatform.services.jcr.ext.resource.UnifiedNodeReference; import org.exoplatform.services.jcr.ext.script.groovy.JcrGroovyClassLoaderProvider.JcrGroovyClassLoader; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.rest.ext.groovy.SourceFile; import org.exoplatform.services.rest.ext.groovy.SourceFolder; import org.picocontainer.Startable; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.HashSet; import java.util.List; import java.util.Set; /** * JcrGroovyCompiler can load source code of groovy script from JCR and parse it * via GroovyClassLoader. * * @author <a href="mailto:andrew00x@gmail.com">Andrey Parfonov</a> * @version $Id$ */ public class JcrGroovyCompiler implements Startable { /** Logger. */ private static final Log LOG = ExoLogger.getLogger(JcrGroovyCompiler.class); protected final JcrGroovyClassLoaderProvider classLoaderProvider; protected List<GroovyScriptAddRepoPlugin> addRepoPlugins; public JcrGroovyCompiler(JcrGroovyClassLoaderProvider classLoaderProvider) { this.classLoaderProvider = classLoaderProvider; } public JcrGroovyCompiler() { this(new JcrGroovyClassLoaderProvider()); } /** * Compile Groovy source that located in <code>sourceReferences</code>. * Compiled sources can be dependent to each other and dependent to Groovy * sources that are accessible for this compiler. * * @param sourceReferences references to Groovy sources to be compiled * @return result of compilation * @throws IOException if any i/o errors occurs */ public Class<?>[] compile(UnifiedNodeReference... sourceReferences) throws IOException { return compile(null, sourceReferences); } /** * Compile Groovy source that located in <code>sourceReferences</code>. * Compiled sources can be dependent to each other and dependent to Groovy * sources that are accessible for this compiler and with additional Groovy * sources <code>src</code>. <b>NOTE</b> To be able load Groovy source files * from specified folders the following rules must be observed: * <ul> * <li>Groovy source files must be located in folder with respect to package * structure</li> * <li>Name of Groovy source files must be the same as name of class located * in file</li> * <li>Groovy source file must have extension '.groovy'</li> * </ul> * <br> * Example: If source stream that we want compile contains the following * code: * * <pre> * package c.b.a * * import a.b.c.A * * class B extends A { * // Do something. * } * </pre> * * Assume we store dependencies in JCR then URL of folder with Groovy sources * may be like this: <code>jcr://repository/workspace#/groovy-library</code>. * Then absolute path to JCR node that contains Groovy source must be as * following: <code>/groovy-library/a/b/c/A.groovy</code> * * @param src additional Groovy source location that should be added in * class-path when compile <code>sourceReferences</code> * @param sourceReferences references to Groovy sources to be compiled * @return result of compilation * @throws IOException if any i/o errors occurs */ public Class<?>[] compile(SourceFolder[] src, UnifiedNodeReference... sourceReferences) throws IOException { SourceFile[] files = new SourceFile[sourceReferences.length]; for (int i = 0; i < sourceReferences.length; i++) files[i] = new SourceFile(sourceReferences[i].getURL()); return doCompile((JcrGroovyClassLoader)classLoaderProvider.getGroovyClassLoader(src), files); } /** * Compile Groovy source that located in <code>files</code>. Compiled sources * can be dependent to each other and dependent to Groovy sources that are * accessible for this compiler and with additional Groovy sources * <code>src</code>. <b>NOTE</b> To be able load Groovy source files from * specified folders the following rules must be observed: * <ul> * <li>Groovy source files must be located in folder with respect to package * structure</li> * <li>Name of Groovy source files must be the same as name of class located * in file</li> * <li>Groovy source file must have extension '.groovy'</li> * </ul> * * @param src additional Groovy source location that should be added in * class-path when compile <code>files</code> * @param files Groovy sources to be compiled * @return result of compilation * @throws IOException if any i/o errors occurs */ public Class<?>[] compile(SourceFolder[] src, SourceFile[] files) throws IOException { return doCompile((JcrGroovyClassLoader)classLoaderProvider.getGroovyClassLoader(src), files); } /** * Compile Groovy source that located in <code>files</code>. Compiled sources * can be dependent to each other and dependent to Groovy sources that are * accessible for this compiler. * * @param files Groovy sources to be compiled * @return result of compilation * @throws IOException if any i/o errors occurs */ public Class<?>[] compile(SourceFile[] files) throws IOException { return doCompile((JcrGroovyClassLoader)classLoaderProvider.getGroovyClassLoader(), files); } @SuppressWarnings("rawtypes") private Class<?>[] doCompile(final JcrGroovyClassLoader cl, final SourceFile[] files) throws IOException { Class[] classes = SecurityHelper.doPrivilegedAction(new PrivilegedAction<Class[]>() { public Class[] run() { return cl.parseClasses(files); } }); return classes; } /** * Get URLs of classes (stored in JCR only) required to compile sources * <code>files</code>. Result array includes URLs of <code>files</code> plus * URLs of other required sources if they can be found in class-path. * * @param sources additional Groovy source location that should be added in * class-path when analyze <code>files</code> * @param files set of sources for analyzing * @return URLs * @throws IOException if any i/o errors occurs */ public URL[] getDependencies(final SourceFolder[] sources, final SourceFile[] files) throws IOException { try { return SecurityHelper.doPrivilegedExceptionAction(new PrivilegedExceptionAction<URL[]>() { public URL[] run() throws IOException { return ((JcrGroovyClassLoader)classLoaderProvider.getGroovyClassLoader()).findDependencies(sources, files); } }); } catch (PrivilegedActionException e) { Throwable cause = e.getCause(); throw (IOException)cause; } } /** * @see org.picocontainer.Startable#start() */ public void start() { if (addRepoPlugins != null && addRepoPlugins.size() > 0) { try { Set<URL> repos = new HashSet<URL>(); for (GroovyScriptAddRepoPlugin pl : addRepoPlugins) repos.addAll(pl.getRepositories()); classLoaderProvider.getGroovyClassLoader().setResourceLoader( new JcrGroovyResourceLoader(repos.toArray(new URL[repos.size()]))); } catch (MalformedURLException e) { LOG.error("Unable add groovy script repository. ", e); } } } /** * @see org.picocontainer.Startable#stop() */ public void stop() { } }