/* * Copyright 2012 astamuse company,Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.astamuse.asta4d.template; import java.io.IOException; import java.io.InputStream; import java.util.Locale; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.parser.Asta4DTagSupportHtmlTreeBuilder; import org.jsoup.parser.Parser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.astamuse.asta4d.Configuration; import com.astamuse.asta4d.Context; import com.astamuse.asta4d.template.AbstractTemplateResolver.TemplateInfo; import com.astamuse.asta4d.util.MemorySafeResourceCache; import com.astamuse.asta4d.util.MemorySafeResourceCache.ResouceHolder; import com.astamuse.asta4d.util.MultiSearchPathResourceLoader; import com.astamuse.asta4d.util.i18n.LocalizeUtil; public abstract class AbstractTemplateResolver extends MultiSearchPathResourceLoader<TemplateInfo>implements TemplateResolver { private final MemorySafeResourceCache<String, Template> defaultTemplateCache = new MemorySafeResourceCache<String, Template>(); private Logger logger = LoggerFactory.getLogger(this.getClass()); private MemorySafeResourceCache<String, Template> retrieveTemplateCache() { Context context = Context.getCurrentThreadContext(); Configuration conf = Configuration.getConfiguration(); if (conf.isCacheEnable()) { return defaultTemplateCache; } else { // for debug, if we do not cache it in context, some templates will // be probably initialized for hundreds times // thus there will be hundreds logs of template initializing in info // level. String key = this.getClass().getName() + "##template-cache-map"; MemorySafeResourceCache<String, Template> contextCachedMap = context.getData(key); if (contextCachedMap == null) { contextCachedMap = new MemorySafeResourceCache<>(); context.setData(key, contextCachedMap); } return contextCachedMap; } } /** * * @param path * @return * @throws TemplateException * error occurs when parsing template file * @throws TemplateNotFoundException * template file does not exist */ public Template findTemplate(String path) throws TemplateException, TemplateNotFoundException { try { MemorySafeResourceCache<String, Template> templateCache = retrieveTemplateCache(); Locale locale = LocalizeUtil.defaultWhenNull(null); String cacheKey = LocalizeUtil.createLocalizedKey(path, locale); ResouceHolder<Template> resource = templateCache.get(cacheKey); if (resource != null) { if (resource.exists()) { return resource.get(); } else { throw new TemplateNotFoundException(path); } } logger.info("Initializing template " + path); TemplateInfo info = searchResource("/", LocalizeUtil.getCandidatePaths(path, locale)); if (info == null) { templateCache.put(cacheKey, null); throw new TemplateNotFoundException(path); } InputStream input = info.getInput(); if (input == null) { templateCache.put(cacheKey, null); throw new TemplateNotFoundException(path); } try { Template t = createTemplate(info); templateCache.put(cacheKey, t); return t; } finally { // we have to close the input stream to avoid file lock try { input.close(); } catch (Exception ex) { logger.error("Error occured when close input stream of " + info.getActualPath(), ex); } } } catch (TemplateNotFoundException e) { throw e; } catch (Exception e) { throw new TemplateException(path + " resolve error", e); } } protected Template createTemplate(TemplateInfo templateInfo) throws TemplateException, TemplateNotFoundException { try { Document doc = parse(templateInfo.getInput()); return new Template(templateInfo.getActualPath(), doc); } catch (IOException e) { String msg = String.format("Template %s parsing failed.", templateInfo.getActualPath()); throw new TemplateException(msg, e); } } protected Document parse(InputStream input) throws IOException { return Jsoup.parse(input, "UTF-8", "", new Parser(new Asta4DTagSupportHtmlTreeBuilder())); } protected TemplateInfo createTemplateInfo(String path, InputStream input) { if (input == null) { return null; } return new TemplateInfo(path, input); } public static class TemplateInfo { private final String actualPath; private final InputStream input; public TemplateInfo(String path, InputStream input) { this.actualPath = path; this.input = input; } public String getActualPath() { return actualPath; } public InputStream getInput() { return input; } } }