Archive

Archive for December 31st, 2007

PageCacheFilter2

December 31st, 2007 Sparkle No comments
/**
 * 2006-5-1
 * @author Sparkle
 */
public class PageCacheFilter2 implements Filter {
	private Set<string> cacheUrlSet = new HashSet<string>();
	private Set<string> scacheUrlSet = new HashSet<string>();
	private String baseCachePath, contentType;
 
	public void init(FilterConfig config) throws ServletException {
		String cacheUrl = config.getInitParameter("cacheUrl");
		if (cacheUrl != null) {
			StringTokenizer tk = new StringTokenizer(cacheUrl);
			while (tk.hasMoreTokens()) {
				String str = tk.nextToken().trim().toLowerCase();
				if (str.endsWith("?")) {
					str = str.substring(0, str.length() - 1);
					scacheUrlSet.add(str);
				}
				cacheUrlSet.add(str);
			}
		}
		baseCachePath = config.getInitParameter("cachePath");
		contentType = config.getInitParameter("contentType");
	}
 
	private int urlHashCode(HttpServletRequest request) {
		String uri = request.getRequestURI();
		String qStr = request.getQueryString();
		if (qStr != null) {
			uri += ('?' + qStr);
		}
		return Math.abs(uri.hashCode());
	}
 
	private String cacheFilePath(HttpServletRequest request) {
		String uri = request.getRequestURI();
		int hash = urlHashCode(request);
		if (scacheUrlSet.contains(uri.toLowerCase())) {
			String entityId = request.getParameter("entityId");
			if (StringUtils.isNotBlank(entityId)) {
				return baseCachePath + "/article/" + subDir(entityId)
						+ '/' + entityId + '/' + hash + ".wml";
			}
		}
		return baseCachePath + "/other/" + subDir2(String.valueOf(hash))
				+ '/' + hash + ".wml";
	}
 
	private String subDir(String str) {
		int length = str.length();
		if (length > 4) {
			return str.substring(0, length - 4);
		}
		return "0";
	}
 
	private String subDir2(String str) {
		int length = str.length();
		if (length > 4) {
			return str.substring(length - 4, length);
		}
		return str;
	}
 
	private void clearCache(HttpServletRequest request, File cacheFile) {
		if (scacheUrlSet.contains(request.getRequestURI().toLowerCase())) {
			File cacheDir = cacheFile.getParentFile();
			for (File file : cacheDir.listFiles()) {
				file.delete();
			}
			cacheDir.delete();
		} else {
			cacheFile.delete();
		}
	}
 
	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		if (!cacheUrlSet.contains(request.getRequestURI().toLowerCase())) {
			chain.doFilter(req, res);
			return;
		}
 
		HttpServletResponse response = (HttpServletResponse) res;
 
		File cacheFile = new File(cacheFilePath(request));
		if (cacheFile.isFile()) {
			if (request.getParameter("refresh") != null) {
				clearCache(request, cacheFile);
			} else {
				try {
					BufferedReader reader = new BufferedReader(
							new FileReader(cacheFile));
					response.setContentType(contentType);
					String line;
					PrintWriter printWriter = response.getWriter();
					while ((line = reader.readLine()) != null) {
						printWriter.println(line);
					}
					printWriter.close();
					return;
				} catch (IOException e) {
					// ignore
					response.reset();
					System.out.println("read cache file error");
				}
			}
 
		}
 
		StringWriter strWriter = new StringWriter();
		boolean[] support = new boolean[1];
		support[0] = true;
		chain.doFilter(req, new ContentResponseWrapper(response, strWriter,
				support));
		if (support[0] && !response.isCommitted()) {
			String content = strWriter.getBuffer().toString();
 
			PrintWriter printWriter = response.getWriter();
			printWriter.println(content);
			printWriter.close();
 
			File cacheDir = cacheFile.getParentFile();
			if (!cacheDir.exists()) {
				cacheDir.mkdirs();
			}
			PrintWriter writer = new PrintWriter(cacheFile);
			writer.println(content);
			writer.close();
		}
 
	}
 
	public void destroy() {
	}
 
}

代码点评:这段代码的作用是,对指定的url进行cache,用了ContentRewriteFilter一样的做法来获取内容,因为具体的细节有些区别,没有继承ContentRewriteFilter而是直接编写代码。先判断是否有cache文件,如果有就输出给用户,但是如果连接上有refresh参数的话,就会清掉cache文件,如果没有cache文件,就会获取Servlet or JSP输出的内容,原原本本保存到磁盘,供下次用户访问使用。如果设置URL的时候,最后一位是问号,就会进入scacheUrlSet,这个是用于文章类型,主要的不同点是有分页,如果要refresh的话,应该要把所有分页的cache文件都清除。至于类名后面有个2,是因为这个是第二个版本。

