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