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