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.freemarker;
016    
017    import com.liferay.portal.kernel.cache.PortalCache;
018    import com.liferay.portal.kernel.cache.SingleVMPoolUtil;
019    import com.liferay.portal.kernel.template.TemplateConstants;
020    import com.liferay.portal.kernel.template.TemplateException;
021    import com.liferay.portal.kernel.template.TemplateResource;
022    import com.liferay.portal.kernel.template.TemplateResourceLoaderUtil;
023    import com.liferay.portal.kernel.util.PropsKeys;
024    import com.liferay.portal.kernel.util.ReflectionUtil;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.template.TemplateResourceThreadLocal;
027    import com.liferay.portal.util.PropsUtil;
028    import com.liferay.portal.util.PropsValues;
029    
030    import freemarker.cache.TemplateCache;
031    
032    import freemarker.template.Configuration;
033    import freemarker.template.Template;
034    
035    import java.io.IOException;
036    
037    import java.lang.reflect.Method;
038    
039    import java.security.AccessController;
040    import java.security.PrivilegedActionException;
041    import java.security.PrivilegedExceptionAction;
042    
043    import java.util.Locale;
044    
045    /**
046     * @author Tina Tian
047     */
048    public class LiferayTemplateCache extends TemplateCache {
049    
050            public LiferayTemplateCache(Configuration configuration)
051                    throws TemplateException {
052    
053                    _configuration = configuration;
054    
055                    try {
056                            _normalizeNameMethod = ReflectionUtil.getDeclaredMethod(
057                                    TemplateCache.class, "normalizeName", String.class);
058                    }
059                    catch (Exception e) {
060                            throw new TemplateException(e);
061                    }
062    
063                    String cacheName = TemplateResource.class.getName();
064    
065                    cacheName = cacheName.concat(StringPool.POUND).concat(
066                            TemplateConstants.LANG_TYPE_FTL);
067    
068                    _portalCache = SingleVMPoolUtil.getCache(cacheName);
069            }
070    
071            @Override
072            public Template getTemplate(
073                            String templateId, Locale locale, String encoding, boolean parse)
074                    throws IOException {
075    
076                    String[] macroTemplateIds = PropsUtil.getArray(
077                            PropsKeys.FREEMARKER_ENGINE_MACRO_LIBRARY);
078    
079                    for (String macroTemplateId : macroTemplateIds) {
080                            int pos = macroTemplateId.indexOf(" as ");
081    
082                            if (pos != -1) {
083                                    macroTemplateId = macroTemplateId.substring(0, pos);
084                            }
085    
086                            if (templateId.equals(macroTemplateId)) {
087    
088                                    // This template is provided by the portal, so invoke it from an
089                                    // access controller
090    
091                                    try {
092                                            return AccessController.doPrivileged(
093                                                    new TemplatePrivilegedExceptionAction(
094                                                            macroTemplateId, locale, encoding, parse));
095                                    }
096                                    catch (PrivilegedActionException pae) {
097                                            throw (IOException)pae.getException();
098                                    }
099                            }
100                    }
101    
102                    return doGetTemplate(templateId, locale, encoding, parse);
103            }
104    
105            private Template doGetTemplate(
106                            String templateId, Locale locale, String encoding, boolean parse)
107                    throws IOException {
108    
109                    if (templateId == null) {
110                            throw new IllegalArgumentException(
111                                    "Argument \"name\" cannot be null");
112                    }
113    
114                    if (locale == null) {
115                            throw new IllegalArgumentException(
116                                    "Argument \"locale\" cannot be null");
117                    }
118    
119                    if (encoding == null) {
120                            throw new IllegalArgumentException(
121                                    "Argument \"encoding\" cannot be null");
122                    }
123    
124                    TemplateResource templateResource = null;
125    
126                    if (templateId.startsWith(
127                                    TemplateConstants.TEMPLATE_RESOURCE_UUID_PREFIX)) {
128    
129                            templateResource = TemplateResourceThreadLocal.getTemplateResource(
130                                    TemplateConstants.LANG_TYPE_FTL);
131                    }
132                    else {
133                            try {
134                                    templateId = (String)_normalizeNameMethod.invoke(
135                                            this, templateId);
136    
137                                    templateResource =
138                                            TemplateResourceLoaderUtil.getTemplateResource(
139                                                    TemplateConstants.LANG_TYPE_FTL, templateId);
140                            }
141                            catch (Exception e) {
142                                    templateResource = null;
143                            }
144                    }
145    
146                    if (templateResource == null) {
147                            throw new IOException(
148                                    "Unable to find FreeMarker template with ID " + templateId);
149                    }
150    
151                    Object object = _portalCache.get(templateResource);
152    
153                    if ((object != null) && (object instanceof Template)) {
154                            return (Template)object;
155                    }
156    
157                    Template template = new Template(
158                            templateResource.getTemplateId(), templateResource.getReader(),
159                            _configuration, TemplateConstants.DEFAUT_ENCODING);
160    
161                    if (PropsValues.
162                                    FREEMARKER_ENGINE_RESOURCE_MODIFICATION_CHECK_INTERVAL != 0) {
163    
164                            _portalCache.put(templateResource, template);
165                    }
166    
167                    return template;
168            }
169    
170            private Configuration _configuration;
171            private Method _normalizeNameMethod;
172            private PortalCache<TemplateResource, Object> _portalCache;
173    
174            private class TemplatePrivilegedExceptionAction
175                    implements PrivilegedExceptionAction<Template> {
176    
177                    public TemplatePrivilegedExceptionAction(
178                            String templateId, Locale locale, String encoding, boolean parse) {
179    
180                            _templateId = templateId;
181                            _locale = locale;
182                            _encoding = encoding;
183                            _parse = parse;
184                    }
185    
186                    public Template run() throws Exception {
187                            return doGetTemplate(_templateId, _locale, _encoding, _parse);
188                    }
189    
190                    private String _encoding;
191                    private Locale _locale;
192                    private boolean _parse;
193                    private String _templateId;
194    
195            }
196    
197    }