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.util.freemarker;
016    
017    import com.liferay.portal.kernel.cache.CacheRegistryItem;
018    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
019    import com.liferay.portal.kernel.memory.FinalizeAction;
020    import com.liferay.portal.kernel.memory.FinalizeManager;
021    import com.liferay.portal.kernel.util.ContextPathUtil;
022    import com.liferay.portal.kernel.util.StringPool;
023    
024    import freemarker.ext.jsp.TaglibFactory;
025    
026    import freemarker.template.TemplateHashModel;
027    import freemarker.template.TemplateModel;
028    import freemarker.template.TemplateModelException;
029    
030    import java.lang.ref.Reference;
031    
032    import java.util.Map;
033    import java.util.concurrent.ConcurrentHashMap;
034    
035    import javax.servlet.ServletContext;
036    
037    /**
038     * @author Shuyang Zhou
039     */
040    public class FreeMarkerTaglibFactoryUtil implements CacheRegistryItem {
041    
042            public static TemplateHashModel createTaglibFactory(
043                    ServletContext servletContext) {
044    
045                    return new TaglibFactoryCacheWrapper(servletContext);
046            }
047    
048            @Override
049            public String getRegistryName() {
050                    return _registryName;
051            }
052    
053            @Override
054            public void invalidate() {
055                    _templateModels.clear();
056            }
057    
058            private static FreeMarkerTaglibFactoryUtil _getInstance(
059                    ServletContext servletContext) {
060    
061                    if (_instance != null) {
062                            return _instance;
063                    }
064    
065                    synchronized(FreeMarkerTaglibFactoryUtil.class) {
066                            if (_instance == null) {
067                                    String contextPath = ContextPathUtil.getContextPath(
068                                            servletContext);
069    
070                                    // First call within current class loader
071    
072                                    _instance = new FreeMarkerTaglibFactoryUtil(contextPath);
073    
074                                    // Unregister previous one if there is one, this should only
075                                    // happen on plugin redeploy
076    
077                                    CacheRegistryUtil.unregister(_instance._registryName);
078    
079                                    // Register current instance
080    
081                                    CacheRegistryUtil.register(_instance);
082    
083                                    // Save a hard stack copy to prevent Tomcat null out heap
084                                    // reference
085    
086                                    final String name = _instance._registryName;
087    
088                                    // Bind _instance lifecycle to servlet context to prevent memory
089                                    // leak on undeploy
090    
091                                    FinalizeManager.register(
092                                            servletContext,
093                                            new FinalizeAction() {
094    
095                                                    @Override
096                                                    public void doFinalize(Reference<?> reference) {
097                                                            CacheRegistryUtil.unregister(name);
098                                                    }
099    
100                                            }, FinalizeManager.PHANTOM_REFERENCE_FACTORY);
101                            }
102                    }
103    
104                    return _instance;
105            }
106    
107            private FreeMarkerTaglibFactoryUtil(String contextPath) {
108                    _contextPath = contextPath;
109                    _registryName = FreeMarkerTaglibFactoryUtil.class.getName().concat(
110                            StringPool.AT).concat(_contextPath);
111            }
112    
113            private static volatile FreeMarkerTaglibFactoryUtil _instance;
114    
115            private final String _contextPath;
116            private final String _registryName;
117            private Map<String, TemplateModel> _templateModels =
118                    new ConcurrentHashMap<String, TemplateModel>();
119    
120            private static class TaglibFactoryCacheWrapper
121                    implements TemplateHashModel {
122    
123                    public TaglibFactoryCacheWrapper(ServletContext servletContext) {
124                            FreeMarkerTaglibFactoryUtil freeMarkerTaglibFactoryUtil =
125                                    _getInstance(servletContext);
126    
127                            _templateModels = freeMarkerTaglibFactoryUtil._templateModels;
128                            _taglibFactory = new TaglibFactory(servletContext);
129                    }
130    
131                    @Override
132                    public TemplateModel get(String uri) throws TemplateModelException {
133                            TemplateModel templateModel = _templateModels.get(uri);
134    
135                            if (templateModel == null) {
136                                    templateModel = _taglibFactory.get(uri);
137    
138                                    _templateModels.put(uri, templateModel);
139                            }
140    
141                            return templateModel;
142                    }
143    
144                    @Override
145                    public boolean isEmpty() {
146                            return false;
147                    }
148    
149                    private final TaglibFactory _taglibFactory;
150                    private final Map<String, TemplateModel> _templateModels;
151    
152            }
153    
154    }