001
014
015 package com.liferay.portal.freemarker;
016
017 import com.liferay.portal.kernel.cache.PortalCache;
018 import com.liferay.portal.kernel.freemarker.FreeMarkerContext;
019 import com.liferay.portal.kernel.freemarker.FreeMarkerEngine;
020 import com.liferay.portal.kernel.freemarker.FreeMarkerVariablesUtil;
021 import com.liferay.portal.kernel.log.Log;
022 import com.liferay.portal.kernel.log.LogFactoryUtil;
023 import com.liferay.portal.kernel.security.pacl.DoPrivileged;
024 import com.liferay.portal.kernel.security.pacl.NotPrivileged;
025 import com.liferay.portal.kernel.util.ReflectionUtil;
026 import com.liferay.portal.kernel.util.StringPool;
027 import com.liferay.portal.kernel.util.Validator;
028 import com.liferay.portal.template.TemplateControlContext;
029 import com.liferay.portal.util.ClassLoaderUtil;
030 import com.liferay.portal.util.PropsValues;
031
032 import freemarker.cache.ClassTemplateLoader;
033 import freemarker.cache.MultiTemplateLoader;
034 import freemarker.cache.TemplateCache;
035 import freemarker.cache.TemplateLoader;
036
037 import freemarker.template.Configuration;
038 import freemarker.template.Template;
039
040 import java.io.IOException;
041 import java.io.Writer;
042
043 import java.lang.reflect.Constructor;
044 import java.lang.reflect.Field;
045
046 import java.security.AccessController;
047 import java.security.PrivilegedExceptionAction;
048
049 import java.util.Locale;
050 import java.util.Map;
051 import java.util.concurrent.ConcurrentHashMap;
052
053
057 @DoPrivileged
058 public class FreeMarkerEngineImpl implements FreeMarkerEngine {
059
060 @Override
061 public void clearClassLoader(ClassLoader classLoader) {
062 _classLoaderToolsContextsMap.remove(classLoader);
063 }
064
065 @Override
066 public void flushTemplate(String freeMarkerTemplateId) {
067 if (_configuration == null) {
068 return;
069 }
070
071 if (_stringTemplateLoader != null) {
072 _stringTemplateLoader.removeTemplate(freeMarkerTemplateId);
073 }
074
075 PortalCache portalCache = LiferayCacheStorage.getPortalCache();
076
077 portalCache.remove(_getResourceCacheKey(freeMarkerTemplateId));
078 }
079
080 public TemplateControlContext getTemplateControlContext() {
081 return _pacl.getTemplateControlContext();
082 }
083
084 @NotPrivileged
085 @Override
086 public FreeMarkerContext getWrappedClassLoaderToolsContext() {
087 return new FreeMarkerContextImpl(_doGetToolsContext(_STANDARD));
088 }
089
090 @NotPrivileged
091 @Override
092 public FreeMarkerContext getWrappedRestrictedToolsContext() {
093 return new FreeMarkerContextImpl(_doGetToolsContext(_RESTRICTED));
094 }
095
096 @NotPrivileged
097 @Override
098 public FreeMarkerContext getWrappedStandardToolsContext() {
099 return new FreeMarkerContextImpl(_doGetToolsContext(_STANDARD));
100 }
101
102 @Override
103 public void init() throws Exception {
104 if (_configuration != null) {
105 return;
106 }
107
108 LiferayTemplateLoader liferayTemplateLoader =
109 new LiferayTemplateLoader();
110
111 liferayTemplateLoader.setTemplateLoaders(
112 PropsValues.FREEMARKER_ENGINE_TEMPLATE_LOADERS);
113
114 _stringTemplateLoader = new StringTemplateLoader();
115
116 MultiTemplateLoader multiTemplateLoader =
117 new MultiTemplateLoader(
118 new TemplateLoader[] {
119 new ClassTemplateLoader(getClass(), StringPool.SLASH),
120 _stringTemplateLoader, liferayTemplateLoader
121 });
122
123 _configuration = new Configuration();
124
125 _configuration.setDefaultEncoding(StringPool.UTF8);
126 _configuration.setLocalizedLookup(
127 PropsValues.FREEMARKER_ENGINE_LOCALIZED_LOOKUP);
128 _configuration.setNewBuiltinClassResolver(
129 new LiferayTemplateClassResolver());
130 _configuration.setObjectWrapper(new LiferayObjectWrapper());
131 _configuration.setSetting(
132 "auto_import", PropsValues.FREEMARKER_ENGINE_MACRO_LIBRARY);
133 _configuration.setSetting(
134 "cache_storage", PropsValues.FREEMARKER_ENGINE_CACHE_STORAGE);
135 _configuration.setSetting(
136 "template_exception_handler",
137 PropsValues.FREEMARKER_ENGINE_TEMPLATE_EXCEPTION_HANDLER);
138 _configuration.setTemplateLoader(multiTemplateLoader);
139 _configuration.setTemplateUpdateDelay(
140 PropsValues.FREEMARKER_ENGINE_MODIFICATION_CHECK_INTERVAL);
141
142 try {
143
144
145
146
147 Field field = ReflectionUtil.getDeclaredField(
148 Configuration.class, "cache");
149
150 TemplateCache templateCache = (TemplateCache)field.get(
151 _configuration);
152
153 templateCache = new LiferayTemplateCache(templateCache);
154
155 field.set(_configuration, templateCache);
156 }
157 catch (Exception e) {
158 throw new Exception("Unable to Initialize Freemarker manager");
159 }
160
161 _encoding = _configuration.getEncoding(_configuration.getLocale());
162 _locale = _configuration.getLocale();
163
164 ClassLoader classLoader = TemplateCache.class.getClassLoader();
165
166 Class<?> templateKeyClass = classLoader.loadClass(
167 TemplateCache.class.getName().concat("$TemplateKey"));
168
169 _templateKeyConstructor = templateKeyClass.getDeclaredConstructor(
170 String.class, Locale.class, String.class, boolean.class);
171
172 _templateKeyConstructor.setAccessible(true);
173 }
174
175 @NotPrivileged
176 @Override
177 public boolean mergeTemplate(
178 String freeMarkerTemplateId, FreeMarkerContext freeMarkerContext,
179 Writer writer)
180 throws Exception {
181
182 return mergeTemplate(
183 freeMarkerTemplateId, null, freeMarkerContext, writer);
184 }
185
186 @NotPrivileged
187 @Override
188 public boolean mergeTemplate(
189 String freeMarkerTemplateId, String freemarkerTemplateContent,
190 FreeMarkerContext freeMarkerContext, Writer writer)
191 throws Exception {
192
193 Template template = AccessController.doPrivileged(
194 new DoGetTemplatePrivilegedAction(
195 freeMarkerTemplateId, freemarkerTemplateContent,
196 StringPool.UTF8));
197
198 FreeMarkerContextImpl freeMarkerContextImpl =
199 (FreeMarkerContextImpl)freeMarkerContext;
200
201 template.process(freeMarkerContextImpl.getWrappedContext(), writer);
202
203 return true;
204 }
205
206 @NotPrivileged
207 @Override
208 public boolean resourceExists(String resource) {
209 try {
210 Template template = _configuration.getTemplate(resource);
211
212 if (template != null) {
213 return true;
214 }
215 else {
216 return false;
217 }
218 }
219 catch (IOException ioe) {
220 if (_log.isWarnEnabled()) {
221 _log.warn(ioe, ioe);
222 }
223
224 return false;
225 }
226 }
227
228 private FreeMarkerContextImpl _doGetToolsContext(
229 String templateContextType) {
230
231 TemplateControlContext templateControlContext =
232 getTemplateControlContext();
233
234 ClassLoader classLoader = templateControlContext.getClassLoader();
235
236 Map<String, FreeMarkerContextImpl> toolsContextMap =
237 _classLoaderToolsContextsMap.get(classLoader);
238
239 if (toolsContextMap == null) {
240 toolsContextMap =
241 new ConcurrentHashMap<String, FreeMarkerContextImpl>();
242
243 _classLoaderToolsContextsMap.put(classLoader, toolsContextMap);
244 }
245
246 FreeMarkerContextImpl freeMarkerContextImpl = toolsContextMap.get(
247 templateContextType);
248
249 if (freeMarkerContextImpl != null) {
250 return freeMarkerContextImpl;
251 }
252
253 freeMarkerContextImpl = new FreeMarkerContextImpl();
254
255 if (_RESTRICTED.equals(templateContextType)) {
256 FreeMarkerVariablesUtil.insertHelperUtilities(
257 freeMarkerContextImpl,
258 PropsValues.JOURNAL_TEMPLATE_FREEMARKER_RESTRICTED_VARIABLES);
259 }
260 else {
261 FreeMarkerVariablesUtil.insertHelperUtilities(
262 freeMarkerContextImpl, null);
263 }
264
265 toolsContextMap.put(templateContextType, freeMarkerContextImpl);
266
267 return freeMarkerContextImpl;
268 }
269
270 private String _getResourceCacheKey(String freeMarkerTemplateId) {
271 try {
272 Object object = _templateKeyConstructor.newInstance(
273 freeMarkerTemplateId, _locale, _encoding, Boolean.TRUE);
274
275 return object.toString();
276 }
277 catch (Exception e) {
278 throw new RuntimeException(
279 "Failed to build FreeMarker internal resource cache key for " +
280 "template id " + freeMarkerTemplateId, e);
281 }
282 }
283
284 private static final String _RESTRICTED = "RESTRICTED";
285
286 private static final String _STANDARD = "STANDARD";
287
288 private static Log _log = LogFactoryUtil.getLog(FreeMarkerEngineImpl.class);
289
290 private static PACL _pacl = new NoPACL();
291
292 private Map<ClassLoader, Map<String, FreeMarkerContextImpl>>
293 _classLoaderToolsContextsMap = new ConcurrentHashMap
294 <ClassLoader, Map<String, FreeMarkerContextImpl>>();
295 private Configuration _configuration;
296 private String _encoding;
297 private Locale _locale;
298 private StringTemplateLoader _stringTemplateLoader;
299 private Constructor<?> _templateKeyConstructor;
300
301 private static class NoPACL implements PACL {
302
303 public TemplateControlContext getTemplateControlContext() {
304 ClassLoader contextClassLoader =
305 ClassLoaderUtil.getContextClassLoader();
306
307 return new TemplateControlContext(null, contextClassLoader);
308 }
309
310 }
311
312 public static interface PACL {
313
314 public TemplateControlContext getTemplateControlContext();
315
316 }
317
318 private class DoGetTemplatePrivilegedAction
319 implements PrivilegedExceptionAction<Template> {
320
321 public DoGetTemplatePrivilegedAction(
322 String freeMarkerTemplateId, String freemarkerTemplateContent,
323 String encoding) {
324
325 _freemarkerTemplateContent = freemarkerTemplateContent;
326 _freeMarkerTemplateId = freeMarkerTemplateId;
327 _encoding = encoding;
328 }
329
330 public Template run() throws Exception {
331 if (Validator.isNotNull(_freemarkerTemplateContent)) {
332 PortalCache portalCache = LiferayCacheStorage.getPortalCache();
333
334 portalCache.remove(_getResourceCacheKey(_freeMarkerTemplateId));
335
336 _stringTemplateLoader.putTemplate(
337 _freeMarkerTemplateId, _freemarkerTemplateContent);
338
339 if (_log.isDebugEnabled()) {
340 _log.debug(
341 "Added " + _freeMarkerTemplateId +
342 " to the string based FreeMarker template " +
343 "repository");
344 }
345 }
346
347 return _configuration.getTemplate(_freeMarkerTemplateId, _encoding);
348 }
349
350 private String _encoding;
351 private String _freemarkerTemplateContent;
352 private String _freeMarkerTemplateId;
353
354 }
355
356 }