001    /**
002     * Copyright (c) 2000-2013 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.velocity;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
020    import com.liferay.portal.kernel.security.pacl.NotPrivileged;
021    import com.liferay.portal.kernel.util.PropsKeys;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Validator;
025    import com.liferay.portal.kernel.velocity.VelocityContext;
026    import com.liferay.portal.kernel.velocity.VelocityEngine;
027    import com.liferay.portal.kernel.velocity.VelocityVariablesUtil;
028    import com.liferay.portal.template.TemplateControlContext;
029    import com.liferay.portal.util.ClassLoaderUtil;
030    import com.liferay.portal.util.PropsUtil;
031    import com.liferay.portal.util.PropsValues;
032    
033    import java.io.Writer;
034    
035    import java.security.AccessControlContext;
036    import java.security.AccessController;
037    import java.security.PrivilegedAction;
038    import java.security.PrivilegedExceptionAction;
039    
040    import java.util.Map;
041    import java.util.concurrent.ConcurrentHashMap;
042    
043    import org.apache.commons.collections.ExtendedProperties;
044    import org.apache.velocity.Template;
045    import org.apache.velocity.runtime.RuntimeConstants;
046    import org.apache.velocity.runtime.resource.ResourceManager;
047    import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
048    import org.apache.velocity.runtime.resource.util.StringResourceRepository;
049    
050    /**
051     * @author Raymond Aug??
052     */
053    @DoPrivileged
054    public class VelocityEngineImpl implements VelocityEngine {
055    
056            public VelocityEngineImpl() {
057            }
058    
059            @Override
060            public void clearClassLoader(ClassLoader classLoader) {
061                    _classLoaderToolsContextsMap.remove(classLoader);
062            }
063    
064            @Override
065            public void flushTemplate(String velocityTemplateId) {
066                    StringResourceRepository stringResourceRepository =
067                            StringResourceLoader.getRepository();
068    
069                    if (stringResourceRepository != null) {
070                            stringResourceRepository.removeStringResource(velocityTemplateId);
071                    }
072    
073                    LiferayResourceCacheUtil.remove(
074                            _getResourceCacheKey(velocityTemplateId));
075            }
076    
077            @NotPrivileged
078            @Override
079            public VelocityContext getEmptyContext() {
080                    return new VelocityContextImpl();
081            }
082    
083            @NotPrivileged
084            @Override
085            public VelocityContext getRestrictedToolsContext() {
086                    return _getToolsContext(_RESTRICTED);
087            }
088    
089            @NotPrivileged
090            @Override
091            public VelocityContext getStandardToolsContext() {
092                    return _getToolsContext(_STANDARD);
093            }
094    
095            public TemplateControlContext getTemplateControlContext() {
096                    return _pacl.getTemplateControlContext();
097            }
098    
099            @NotPrivileged
100            @Override
101            public VelocityContext getWrappedClassLoaderToolsContext() {
102                    return new VelocityContextImpl(_getToolsContext(_STANDARD));
103            }
104    
105            @NotPrivileged
106            @Override
107            public VelocityContext getWrappedRestrictedToolsContext() {
108                    return new VelocityContextImpl(_getToolsContext(_RESTRICTED));
109            }
110    
111            @NotPrivileged
112            @Override
113            public VelocityContext getWrappedStandardToolsContext() {
114                    return new VelocityContextImpl(_getToolsContext(_STANDARD));
115            }
116    
117            @Override
118            public void init() throws Exception {
119                    if (_velocityEngine != null) {
120                            return;
121                    }
122    
123                    _velocityEngine = new org.apache.velocity.app.VelocityEngine();
124    
125                    LiferayResourceLoader.setVelocityResourceListeners(
126                            PropsValues.VELOCITY_ENGINE_RESOURCE_LISTENERS);
127    
128                    ExtendedProperties extendedProperties = new FastExtendedProperties();
129    
130                    extendedProperties.setProperty(
131                            org.apache.velocity.app.VelocityEngine.EVENTHANDLER_METHODEXCEPTION,
132                            LiferayMethodExceptionEventHandler.class.getName());
133    
134                    extendedProperties.setProperty(
135                            RuntimeConstants.INTROSPECTOR_RESTRICT_CLASSES,
136                            StringUtil.merge(PropsValues.VELOCITY_ENGINE_RESTRICTED_CLASSES));
137    
138                    extendedProperties.setProperty(
139                            RuntimeConstants.INTROSPECTOR_RESTRICT_PACKAGES,
140                            StringUtil.merge(PropsValues.VELOCITY_ENGINE_RESTRICTED_PACKAGES));
141    
142                    extendedProperties.setProperty(_RESOURCE_LOADER, "string,servlet");
143    
144                    extendedProperties.setProperty(
145                            "string." + _RESOURCE_LOADER + ".cache",
146                            String.valueOf(
147                                    PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED));
148    
149                    extendedProperties.setProperty(
150                            "string." + _RESOURCE_LOADER + ".class",
151                            StringResourceLoader.class.getName());
152    
153                    extendedProperties.setProperty(
154                            "string." + _RESOURCE_LOADER + ".repository.class",
155                            StringResourceRepositoryImpl.class.getName());
156    
157                    extendedProperties.setProperty(
158                            "servlet." + _RESOURCE_LOADER + ".cache",
159                            String.valueOf(
160                                    PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED));
161    
162                    extendedProperties.setProperty(
163                            "servlet." + _RESOURCE_LOADER + ".class",
164                            LiferayResourceLoader.class.getName());
165    
166                    extendedProperties.setProperty(
167                            org.apache.velocity.app.VelocityEngine.RESOURCE_MANAGER_CLASS,
168                            PropsUtil.get(PropsKeys.VELOCITY_ENGINE_RESOURCE_MANAGER));
169    
170                    extendedProperties.setProperty(
171                            org.apache.velocity.app.VelocityEngine.RESOURCE_MANAGER_CACHE_CLASS,
172                            PropsUtil.get(PropsKeys.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE));
173    
174                    extendedProperties.setProperty(
175                            org.apache.velocity.app.VelocityEngine.VM_LIBRARY,
176                            PropsUtil.get(PropsKeys.VELOCITY_ENGINE_VELOCIMACRO_LIBRARY));
177    
178                    extendedProperties.setProperty(
179                            org.apache.velocity.app.VelocityEngine.VM_LIBRARY_AUTORELOAD,
180                            String.valueOf(
181                                    !PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED));
182    
183                    extendedProperties.setProperty(
184                            org.apache.velocity.app.VelocityEngine.
185                                    VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL,
186                            String.valueOf(
187                                    !PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED));
188    
189                    extendedProperties.setProperty(
190                            org.apache.velocity.app.VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS,
191                            PropsUtil.get(PropsKeys.VELOCITY_ENGINE_LOGGER));
192    
193                    extendedProperties.setProperty(
194                            org.apache.velocity.app.VelocityEngine.RUNTIME_LOG_LOGSYSTEM +
195                                    ".log4j.category",
196                            PropsUtil.get(PropsKeys.VELOCITY_ENGINE_LOGGER_CATEGORY));
197    
198                    _velocityEngine.setExtendedProperties(extendedProperties);
199    
200                    _velocityEngine.init();
201    
202                    _restrictedToolsContext = new VelocityContextImpl();
203    
204                    VelocityVariablesUtil.insertHelperUtilities(
205                            _restrictedToolsContext,
206                            PropsValues.JOURNAL_TEMPLATE_VELOCITY_RESTRICTED_VARIABLES);
207    
208                    _standardToolsContext = new VelocityContextImpl();
209    
210                    VelocityVariablesUtil.insertHelperUtilities(
211                            _standardToolsContext, null);
212            }
213    
214            @NotPrivileged
215            @Override
216            public boolean mergeTemplate(
217                            String velocityTemplateId, String velocityTemplateContent,
218                            VelocityContext velocityContext, Writer writer)
219                    throws Exception {
220    
221                    Template template = AccessController.doPrivileged(
222                            new DoGetTemplatePrivilegedAction(
223                                    velocityTemplateId, velocityTemplateContent, StringPool.UTF8));
224    
225                    VelocityContextImpl velocityContextImpl =
226                            (VelocityContextImpl)velocityContext;
227    
228                    template.merge(velocityContextImpl.getWrappedVelocityContext(), writer);
229    
230                    return true;
231            }
232    
233            @NotPrivileged
234            @Override
235            public boolean mergeTemplate(
236                            String velocityTemplateId, VelocityContext velocityContext,
237                            Writer writer)
238                    throws Exception {
239    
240                    return mergeTemplate(velocityTemplateId, null, velocityContext, writer);
241            }
242    
243            @NotPrivileged
244            @Override
245            public boolean resourceExists(String resource) {
246                    return _velocityEngine.resourceExists(resource);
247            }
248    
249            private VelocityContextImpl _doGetToolsContext(
250                    ClassLoader classLoader, String templateContextType) {
251    
252                    Map<String, VelocityContextImpl> toolsContextMap =
253                            _classLoaderToolsContextsMap.get(classLoader);
254    
255                    if (toolsContextMap == null) {
256                            toolsContextMap =
257                                    new ConcurrentHashMap<String, VelocityContextImpl>();
258    
259                            _classLoaderToolsContextsMap.put(classLoader, toolsContextMap);
260                    }
261    
262                    VelocityContextImpl velocityContextImpl = toolsContextMap.get(
263                            templateContextType);
264    
265                    if (velocityContextImpl != null) {
266                            return velocityContextImpl;
267                    }
268    
269                    velocityContextImpl = new VelocityContextImpl();
270    
271                    if (_RESTRICTED.equals(templateContextType)) {
272                            VelocityVariablesUtil.insertHelperUtilities(
273                                    velocityContextImpl,
274                                    PropsValues.JOURNAL_TEMPLATE_VELOCITY_RESTRICTED_VARIABLES);
275                    }
276                    else {
277                            VelocityVariablesUtil.insertHelperUtilities(
278                                    velocityContextImpl, null);
279                    }
280    
281                    toolsContextMap.put(templateContextType, velocityContextImpl);
282    
283                    return velocityContextImpl;
284            }
285    
286            private String _getResourceCacheKey(String velocityTemplateId) {
287                    return _RESOURCE_TEMPLATE_NAME_SPACE.concat(velocityTemplateId);
288            }
289    
290            private VelocityContextImpl _getToolsContext(String templateContextType) {
291                    TemplateControlContext templateControlContext =
292                            getTemplateControlContext();
293    
294                    AccessControlContext accessControlContext =
295                            templateControlContext.getAccessControlContext();
296                    ClassLoader classLoader = templateControlContext.getClassLoader();
297    
298                    if (accessControlContext == null) {
299                            return _doGetToolsContext(classLoader, templateContextType);
300                    }
301    
302                    return AccessController.doPrivileged(
303                            new DoGetToolsContextPrivilegedAction(
304                                    classLoader, templateContextType),
305                            accessControlContext);
306            }
307    
308            private static final String _RESOURCE_LOADER =
309                    org.apache.velocity.app.VelocityEngine.RESOURCE_LOADER;
310    
311            private static final String _RESOURCE_TEMPLATE_NAME_SPACE = String.valueOf(
312                    ResourceManager.RESOURCE_TEMPLATE);
313    
314            private static final String _RESTRICTED = "RESTRICTED";
315    
316            private static final String _STANDARD = "STANDARD";
317    
318            private static Log _log = LogFactoryUtil.getLog(VelocityEngineImpl.class);
319    
320            private static PACL _pacl = new NoPACL();
321    
322            private Map<ClassLoader, Map<String, VelocityContextImpl>>
323                    _classLoaderToolsContextsMap = new ConcurrentHashMap
324                            <ClassLoader, Map<String, VelocityContextImpl>>();
325            private VelocityContextImpl _restrictedToolsContext;
326            private VelocityContextImpl _standardToolsContext;
327            private org.apache.velocity.app.VelocityEngine _velocityEngine;
328    
329            private static class NoPACL implements PACL {
330    
331                    public TemplateControlContext getTemplateControlContext() {
332                            ClassLoader contextClassLoader =
333                                    ClassLoaderUtil.getContextClassLoader();
334    
335                            return new TemplateControlContext(null, contextClassLoader);
336                    }
337    
338            }
339    
340            public static interface PACL {
341    
342                    public TemplateControlContext getTemplateControlContext();
343    
344            }
345    
346            private class DoGetTemplatePrivilegedAction
347                    implements PrivilegedExceptionAction<Template> {
348    
349                    public DoGetTemplatePrivilegedAction(
350                            String velocityTemplateId, String velocityTemplateContent,
351                            String encoding) {
352    
353                            _velocityTemplateId = velocityTemplateId;
354                            _velocityTemplateContent = velocityTemplateContent;
355                            _encoding = encoding;
356                    }
357    
358                    public Template run() throws Exception {
359                            if (Validator.isNotNull(_velocityTemplateContent)) {
360                                    LiferayResourceCacheUtil.remove(
361                                            _getResourceCacheKey(_velocityTemplateId));
362    
363                                    StringResourceRepository stringResourceRepository =
364                                            StringResourceLoader.getRepository();
365    
366                                    stringResourceRepository.putStringResource(
367                                            _velocityTemplateId, _velocityTemplateContent);
368    
369                                    if (_log.isDebugEnabled()) {
370                                            _log.debug(
371                                                    "Added " + _velocityTemplateId +
372                                                            " to the Velocity template repository");
373                                    }
374                            }
375    
376                            return _velocityEngine.getTemplate(_velocityTemplateId, _encoding);
377                    }
378    
379                    private String _encoding;
380                    private String _velocityTemplateContent;
381                    private String _velocityTemplateId;
382    
383            }
384    
385            private class DoGetToolsContextPrivilegedAction
386                    implements PrivilegedAction<VelocityContextImpl> {
387    
388                    public DoGetToolsContextPrivilegedAction(
389                            ClassLoader classLoader, String templateContextType) {
390    
391                            _classLoader = classLoader;
392                            _templateContextType = templateContextType;
393                    }
394    
395                    public VelocityContextImpl run() {
396                            return _doGetToolsContext(_classLoader, _templateContextType);
397                    }
398    
399                    private ClassLoader _classLoader;
400                    private String _templateContextType;
401    
402            }
403    
404    }