001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.freemarker;
016    
017    import com.liferay.portal.kernel.cache.PortalCache;
018    import com.liferay.portal.kernel.freemarker.FreeMarkerContext;
019    import com.liferay.portal.kernel.freemarker.FreeMarkerEngine;
020    import com.liferay.portal.kernel.freemarker.FreeMarkerVariablesUtil;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.util.StringPool;
024    import com.liferay.portal.kernel.util.Validator;
025    import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal;
026    import com.liferay.portal.security.pacl.PACLClassLoaderUtil;
027    import com.liferay.portal.security.pacl.PACLPolicy;
028    import com.liferay.portal.security.pacl.PACLPolicyManager;
029    import com.liferay.portal.util.PropsValues;
030    
031    import freemarker.cache.ClassTemplateLoader;
032    import freemarker.cache.MultiTemplateLoader;
033    import freemarker.cache.TemplateCache;
034    import freemarker.cache.TemplateLoader;
035    
036    import freemarker.template.Configuration;
037    import freemarker.template.Template;
038    
039    import java.io.IOException;
040    import java.io.Writer;
041    
042    import java.lang.reflect.Constructor;
043    
044    import java.util.Locale;
045    import java.util.Map;
046    import java.util.concurrent.ConcurrentHashMap;
047    
048    /**
049     * @author Mika Koivisto
050     * @author Raymond Augé
051     */
052    public class FreeMarkerEngineImpl implements FreeMarkerEngine {
053    
054            public void clearClassLoader(ClassLoader classLoader) {
055                    _classLoaderFreeMarkerContexts.remove(classLoader);
056            }
057    
058            public void flushTemplate(String freeMarkerTemplateId) {
059                    if (_configuration == null) {
060                            return;
061                    }
062    
063                    if (_stringTemplateLoader != null) {
064                            _stringTemplateLoader.removeTemplate(freeMarkerTemplateId);
065                    }
066    
067                    PortalCache portalCache = LiferayCacheStorage.getPortalCache();
068    
069                    portalCache.remove(_getResourceCacheKey(freeMarkerTemplateId));
070            }
071    
072            public FreeMarkerContext getWrappedClassLoaderToolsContext() {
073    
074                    // This context will have all of its utilities initialized within the
075                    // class loader of the current thread
076    
077                    ClassLoader contextClassLoader =
078                            PACLClassLoaderUtil.getContextClassLoader();
079    
080                    PACLPolicy threadLocalPACLPolicy =
081                            PortalSecurityManagerThreadLocal.getPACLPolicy();
082    
083                    PACLPolicy contextClassLoaderPACLPolicy =
084                            PACLPolicyManager.getPACLPolicy(contextClassLoader);
085    
086                    try {
087                            PortalSecurityManagerThreadLocal.setPACLPolicy(
088                                    contextClassLoaderPACLPolicy);
089    
090                            FreeMarkerContextImpl classLoaderContext =
091                                    _classLoaderFreeMarkerContexts.get(contextClassLoader);
092    
093                            if (classLoaderContext == null) {
094                                    classLoaderContext = new FreeMarkerContextImpl();
095    
096                                    FreeMarkerVariablesUtil.insertHelperUtilities(
097                                            classLoaderContext, null);
098    
099                                    _classLoaderFreeMarkerContexts.put(
100                                            contextClassLoader, classLoaderContext);
101                            }
102    
103                            return new PACLFreeMarkerContextImpl(
104                                    classLoaderContext.getWrappedContext(),
105                                    contextClassLoaderPACLPolicy);
106                    }
107                    finally {
108                            PortalSecurityManagerThreadLocal.setPACLPolicy(
109                                    threadLocalPACLPolicy);
110                    }
111            }
112    
113            public FreeMarkerContext getWrappedRestrictedToolsContext() {
114                    return new FreeMarkerContextImpl(
115                            _restrictedToolsContext.getWrappedContext());
116            }
117    
118            public FreeMarkerContext getWrappedStandardToolsContext() {
119                    return new FreeMarkerContextImpl(
120                            _standardToolsContext.getWrappedContext());
121            }
122    
123            public void init() throws Exception {
124                    if (_configuration != null) {
125                            return;
126                    }
127    
128                    LiferayTemplateLoader liferayTemplateLoader =
129                            new LiferayTemplateLoader();
130    
131                    liferayTemplateLoader.setTemplateLoaders(
132                            PropsValues.FREEMARKER_ENGINE_TEMPLATE_LOADERS);
133    
134                    _stringTemplateLoader = new StringTemplateLoader();
135    
136                    MultiTemplateLoader multiTemplateLoader =
137                            new MultiTemplateLoader(
138                                    new TemplateLoader[] {
139                                            new ClassTemplateLoader(getClass(), StringPool.SLASH),
140                                            _stringTemplateLoader, liferayTemplateLoader
141                                    });
142    
143                    _configuration = new Configuration();
144    
145                    _configuration.setDefaultEncoding(StringPool.UTF8);
146                    _configuration.setLocalizedLookup(
147                            PropsValues.FREEMARKER_ENGINE_LOCALIZED_LOOKUP);
148                    _configuration.setNewBuiltinClassResolver(
149                            new LiferayTemplateClassResolver());
150                    _configuration.setObjectWrapper(new LiferayObjectWrapper());
151                    _configuration.setSetting(
152                            "auto_import", PropsValues.FREEMARKER_ENGINE_MACRO_LIBRARY);
153                    _configuration.setSetting(
154                            "cache_storage", PropsValues.FREEMARKER_ENGINE_CACHE_STORAGE);
155                    _configuration.setSetting(
156                            "template_exception_handler",
157                            PropsValues.FREEMARKER_ENGINE_TEMPLATE_EXCEPTION_HANDLER);
158                    _configuration.setTemplateLoader(multiTemplateLoader);
159                    _configuration.setTemplateUpdateDelay(
160                            PropsValues.FREEMARKER_ENGINE_MODIFICATION_CHECK_INTERVAL);
161    
162                    _encoding = _configuration.getEncoding(_configuration.getLocale());
163                    _locale = _configuration.getLocale();
164    
165                    _restrictedToolsContext = new FreeMarkerContextImpl();
166    
167                    FreeMarkerVariablesUtil.insertHelperUtilities(
168                            _restrictedToolsContext,
169                            PropsValues.JOURNAL_TEMPLATE_FREEMARKER_RESTRICTED_VARIABLES);
170    
171                    _standardToolsContext = new FreeMarkerContextImpl();
172    
173                    FreeMarkerVariablesUtil.insertHelperUtilities(
174                            _standardToolsContext, null);
175    
176                    ClassLoader classLoader = TemplateCache.class.getClassLoader();
177    
178                    Class<?> templateKeyClass = classLoader.loadClass(
179                            TemplateCache.class.getName().concat("$TemplateKey"));
180    
181                    _templateKeyConstructor = templateKeyClass.getDeclaredConstructor(
182                            String.class, Locale.class, String.class, boolean.class);
183    
184                    _templateKeyConstructor.setAccessible(true);
185            }
186    
187            public boolean mergeTemplate(
188                            String freeMarkerTemplateId, FreeMarkerContext freeMarkerContext,
189                            Writer writer)
190                    throws Exception {
191    
192                    return mergeTemplate(
193                            freeMarkerTemplateId, null, freeMarkerContext, writer);
194            }
195    
196            public boolean mergeTemplate(
197                            String freeMarkerTemplateId, String freemarkerTemplateContent,
198                            FreeMarkerContext freeMarkerContext, Writer writer)
199                    throws Exception {
200    
201                    if (Validator.isNotNull(freemarkerTemplateContent)) {
202                            PortalCache portalCache = LiferayCacheStorage.getPortalCache();
203    
204                            portalCache.remove(_getResourceCacheKey(freeMarkerTemplateId));
205    
206                            _stringTemplateLoader.putTemplate(
207                                    freeMarkerTemplateId, freemarkerTemplateContent);
208    
209                            if (_log.isDebugEnabled()) {
210                                    _log.debug(
211                                            "Added " + freeMarkerTemplateId +
212                                                    " to the string based FreeMarker template repository");
213                            }
214                    }
215    
216                    FreeMarkerContextImpl freeMarkerContextImpl =
217                            (FreeMarkerContextImpl)freeMarkerContext;
218    
219                    PACLPolicy threadLocalPACLPolicy =
220                            PortalSecurityManagerThreadLocal.getPACLPolicy();
221    
222                    try {
223                            if (freeMarkerContextImpl instanceof PACLFreeMarkerContextImpl) {
224                                    PACLFreeMarkerContextImpl paclContextImpl =
225                                            (PACLFreeMarkerContextImpl)freeMarkerContextImpl;
226    
227                                    PortalSecurityManagerThreadLocal.setPACLPolicy(
228                                            paclContextImpl.getPaclPolicy());
229                            }
230    
231                            Template template = _configuration.getTemplate(
232                                    freeMarkerTemplateId, StringPool.UTF8);
233    
234                            template.process(freeMarkerContextImpl.getWrappedContext(), writer);
235                    }
236                    finally {
237                            if (freeMarkerContextImpl instanceof PACLFreeMarkerContextImpl) {
238                                    PortalSecurityManagerThreadLocal.setPACLPolicy(
239                                            threadLocalPACLPolicy);
240                            }
241                    }
242    
243                    return true;
244            }
245    
246            public boolean resourceExists(String resource) {
247                    try {
248                            Template template = _configuration.getTemplate(resource);
249    
250                            if (template != null) {
251                                    return true;
252                            }
253                            else {
254                                    return false;
255                            }
256                    }
257                    catch (IOException ioe) {
258                            if (_log.isWarnEnabled()) {
259                                    _log.warn(ioe, ioe);
260                            }
261    
262                            return false;
263                    }
264            }
265    
266            private String _getResourceCacheKey(String freeMarkerTemplateId) {
267                    try {
268                            Object object = _templateKeyConstructor.newInstance(
269                                    freeMarkerTemplateId, _locale, _encoding, Boolean.TRUE);
270    
271                            return object.toString();
272                    }
273                    catch (Exception e) {
274                            throw new RuntimeException(
275                                    "Failed to build FreeMarker internal resource cache key for " +
276                                            "template id " + freeMarkerTemplateId, e);
277                    }
278            }
279    
280            private static Log _log = LogFactoryUtil.getLog(FreeMarkerEngineImpl.class);
281    
282            private Map<ClassLoader, FreeMarkerContextImpl>
283                    _classLoaderFreeMarkerContexts =
284                            new ConcurrentHashMap<ClassLoader, FreeMarkerContextImpl>();
285            private Configuration _configuration;
286            private String _encoding;
287            private Locale _locale;
288            private FreeMarkerContextImpl _restrictedToolsContext;
289            private FreeMarkerContextImpl _standardToolsContext;
290            private StringTemplateLoader _stringTemplateLoader;
291            private Constructor<?> _templateKeyConstructor;
292    
293    }