SiteMesh 是opensymphony开源组织下一款优秀的Java组件,一般用于页面模板装饰,以减少重复的视图代码为目的。
SiteMesh目前最新版本2.4.1,对HTML页面支持良好,不支持WML页面的装饰。
集成SiteMesh到web application较为简单,在这里假设读者熟悉SiteMesh用于HTML的装饰,如果读者不太了解SiteMesh的用法,请查阅官方相关文档。下面简单列出SiteMesh的使用步骤:
一、SiteMesh用法
1.1 web.xml文件配置过滤器
<!--sitemesh filter--> <filter> <filter-name>sitemesh</filter-name> <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class> </filter> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> </filter-mapping>
1.2 配置sitemesh.xml文件
<sitemesh> <property name="decorators-file" value="/WEB-INF/decorators.xml"/> <excludes file="${decorators-file}"/> <page-parsers> <parser default="true" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser"/> <parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser"/> <parser content-type="text/html;charset=UTF-8" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser"/> </page-parsers> <decorator-mappers> <mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper"> <param name="property.1" value="meta.decorator" /> <param name="property.2" value="decorator" /> </mapper> <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper"> <param name="config" value="${decorators-file}"/> </mapper> </decorator-mappers> </sitemesh>
1.3 decorators.xml 文件
<decorators defaultdir="/view/decorators"> <excludes> <pattern>/resources/*</pattern> </excludes> <decorator name="system" page="system.jsp"/> <decorator name="background" page="background.jsp"/> <decorator name="wml" page="wml.jsp"/> </decorators>
1.4 具体装饰模板
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"%> <%@ page pageEncoding="utf-8" %> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <link rel="stylesheet" type="text/css" media="all" href="styles/default/theme.css" /> <script language="javascript" src="scripts/jquery/jquery-min.js"></script> <decorator:head/> <title><decorator:title/>|title test</title> </head> <body <decorator:getProperty property="body.id" writeEntireProperty="true"/> <decorator:getProperty property="body.class" writeEntireProperty="true"/>> <jsp:include page="/view/common/messages.jsp"/> <decorator:body/> <jsp:include page="/view/html/common/footer.jsp"/> </body> </html>
1.5 视图页面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %> <%@ include file="/view/common/taglibs.jsp" %> <%@ page import="org.apache.shiro.SecurityUtils" %> <html> <head> <meta name="decorator" content="system"/> <title>bye!</title> </head> <body> XXXXXXXXXXXXXXXX </body> </html>
以上是SiteMesh针对HTML页面的用法,下面扩展SiteMesh以实现对WML页面的支持
2扩展SiteMesh以实现对WML页面的支持
2.1 实现com.opensymphony.module.sitemesh.PageParser
package com.wearereading.frameworks.sitemesh.wml; import java.io.IOException; import com.opensymphony.module.sitemesh.Page; import com.opensymphony.module.sitemesh.PageParser; import com.opensymphony.module.sitemesh.html.HTMLProcessor; import com.opensymphony.module.sitemesh.html.State; import com.opensymphony.module.sitemesh.html.rules.HeadExtractingRule; import com.opensymphony.module.sitemesh.html.rules.MetaTagRule; import com.opensymphony.module.sitemesh.html.rules.TitleExtractingRule; import com.opensymphony.module.sitemesh.html.util.CharArray; /** * WML页面解析 * @author Aspen * */ public class WMLPageParser implements PageParser { public Page parse(char[] data) throws IOException { CharArray head = new CharArray(64); CharArray card = new CharArray(4096); //cards TODO 暂只支持一个CARD TokenizedWMLPage page = new TokenizedWMLPage(data, card, head); HTMLProcessor processor = new HTMLProcessor(data, card); State wml = processor.defaultState(); // Core rules for SiteMesh to be functional. wml.addRule(new HeadExtractingRule(head)); // contents of <head> wml.addRule(new CardTagRule(page, card)); // contents of <card> wml.addRule(new TitleExtractingRule(page)); // the <title> wml.addRule(new MetaTagRule(page)); // all <meta> tags processor.process(); return page; } }
package com.wearereading.frameworks.sitemesh.wml; import com.opensymphony.module.sitemesh.html.BasicRule; import com.opensymphony.module.sitemesh.html.Tag; import com.opensymphony.module.sitemesh.html.rules.PageBuilder; import com.opensymphony.module.sitemesh.html.util.CharArray; /** * * @author Aspen * */ public class CardTagRule extends BasicRule{ private final PageBuilder page; private final CharArray card; public CardTagRule(PageBuilder page, CharArray card) { super("card"); this.page = page; this.card = card; } public void process(Tag tag) { if (tag.getType() == Tag.OPEN || tag.getType() == Tag.EMPTY) { for (int i = 0; i < tag.getAttributeCount(); i++) { page.addProperty("card." + tag.getAttributeName(i), tag.getAttributeValue(i)); } card.clear(); } else { context.pushBuffer(new CharArray(64)); } } }
2.2 扩展com.opensymphony.module.sitemesh.Page
package com.wearereading.frameworks.sitemesh.wml; import java.io.IOException; import java.io.Writer; import com.opensymphony.module.sitemesh.Page; /** * WML page * @author Aspen * */ public interface WMLPage extends Page{ /** * Write the contents of the <code><head></code> tag. */ void writeHead(Writer out) throws IOException; /** * Convenience method to return the contents of the <code><head></code> tag as a String. */ String getHead(); }
package com.wearereading.frameworks.sitemesh.wml; import java.io.IOException; import java.io.Writer; import com.opensymphony.module.sitemesh.parser.AbstractPage; /** * * @author Aspen * */ public abstract class AbstractWMLPage extends AbstractPage implements WMLPage { public abstract void writeHead(Writer out) throws IOException; }
package com.wearereading.frameworks.sitemesh.wml; import java.io.IOException; import java.io.Writer; import com.opensymphony.module.sitemesh.html.rules.PageBuilder; import com.opensymphony.module.sitemesh.html.util.CharArray; /** * * @author Aspen * */ public class TokenizedWMLPage extends AbstractWMLPage implements PageBuilder { private CharArray[] cards; private CharArray head; public TokenizedWMLPage(char[] original, CharArray[] cards, CharArray head) { this.pageData = original; this.cards = cards; this.head = head; addProperty("title", ""); } public TokenizedWMLPage(char[] original, CharArray card, CharArray head){ this(original,new CharArray[]{card},head ); } public void writeHead(Writer out) throws IOException { out.write(head.toString()); } public void writeBody(Writer out) throws IOException { for( int i=0;i<cards.length;i++) out.write(cards[i].toString()); } public String getHead() { return head.toString(); } public String getBody() { StringBuffer body = new StringBuffer(); for( int i=0;i<cards.length;i++) body.append(cards[i].toString()); return body.toString(); } public String getPage() { return new String(pageData); } }
2.3 CARD标签
package com.wearereading.frameworks.sitemesh.wml; import com.opensymphony.module.sitemesh.taglib.AbstractTag; /** * * @author Aspen * */ public class CardTag extends AbstractTag { private static final long serialVersionUID = 1L; public final int doEndTag() { try { getPage().writeBody(getOut()); } catch (Exception e) { trace(e); } return EVAL_PAGE; } }
2.4 扩展SiteMesh 过滤器
package com.wearereading.frameworks.sitemesh.wml; import javax.servlet.FilterConfig; import com.opensymphony.module.sitemesh.Config; import com.opensymphony.module.sitemesh.Factory; import com.opensymphony.sitemesh.ContentProcessor; import com.opensymphony.sitemesh.webapp.SiteMeshFilter; import com.opensymphony.sitemesh.webapp.SiteMeshWebAppContext; /** * 扩展支持WML * @author Aspen * */ public class WMLSupportedSiteMeshFilter extends SiteMeshFilter{ private FilterConfig filterConfig; public void init(FilterConfig filterConfig) { this.filterConfig = filterConfig; super.init(filterConfig); } public void destroy() { filterConfig = null; super.destroy(); } protected ContentProcessor initContentProcessor(SiteMeshWebAppContext webAppContext) { Factory factory = Factory.getInstance(new Config(filterConfig)); factory.refresh(); return new WMLPageParser2ContentProcessor(factory); } }
package com.wearereading.frameworks.sitemesh.wml; import java.io.IOException; import com.opensymphony.module.sitemesh.Factory; import com.opensymphony.module.sitemesh.HTMLPage; import com.opensymphony.module.sitemesh.Page; import com.opensymphony.module.sitemesh.PageParser; import com.opensymphony.module.sitemesh.filter.HttpContentType; import com.opensymphony.sitemesh.Content; import com.opensymphony.sitemesh.SiteMeshContext; import com.opensymphony.sitemesh.compatability.HTMLPage2Content; import com.opensymphony.sitemesh.compatability.PageParser2ContentProcessor; /** * * @author Aspen * */ public class WMLPageParser2ContentProcessor extends PageParser2ContentProcessor { private final Factory factory; public WMLPageParser2ContentProcessor(Factory factory) { super(factory); this.factory=factory; } public Content build(char[] data, SiteMeshContext context) throws IOException { HttpContentType httpContentType = new HttpContentType(context.getContentType()); PageParser pageParser = factory.getPageParser(httpContentType.getType()); Page page = pageParser.parse(data); if( page instanceof WMLPage) return new WMLPage2Content((WMLPage) page); return new HTMLPage2Content((HTMLPage) page); } }
package com.wearereading.frameworks.sitemesh.wml; import java.io.IOException; import java.io.Writer; import com.opensymphony.sitemesh.Content; /** * * @author Aspen * */ public class WMLPage2Content implements Content { private final WMLPage page; public WMLPage2Content( WMLPage page ){ this.page = page; } public void writeOriginal(Writer out) throws IOException { page.writePage(out); } public int originalLength() { return page.getContentLength(); } public void writeBody(Writer out) throws IOException { page.writeBody(out); } public void writeHead(Writer out) throws IOException { page.writeHead(out); } public String getTitle() { return page.getTitle(); } public String getProperty(String name) { return page.getProperty(name); } public String[] getPropertyKeys() { return page.getPropertyKeys(); } public void addProperty(String name, String value) { page.addProperty(name, value); } }
SiteMesh支持装饰HTML和WML的用法
需要上传的附件(sitemesh-2.4.1-wml-0.9.jar),你也可以使用上述源代码。
3.1 web.xml
<!--sitemesh filter--> <filter> <filter-name>sitemesh</filter-name> <filter-class>com.wearereading.frameworks.sitemesh.wml.WMLSupportedSiteMeshFilter</filter-class> </filter> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> </filter-mapping>
3.2 sitemesh.xml
<sitemesh> <property name="decorators-file" value="/WEB-INF/decorators.xml"/> <excludes file="${decorators-file}"/> <page-parsers> <parser default="true" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser"/> < parser content-type="text/vnd.wap.wml" class="com.wearereading.frameworks.sitemesh.wml.WMLPageParser"/> <parser content-type="text/vnd.wap.wml;charset=UTF-8" class="com.wearereading.frameworks.sitemesh.wml.WMLPageParser"/> <parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser"/> <parser content-type="text/html;charset=UTF-8" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser"/> </page-parsers> <decorator-mappers> <mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper"> <param name="property.1" value="meta.decorator" /> <param name="property.2" value="decorator" /> </mapper> <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper"> <param name="config" value="${decorators-file}"/> </mapper> </decorator-mappers> </sitemesh>
3.3 装饰模板
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <%@ page contentType="text/vnd.wap.wml;charset=UTF-8"%> <%@ taglib uri="http://www.wearereading.com/frameworks/sitemesh/wml/decorator" prefix="wml-decorator"%> <wml> <head> <decorator:head/> <meta name="keyword" content="WAP"/> </head> <card <decorator:getProperty property="card.id" writeEntireProperty="true" /> <decorator:getProperty property="card.title" writeEntireProperty="true"/>> <jsp:include page="/view/wml/common/header.jsp" /> <wml-decorator:card/> <jsp:include page="/view/wml/common/footer.jsp" /> </card> </wml>