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.template;
016    
017    import com.liferay.portal.deploy.sandbox.SandboxHandler;
018    import com.liferay.portal.kernel.cache.MultiVMPool;
019    import com.liferay.portal.kernel.cache.PortalCache;
020    import com.liferay.portal.kernel.cache.PortalCacheListener;
021    import com.liferay.portal.kernel.cache.PortalCacheListenerScope;
022    import com.liferay.portal.kernel.cache.SingleVMPool;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
026    import com.liferay.portal.kernel.template.ClassLoaderTemplateResource;
027    import com.liferay.portal.kernel.template.TemplateConstants;
028    import com.liferay.portal.kernel.template.TemplateException;
029    import com.liferay.portal.kernel.template.TemplateResource;
030    import com.liferay.portal.kernel.template.TemplateResourceLoader;
031    import com.liferay.portal.kernel.template.URLTemplateResource;
032    import com.liferay.portal.kernel.util.InstanceFactory;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.Validator;
035    
036    import java.io.IOException;
037    import java.io.ObjectInput;
038    import java.io.ObjectOutput;
039    import java.io.Reader;
040    
041    import java.util.HashSet;
042    import java.util.Set;
043    
044    /**
045     * @author Tina Tian
046     */
047    @DoPrivileged
048    public class DefaultTemplateResourceLoader implements TemplateResourceLoader {
049    
050            public DefaultTemplateResourceLoader(
051                    String name, String[] templateResourceParserClassNames,
052                    long modificationCheckInterval, MultiVMPool multiVMPool,
053                    SingleVMPool singleVMPool) {
054    
055                    if (Validator.isNull(name)) {
056                            throw new IllegalArgumentException(
057                                    "Template resource loader name is null");
058                    }
059    
060                    if (templateResourceParserClassNames == null) {
061                            throw new IllegalArgumentException(
062                                    "Template resource parser class names is null");
063                    }
064    
065                    _name = name;
066    
067                    for (String templateResourceParserClassName :
068                                    templateResourceParserClassNames) {
069    
070                            try {
071                                    TemplateResourceParser templateResourceParser =
072                                            (TemplateResourceParser)InstanceFactory.newInstance(
073                                                    templateResourceParserClassName);
074    
075                                    _templateResourceParsers.add(templateResourceParser);
076                            }
077                            catch (Exception e) {
078                                    _log.error(e, e);
079                            }
080                    }
081    
082                    _modificationCheckInterval = modificationCheckInterval;
083    
084                    _multiVMPool = multiVMPool;
085    
086                    String portalCacheName = TemplateResourceLoader.class.getName();
087    
088                    portalCacheName = portalCacheName.concat(
089                            StringPool.PERIOD).concat(name);
090    
091                    _multiVMPortalCache =
092                            (PortalCache<String, TemplateResource>)_multiVMPool.getPortalCache(
093                                    portalCacheName);
094    
095                    PortalCacheListener<String, TemplateResource> cacheListener =
096                            new TemplateResourcePortalCacheListener(name);
097    
098                    _multiVMPortalCache.registerPortalCacheListener(
099                            cacheListener, PortalCacheListenerScope.ALL);
100    
101                    _singleVMPool = singleVMPool;
102    
103                    _singleVMPortalCache =
104                            (PortalCache<String, TemplateResource>)_singleVMPool.getPortalCache(
105                                    portalCacheName);
106    
107                    _singleVMPortalCache.registerPortalCacheListener(
108                            cacheListener, PortalCacheListenerScope.ALL);
109            }
110    
111            @Override
112            public void clearCache() {
113                    _multiVMPortalCache.removeAll();
114                    _singleVMPortalCache.removeAll();
115            }
116    
117            @Override
118            public void clearCache(String templateId) {
119                    _multiVMPortalCache.remove(templateId);
120                    _singleVMPortalCache.remove(templateId);
121            }
122    
123            @Override
124            public void destroy() {
125                    _multiVMPool.removePortalCache(
126                            _multiVMPortalCache.getPortalCacheName());
127                    _singleVMPool.removePortalCache(
128                            _singleVMPortalCache.getPortalCacheName());
129    
130                    _templateResourceParsers.clear();
131            }
132    
133            @Override
134            public String getName() {
135                    return _name;
136            }
137    
138            @Override
139            public TemplateResource getTemplateResource(String templateId) {
140                    if (_modificationCheckInterval == 0) {
141                            return _loadFromParser(templateId);
142                    }
143    
144                    TemplateResource templateResource = _loadFromCache(templateId);
145    
146                    if (templateResource != null) {
147                            if (templateResource instanceof NullHolderTemplateResource) {
148                                    return null;
149                            }
150    
151                            return templateResource;
152                    }
153    
154                    templateResource = _loadFromParser(templateId);
155    
156                    _updateCache(templateId, templateResource);
157    
158                    return templateResource;
159            }
160    
161            @Override
162            public boolean hasTemplateResource(String templateId) {
163                    TemplateResource templateResource = getTemplateResource(templateId);
164    
165                    if (templateResource != null) {
166                            return true;
167                    }
168    
169                    return false;
170            }
171    
172            private TemplateResource _getTemplateResource() {
173                    TemplateResource templateResource =
174                            TemplateResourceThreadLocal.getTemplateResource(_name);
175    
176                    if (templateResource instanceof CacheTemplateResource) {
177                            CacheTemplateResource cacheTemplateResource =
178                                    (CacheTemplateResource)templateResource;
179    
180                            return cacheTemplateResource.getInnerTemplateResource();
181                    }
182    
183                    return templateResource;
184            }
185    
186            private Set<TemplateResourceParser> _getTemplateResourceParsers() {
187                    TemplateResource templateResource = _getTemplateResource();
188    
189                    if ((templateResource != null) &&
190                            (templateResource instanceof ClassLoaderTemplateResource)) {
191    
192                            ClassLoaderTemplateResource classLoaderTemplateResource =
193                                    (ClassLoaderTemplateResource)templateResource;
194    
195                            ClassLoaderResourceParser classLoaderResourceParser =
196                                    new ClassLoaderResourceParser(
197                                            classLoaderTemplateResource.getClassLoader());
198    
199                            Set<TemplateResourceParser> templateResourceParsers = new HashSet<>(
200                                    _templateResourceParsers);
201    
202                            templateResourceParsers.add(classLoaderResourceParser);
203    
204                            return templateResourceParsers;
205                    }
206    
207                    return _templateResourceParsers;
208            }
209    
210            private TemplateResource _loadFromCache(
211                    PortalCache<String, TemplateResource> portalCache, String templateId) {
212    
213                    Object object = portalCache.get(templateId);
214    
215                    if (object == null) {
216                            return null;
217                    }
218    
219                    if (!(object instanceof TemplateResource)) {
220                            portalCache.remove(templateId);
221    
222                            if (_log.isWarnEnabled()) {
223                                    _log.warn(
224                                            "Remove template " + templateId +
225                                                    " because it is not a template resource");
226                            }
227    
228                            return null;
229                    }
230    
231                    TemplateResource templateResource = (TemplateResource)object;
232    
233                    if (_modificationCheckInterval > 0) {
234                            long expireTime =
235                                    templateResource.getLastModified() + _modificationCheckInterval;
236    
237                            if (System.currentTimeMillis() > expireTime) {
238                                    portalCache.remove(templateId);
239    
240                                    templateResource = _nullHolderTemplateResource;
241    
242                                    if (_log.isDebugEnabled()) {
243                                            _log.debug(
244                                                    "Remove expired template resource " + templateId);
245                                    }
246                            }
247                    }
248    
249                    return templateResource;
250            }
251    
252            private TemplateResource _loadFromCache(String templateId) {
253                    TemplateResource templateResource = _loadFromCache(
254                            _singleVMPortalCache, templateId);
255    
256                    if (templateResource != null) {
257                            if (templateResource == _nullHolderTemplateResource) {
258                                    return null;
259                            }
260    
261                            return templateResource;
262                    }
263    
264                    templateResource = _loadFromCache(_multiVMPortalCache, templateId);
265    
266                    if ((templateResource == null) ||
267                            (templateResource == _nullHolderTemplateResource)) {
268    
269                            return null;
270                    }
271    
272                    return templateResource;
273            }
274    
275            private TemplateResource _loadFromParser(String templateId) {
276                    Set<TemplateResourceParser> templateResourceParsers =
277                            _getTemplateResourceParsers();
278    
279                    for (TemplateResourceParser templateResourceParser :
280                                    templateResourceParsers) {
281    
282                            try {
283                                    TemplateResource templateResource =
284                                            templateResourceParser.getTemplateResource(templateId);
285    
286                                    if (templateResource != null) {
287                                            if ((_modificationCheckInterval != 0) &&
288                                                    (!_name.equals(TemplateConstants.LANG_TYPE_VM) ||
289                                                     !templateId.contains(
290                                                             SandboxHandler.SANDBOX_MARKER))) {
291    
292                                                    templateResource = new CacheTemplateResource(
293                                                            templateResource);
294                                            }
295    
296                                            return templateResource;
297                                    }
298                            }
299                            catch (TemplateException te) {
300                                    if (_log.isWarnEnabled()) {
301                                            _log.warn(
302                                                    "Unable to parse template " + templateId +
303                                                            " with parser " + templateResourceParser,
304                                                    te);
305                                    }
306                            }
307                    }
308    
309                    return null;
310            }
311    
312            private void _updateCache(
313                    String templateId, TemplateResource templateResource) {
314    
315                    if (templateResource == null) {
316                            _singleVMPortalCache.put(
317                                    templateId, new NullHolderTemplateResource());
318    
319                            return;
320                    }
321    
322                    CacheTemplateResource cacheTemplateResource =
323                            (CacheTemplateResource)templateResource;
324    
325                    TemplateResource innerTemplateResource =
326                            cacheTemplateResource.getInnerTemplateResource();
327    
328                    if (innerTemplateResource instanceof URLTemplateResource) {
329                            _singleVMPortalCache.put(templateId, templateResource);
330    
331                            return;
332                    }
333    
334                    _multiVMPortalCache.put(templateId, templateResource);
335            }
336    
337            private static final Log _log = LogFactoryUtil.getLog(
338                    DefaultTemplateResourceLoader.class);
339    
340            private static final NullHolderTemplateResource
341                    _nullHolderTemplateResource = new NullHolderTemplateResource();
342    
343            private long _modificationCheckInterval;
344            private final MultiVMPool _multiVMPool;
345            private final PortalCache<String, TemplateResource> _multiVMPortalCache;
346            private final String _name;
347            private final SingleVMPool _singleVMPool;
348            private final PortalCache<String, TemplateResource> _singleVMPortalCache;
349            private final Set<TemplateResourceParser> _templateResourceParsers =
350                    new HashSet<>();
351    
352            private static class NullHolderTemplateResource
353                    implements TemplateResource {
354    
355                    /**
356                     * The empty constructor is required by {@link java.io.Externalizable}.
357                     * Do not use this for any other purpose.
358                     */
359                    public NullHolderTemplateResource() {
360                    }
361    
362                    @Override
363                    public long getLastModified() {
364                            return _lastModified;
365                    }
366    
367                    @Override
368                    public Reader getReader() {
369                            return null;
370                    }
371    
372                    @Override
373                    public String getTemplateId() {
374                            return null;
375                    }
376    
377                    @Override
378                    public void readExternal(ObjectInput objectInput) throws IOException {
379                            _lastModified = objectInput.readLong();
380                    }
381    
382                    @Override
383                    public void writeExternal(ObjectOutput objectOutput)
384                            throws IOException {
385    
386                            objectOutput.writeLong(_lastModified);
387                    }
388    
389                    private long _lastModified = System.currentTimeMillis();
390    
391            }
392    
393    }