Beetl自定义ResourceLoader,实现特殊的模板加载需求

原创
2017/07/16 10:39
阅读数 541

一直以来,有个目标是:使用Beetl的时候,如果web root 里有模板文件,则beetl从web root里加载。如果没有,则从jar里加载,或者从Db里加载。

这样,工程里大量相同的模板模板可以共用(当你的应用,有N个客户的时候,特别需要这个,更新一个jar,重启一下即可)。

最近有时间了,开始折腾了一下,走了不少弯路。在作者@闲.大赋 的指点下,基本算是实现。

弯路记载:http://bbs.ibeetl.com/bbs/bbs/topic/632-1.html

最后确认CompositeResourceLoader 无法满足自己的需求。于是磕磕碰碰的自己实现了一个ResourceLoader,自定义加载模板。

直接贴代码吧

package com.jfinal.ext.beetl;

import java.io.File;
import java.net.URL;
import java.util.Map;

import org.beetl.core.GroupTemplate;
import org.beetl.core.Resource;
import org.beetl.core.ResourceLoader;
import org.beetl.core.misc.BeetlUtil;
/**
 * 自定义的ResourceLoader,用于支持从文件,jar和数据库里加载模板。
 * @author Neoman
 */
public class AppResourceLoader implements ResourceLoader{
	private String root = null;
	boolean autoCheck = false;
	//模板来自文件
	boolean fromFile = true;
	//模板来自Db
	boolean fromDb = false;
	//模板来自jar包
	boolean fromJar = false;
	
	protected String charset = "UTF-8";
	String functionRoot = "functions";
	GroupTemplate gt = null;
	String functionSuffix = "fn";
	ClassLoader classLoader = null;

	/**
	 * 使用加载beetl.jar的classloader,以及默认root为根目录
	 */
	public AppResourceLoader()
	{
		//保留,用于通过配置构造一个ResouceLoader
		classLoader = this.getClass().getClassLoader();
		this.root = "";

	}

	/** 使用指定的classloader
	 * @param classLoader
	 */
	public AppResourceLoader(ClassLoader classLoader)
	{

		this.classLoader = classLoader;
		this.root = "";

	}

	/**使用指定的classloader和root
	 * @param classLoader
	 * @param root 模板路径,如/com/templates/
	 */
	public AppResourceLoader(ClassLoader classLoader, String root)
	{

		this.classLoader = classLoader;
		this.root = root;

	}

	/**
	 * @param classLoader
	 * @param root
	 * @param charset 
	 */
	public AppResourceLoader(ClassLoader classLoader, String root, String charset)
	{

		this(classLoader, root);
		this.charset = charset;
	}

	/** 
	 * @param root ,/com/templates/如其后的resourceId对应的路径是root+"/"+resourceId
	 */
	public AppResourceLoader(String root)
	{

		this();
		if (root.equals("/"))
		{
			this.root = "";
		}
		else
		{
			this.root = root;
		}

	}

