001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.scripting.groovy;
016    
017    import com.liferay.portal.kernel.cache.PortalCache;
018    import com.liferay.portal.kernel.cache.SingleVMPoolUtil;
019    import com.liferay.portal.kernel.concurrent.ConcurrentReferenceKeyHashMap;
020    import com.liferay.portal.kernel.memory.FinalizeManager;
021    import com.liferay.portal.kernel.scripting.BaseScriptingExecutor;
022    import com.liferay.portal.kernel.scripting.ExecutionException;
023    import com.liferay.portal.kernel.scripting.ScriptingException;
024    import com.liferay.portal.kernel.util.AggregateClassLoader;
025    import com.liferay.portal.kernel.util.ArrayUtil;
026    import com.liferay.portal.util.ClassLoaderUtil;
027    
028    import groovy.lang.Binding;
029    import groovy.lang.GroovyShell;
030    import groovy.lang.Script;
031    
032    import java.util.HashMap;
033    import java.util.Map;
034    import java.util.Set;
035    import java.util.concurrent.ConcurrentMap;
036    
037    /**
038     * @author Alberto Montero
039     * @author Brian Wing Shun Chan
040     */
041    public class GroovyExecutor extends BaseScriptingExecutor {
042    
043            @Override
044            public void clearCache() {
045                    _portalCache.removeAll();
046            }
047    
048            @Override
049            public Map<String, Object> eval(
050                            Set<String> allowedClasses, Map<String, Object> inputObjects,
051                            Set<String> outputNames, String script, ClassLoader... classLoaders)
052                    throws ScriptingException {
053    
054                    if (allowedClasses != null) {
055                            throw new ExecutionException(
056                                    "Constrained execution not supported for Groovy");
057                    }
058    
059                    Script compiledScript = getCompiledScript(script, classLoaders);
060    
061                    Binding binding = new Binding(inputObjects);
062    
063                    compiledScript.setBinding(binding);
064    
065                    compiledScript.run();
066    
067                    if (outputNames == null) {
068                            return null;
069                    }
070    
071                    Map<String, Object> outputObjects = new HashMap<String, Object>();
072    
073                    for (String outputName : outputNames) {
074                            outputObjects.put(outputName, binding.getVariable(outputName));
075                    }
076    
077                    return outputObjects;
078            }
079    
080            @Override
081            public String getLanguage() {
082                    return _LANGUAGE;
083            }
084    
085            protected Script getCompiledScript(
086                    String script, ClassLoader[] classLoaders) {
087    
088                    GroovyShell groovyShell = getGroovyShell(classLoaders);
089    
090                    String key = String.valueOf(script.hashCode());
091    
092                    Script compiledScript = _portalCache.get(key);
093    
094                    if (compiledScript == null) {
095                            compiledScript = groovyShell.parse(script);
096    
097                            _portalCache.put(key, compiledScript);
098                    }
099    
100                    return compiledScript;
101            }
102    
103            protected GroovyShell getGroovyShell(ClassLoader[] classLoaders) {
104                    if (ArrayUtil.isEmpty(classLoaders)) {
105                            if (_groovyShell == null) {
106                                    synchronized (this) {
107                                            if (_groovyShell == null) {
108                                                    _groovyShell = new GroovyShell();
109                                            }
110                                    }
111                            }
112    
113                            return _groovyShell;
114                    }
115    
116                    ClassLoader aggregateClassLoader =
117                            AggregateClassLoader.getAggregateClassLoader(
118                                    ClassLoaderUtil.getPortalClassLoader(), classLoaders);
119    
120                    GroovyShell groovyShell = _groovyShells.get(aggregateClassLoader);
121    
122                    if (groovyShell == null) {
123                            groovyShell = new GroovyShell(aggregateClassLoader);
124    
125                            GroovyShell oldGroovyShell = _groovyShells.putIfAbsent(
126                                    aggregateClassLoader, groovyShell);
127    
128                            if (oldGroovyShell != null) {
129                                    groovyShell = oldGroovyShell;
130                            }
131                    }
132    
133                    return groovyShell;
134            }
135    
136            private static final String _CACHE_NAME = GroovyExecutor.class.getName();
137    
138            private static final String _LANGUAGE = "groovy";
139    
140            private volatile GroovyShell _groovyShell = new GroovyShell();
141            private final ConcurrentMap<ClassLoader, GroovyShell> _groovyShells =
142                    new ConcurrentReferenceKeyHashMap<ClassLoader, GroovyShell>(
143                            FinalizeManager.WEAK_REFERENCE_FACTORY);
144            private final PortalCache<String, Script> _portalCache =
145                    SingleVMPoolUtil.getCache(_CACHE_NAME);
146    
147    }