(最近在考虑用Squid+Etag来实现一样的功能)

Categories: Uncategorized Tags: ,

ContentRewriteFilter

December 31st, 2007 Sparkle No comments
    /**
     * 2006-4-30
     * @author Sparkle
     */
    public abstract class ContentRewriteFilter implements Filter {
	protected boolean wantRewrite(HttpServletRequest request) {
		return true;
	}
 
	protected abstract String rewrite(String content,
			HttpServletRequest request, HttpServletResponse response);
 
	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		if (!wantRewrite(request) || res.isCommitted()) {
			chain.doFilter(req, res);
			return;
		}
 
		HttpServletResponse response = (HttpServletResponse) res;
		StringWriter strWriter = new StringWriter();
		boolean[] support = new boolean[1];
		support[0] = true;
		chain.doFilter(req, new ContentResponseWrapper(response, strWriter,
				support));
		if (support[0] && !response.isCommitted()) {
			String content = strWriter.getBuffer().toString();
 
			content = rewrite(content, request, response);
 
			PrintWriter printWriter = response.getWriter();
			printWriter.println(content);
			printWriter.close();
		}
 
	}
 
	public void destroy() {
	}
 
	public void init(FilterConfig config) throws ServletException {
	}
    }
 
 
    public class ContentResponseWrapper extends HttpServletResponseWrapper {
	private Writer writer;
 
	private boolean[] support;
 
	public ContentResponseWrapper(HttpServletResponse response, Writer writer,
			boolean[] support) {
		super(response);
		this.writer = writer;
		this.support = support;
	}
 
	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		support[0] = false;
		return super.getOutputStream();
	}
 
	@Override
	public PrintWriter getWriter() throws IOException {
		return new PrintWriter(writer);
	}
 
    }

代码点评:这段代码的作用是,获得Servlet or JSP输出的内容进行改写操作,这是一个基类,继承之后重载abstract rewrite即可获得输出的内容进行改写。这段代码在实际环境工作之后做了不少的修正,对response的commit状态,和是否成功获取内容都有较好的判断。中途用了一个[0]的技巧回传了一个boolean值。使用这个代码实现了动态插入广告,个人短消息等资料,还有sessionid自动补充(!)等等功能

Categories: Uncategorized Tags:

CryptUtil

December 31st, 2007 Sparkle No comments
    /**
     * 2006-6-10
     * @author Sparkle
     */
    public class CryptUtil {
	/**
	 * 将一个数字转换成0-9a-zA-z的62进制
	 */
	public static String encodeLongToString(long i) {
		if (i == 0) {
			return "0";
		} else if (i < 0) {
			throw new IllegalArgumentException();
		}
		StringBuffer buffer = new StringBuffer();
		while (i > 0) {
			buffer.insert(0, encodeIntToChar((int) (i % 62)));
			i /= 62;
		}
		return buffer.toString();
	}
 
	/**
	 * 将0-9a-zA-z的62进制转换成一个数字
	 */
	public static long decodeStringToLong(String s) {
		long i = 0;
		for (int index = 0; index < s.length(); index++) {
			if (index > 0) {
				i *= 62;
			}
			i += decodeCharToInt(s.charAt(index));
		}
		return i;
	}
 
	/**
	 * 将数字作为顺序转换成单位的0-9a-zA-z<br />
	 * 0 -> '0'<br />
	 * 9 -> '9'<br />
	 * 10 -> 'a'<br />
	 * 35 -> 'z'<br />
	 * 36 -> 'A'<br />
	 * 61 -> 'Z'
	 */
	private static char encodeIntToChar(int i) {
		if (i >= 0 && i <= 9) {
			return (char) (i + 48);
		} else if (i >= 10 && i <= 35) {
			return (char) (i + 87);
		} else if (i >= 36 && i <= 61) {
			return (char) (i + 29);
		} else {
			throw new IllegalArgumentException();
		}
	}
 
	/**
	 * 将单位的0-9a-zA-z按顺序转换成数字<br />
	 * '0' -> 0<br />
	 * '9' -> 9<br />
	 * 'a' -> 10<br />
	 * 'z' -> 35<br />
	 * 'A' -> 36<br />
	 * 'Z' -> 61
	 */
	private static int decodeCharToInt(char c) {
		if (c >= '0' && c <= '9') {
			return c - 48;
		} else if (c >= 'a' && c <= 'z') {
			return c - 87;
		} else if (c >= 'A' && c <= 'Z') {
			return c - 29;
		} else {
			throw new IllegalArgumentException();
		}
	}
    }

代码点评:这段代码的作用是,转换出来的字符串会比16进制要短很多

Categories: Uncategorized Tags: