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.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.TemplateResource;
021    import com.liferay.portal.kernel.template.TemplateResourceLoaderUtil;
022    import com.liferay.portal.kernel.util.PropsKeys;
023    import com.liferay.portal.kernel.util.ReflectionUtil;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.template.TemplateResourceThreadLocal;
026    import com.liferay.portal.util.PropsUtil;
027    import com.liferay.portal.util.PropsValues;
028    
029    import java.io.IOException;
030    import java.io.Reader;
031    
032    import java.lang.reflect.Field;
033    
034    import java.security.AccessController;
035    import java.security.PrivilegedActionException;
036    import java.security.PrivilegedExceptionAction;
037    
038    import org.apache.commons.collections.ExtendedProperties;
039    import org.apache.velocity.Template;
040    import org.apache.velocity.exception.ParseErrorException;
041    import org.apache.velocity.exception.ResourceNotFoundException;
042    import org.apache.velocity.runtime.RuntimeInstance;
043    import org.apache.velocity.runtime.RuntimeServices;
044    import org.apache.velocity.runtime.resource.Resource;
045    import org.apache.velocity.runtime.resource.ResourceManager;
046    import org.apache.velocity.runtime.resource.ResourceManagerImpl;
047    
048    /**
049     * @author Brian Wing Shun Chan
050     * @author Shuyang Zhou
051     */
052    public class LiferayResourceManager extends ResourceManagerImpl {
053    
054            public LiferayResourceManager() {
055                    String cacheName = TemplateResource.class.getName();
056    
057                    cacheName = cacheName.concat(StringPool.POUND).concat(
058                            TemplateConstants.LANG_TYPE_VM);
059    
060                    _portalCache = SingleVMPoolUtil.getCache(cacheName);
061            }
062    
063            @Override
064            public String getLoaderNameForResource(String source) {
065    
066                    // Velocity's default implementation makes its cache useless because
067                    // getResourceStream is called to test the availability of a template
068    
069                    if ((globalCache.get(ResourceManager.RESOURCE_CONTENT + source) !=
070                                    null) ||
071                            (globalCache.get(ResourceManager.RESOURCE_TEMPLATE + source) !=
072                                    null)) {
073    
074                            return LiferayResourceLoader.class.getName();
075                    }
076                    else {
077                            return super.getLoaderNameForResource(source);
078                    }
079            }
080    
081            @Override
082            public Resource getResource(
083                            final String resourceName, final int resourceType,
084                            final String encoding)
085                    throws Exception, ParseErrorException, ResourceNotFoundException {
086    
087                    String[] macroTemplateIds = PropsUtil.getArray(
088                            PropsKeys.VELOCITY_ENGINE_VELOCIMACRO_LIBRARY);
089    
090                    for (String macroTemplateId : macroTemplateIds) {
091                            if (resourceName.equals(macroTemplateId)) {
092    
093                                    // This resource is provided by the portal, so invoke it from an
094                                    // access controller
095    
096                                    try {
097                                            return AccessController.doPrivileged(
098                                                    new ResourcePrivilegedExceptionAction(
099                                                            resourceName, resourceType, encoding));
100                                    }
101                                    catch (PrivilegedActionException pae) {
102                                            throw (IOException)pae.getException();
103                                    }
104                            }
105                    }
106    
107                    return doGetResource(resourceName, resourceType, encoding);
108            }
109    
110            @Override
111            public synchronized void initialize(RuntimeServices runtimeServices)
112                    throws Exception {
113    
114                    ExtendedProperties extendedProperties =
115                            runtimeServices.getConfiguration();
116    
117                    Field field = ReflectionUtil.getDeclaredField(
118                            RuntimeInstance.class, "configuration");
119    
120                    field.set(
121                            runtimeServices, new FastExtendedProperties(extendedProperties));
122    
123                    super.initialize(runtimeServices);
124            }
125    
126            private Template _createTemplate(TemplateResource templateResource)
127                    throws IOException {
128    
129                    Template template = new LiferayTemplate(templateResource.getReader());
130    
131                    template.setEncoding(TemplateConstants.DEFAUT_ENCODING);
132                    template.setName(templateResource.getTemplateId());
133                    template.setResourceLoader(new LiferayResourceLoader());
134                    template.setRuntimeServices(rsvc);
135    
136                    template.process();
137    
138                    return template;
139            }
140    
141            private Resource doGetResource(
142                            final String resourceName, final int resourceType,
143                            final String encoding)
144                    throws Exception, ParseErrorException, ResourceNotFoundException {
145    
146                    if (resourceType != ResourceManager.RESOURCE_TEMPLATE) {
147                            return super.getResource(resourceName, resourceType, encoding);
148                    }
149    
150                    TemplateResource templateResource = null;
151    
152                    if (resourceName.startsWith(
153                                    TemplateConstants.TEMPLATE_RESOURCE_UUID_PREFIX)) {
154    
155                            templateResource = TemplateResourceThreadLocal.getTemplateResource(
156                                    TemplateConstants.LANG_TYPE_VM);
157                    }
158                    else {
159                            templateResource = TemplateResourceLoaderUtil.getTemplateResource(
160                                    TemplateConstants.LANG_TYPE_VM, resourceName);
161                    }
162    
163                    if (templateResource == null) {
164                            throw new ResourceNotFoundException(
165                                    "Unable to find Velocity template with ID " + resourceName);
166                    }
167    
168                    Object object = _portalCache.get(templateResource);
169    
170                    if ((object != null) && (object instanceof Template)) {
171                            return (Template)object;
172                    }
173    
174                    Template template = _createTemplate(templateResource);
175    
176                    if (PropsValues.VELOCITY_ENGINE_RESOURCE_MODIFICATION_CHECK_INTERVAL !=
177                                    0) {
178    
179                            _portalCache.put(templateResource, template);
180                    }
181    
182                    return template;
183            }
184    
185            private PortalCache<TemplateResource, Object> _portalCache;
186    
187            private class LiferayTemplate extends Template {
188    
189                    public LiferayTemplate(Reader reader) {
190                            _reader = reader;
191                    }
192    
193                    @Override
194                    public boolean process() throws IOException, ParseErrorException {
195                            data = null;
196    
197                            try {
198                                    data = rsvc.parse(_reader, name);
199    
200                                    initDocument();
201    
202                                    return true;
203                            }
204                            catch (Exception e) {
205                                    throw new ParseErrorException(
206                                            "Unable to parse Velocity template");
207                            }
208                            finally {
209                                    if (_reader != null) {
210                                            _reader.close();
211                                    }
212                            }
213                    }
214    
215                    private Reader _reader;
216    
217            }
218    
219            private class ResourcePrivilegedExceptionAction
220                    implements PrivilegedExceptionAction<Resource> {
221    
222                    public ResourcePrivilegedExceptionAction(
223                            String resourceName, int resourceType, String encoding) {
224    
225                            _resourceName = resourceName;
226                            _resourceType = resourceType;
227                            _encoding = encoding;
228                    }
229    
230                    @Override
231                    public Resource run() throws Exception {
232                            return doGetResource(_resourceName, _resourceType, _encoding);
233                    }
234    
235                    private String _encoding;
236                    private String _resourceName;
237                    private int _resourceType;
238    
239            }
240    
241    }