	public AppResourceLoader(String root, String charset)
	{

		this(root);
		this.charset = charset;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.beetl.core.ResourceLoader#getResource(java.lang.String)
	 */
	@Override
	public Resource getResource(String key)
	{
		AppResource resource = new AppResource(root, key, this);
		resource.setFromFile(fromFile);
		resource.setFromDb(fromDb);
		resource.setFromJar(fromJar);
		return resource;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.beetl.core.ResourceLoader#close()
	 */
	@Override
	public void close()
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean isModified(Resource key)
	{
		if (this.autoCheck)
		{
			return key.isModified();
		}
		else
		{
			return false;
		}
	}

	public boolean isAutoCheck()
	{
		return autoCheck;
	}

	public void setAutoCheck(boolean autoCheck)
	{
		this.autoCheck = autoCheck;
	}

	public String getRoot()
	{
		return root;
	}

	@Override
	public void init(GroupTemplate gt)
	{
		Map<String, String> resourceMap = gt.getConf().getResourceMap();
		if (resourceMap.get("root") != null)
		{
			String temp = resourceMap.get("root");
			if (temp.equals("/") || temp.length() == 0)
			{

			}
			else
			{

				if (this.root.endsWith("/"))
				{
					this.root = this.root + resourceMap.get("root");
				}
				else
				{
					this.root = this.root + "/" + resourceMap.get("root");
				}

			}

		}

		if (this.charset == null)
		{
			this.charset = resourceMap.get("charset");

		}

		this.functionSuffix = resourceMap.get("functionSuffix");

		this.autoCheck = Boolean.parseBoolean(resourceMap.get("autoCheck"));
		this.functionRoot = resourceMap.get("functionRoot");
		//初始化functions
		URL url = classLoader.getResource("");
		this.gt = gt;
		
		if (url!=null&&url.getProtocol().equals("file"))
		{

			File fnRoot = new File(url.getFile() + File.separator + root + File.separator + this.functionRoot);
			if (fnRoot.exists())
			{
				String ns = "";
				String path = "/".concat(this.functionRoot).concat("/");
				BeetlUtil.autoFileFunctionRegister(gt, fnRoot, ns, path, this.functionSuffix);
			}

		}

	}

	
	@Override
	public boolean exist(String key)
	{
		URL url = this.classLoader.getResource(root + key);
		if(url==null){
			//兼容以前的
			url = this.classLoader.getClass().getResource(root + key);
		}
		return url!=null;
		

	}

	public String getCharset()
	{
		return charset;
	}

	public void setCharset(String charset)
	{
		this.charset = charset;
	}

	@Override
	public String getResourceId(Resource resource, String id)
	{
		if (resource == null)
			return id;
		else
			return BeetlUtil.getRelPath(resource.getId(), id);
	}

	public ClassLoader getClassLoader() {
		return classLoader;
	}

	public void setClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@Override
	public String getInfo() {
		return  "ClassLoader:"+this.classLoader+" Path:"+root;
	}

	public boolean isFromFile() {
		return fromFile;
	}

	public void setFromFile(boolean fromFile) {
		this.fromFile = fromFile;
	}

	public boolean isFromDb() {
		return fromDb;
	}

	public void setFromDb(boolean fromDb) {
		this.fromDb = fromDb;
	}

	public boolean isFromJar() {
		return fromJar;
	}

	public void setFromJar(boolean fromJar) {
		this.fromJar = fromJar;
	}

}

AppResourceLoader基本复制之前org.beetl.core.resource.WebAppResourceLoader的。init 注册函数的时候,还是仅仅读取web root里的目录。因为我自己的是代码里注册了,所以,也没去实现从jar加载function

package com.jfinal.ext.beetl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;

import org.beetl.core.Resource;
import org.beetl.core.ResourceLoader;
import org.beetl.core.exception.BeetlException;

import com.jfinal.kit.PathKit;
/**
 * 模板资源读取,支持从db,文件,classpath,jar 里读取模板资源:
 * 先从webroot的文件里读取,如果没有,则读取数据库,如何还找不到,读取jar或者classpath里的
 * @author Neoman
 *
 */
public class AppResource extends Resource{
	String root = null;

	File file = null;
	long lastModified = 0;
	//模板来自文件
	boolean fromFile = true;
	//模板来自Db
	boolean fromDb = false;
	//模板来自jar包
	boolean fromJar = false;
	public AppResource(String root, String key, ResourceLoader resourceLoader)
	{
		super(key, resourceLoader);
		this.root = root;
	}

	@Override
	public Reader openReader()
	{
		InputStream is = null;
		Reader br;
		AppResourceLoader loader = (AppResourceLoader) this.resourceLoader;
		try
		{	//从文件里读取,一般的web root里
			file = new File(PathKit.getWebRootPath() + root, id);
			if (file.exists() && fromFile) {
				is = new FileInputStream(file);
			}
			//从数据库里读取,暂未实现
			if (is == null && fromDb) {
				
			}
			//从jar 或者classpath里读取
			if (is == null && fromJar) {
				ClassLoader cs = loader.getClassLoader();
				URL url = cs.getResource(root + id);
				if(url==null){
					//兼容以前的写法
					url = resourceLoader.getClass().getResource(root + id);
				}
				if (url == null)
				{
					BeetlException be = new BeetlException(BeetlException.TEMPLATE_LOAD_ERROR);
					be.resourceId = this.id;
					throw be;
				}
				is = url.openStream();
			}
			if (is == null) {
				BeetlException be = new BeetlException(BeetlException.TEMPLATE_LOAD_ERROR, " 模板不存在: " + loader.getInfo());
				be.resourceId = this.id;
				throw be;
			}
			br = new BufferedReader(new InputStreamReader(is, loader.charset));
			return br;
		}
		catch (UnsupportedEncodingException e)
		{
			return null;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			BeetlException be = new BeetlException(BeetlException.TEMPLATE_LOAD_ERROR, " 模板根目录为 " + loader.getRoot());
			be.resourceId = this.id;
			throw be;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			BeetlException be = new BeetlException(BeetlException.TEMPLATE_LOAD_ERROR, " 模板根目录为 " + loader.getRoot());
			be.resourceId = this.id;
			throw be;
		}
	}

	/**
	 * 目前,只能跟踪文件的变化
	 */
	@Override
	public boolean isModified()
	{
		if (fromFile && file != null && file.exists())
		{
			return file.lastModified() != this.lastModified;
		}
//		String refresh = SysConfig.dao.queryConfig("beetl.template.refresh", "0");
//		if (!"0".equals(refresh)) {//不等于0,刷新模板
//			SysConfig.dao.updateConfig("beetl.template.refresh", "0");
//			return true;
//		}
		//jar里,肯定要重启了
		if (fromJar)
		{
			return false;
		}
		//db里 判断时间--暂未 实现
		if (fromDb)
		{
			return false;
		}
		
		return false;
	}

	@Override
	public String getId()
	{
		return id;
	}

	public boolean isFromFile() {
		return fromFile;
	}

	public void setFromFile(boolean fromFile) {
		this.fromFile = fromFile;
	}

	public boolean isFromDb() {
		return fromDb;
	}

	public void setFromDb(boolean fromDb) {
		this.fromDb = fromDb;
	}

	public boolean isFromJar() {
		return fromJar;
	}

	public void setFromJar(boolean fromJar) {
		this.fromJar = fromJar;
	}

}

暂时未用到从db加载,所以没去实现了。不过也很简单了。AppResource 主要是 openReader 里面判断资源从哪里加载。

博客粗略记录一下,希望对大家有用,欢迎交流,微信:netsafer

展开阅读全文
打赏
0
1 收藏
分享
加载中
不错,记录一下
2017/07/16 21:06
回复
举报
更多评论
打赏
1 评论
1 收藏
0
分享
返回顶部
顶部