Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1068460
  • 博文数量: 282
  • 博客积分: 10865
  • 博客等级: 上将
  • 技术积分: 2480
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-12 12:35
文章存档

2017年(1)

2016年(3)

2015年(10)

2014年(12)

2013年(5)

2012年(10)

2011年(29)

2010年(3)

2008年(13)

2007年(92)

2006年(104)

我的朋友

分类: Java

2007-04-22 18:35:50

1. 使用 sitemesh 的 Servlet Filter 做页面修饰.

这种方式是 sitemesh 默认的使用方式,我们先来分析一下工作流程.

Action的定义(webwork-default.xml):

代码
  1. <result-type name="freemarker" class="com.opensymphony.webwork.views.freemarker.FreemarkerResult" default="true"/>  
  2. <action name="viewLogin" class="foo.bar.viewLoginAction">  
  3.     <result name="success">/login.ftlresult>  
  4. action>  
render_code();

当客户端调用viewLogin,Web容器根据.action后缀,交给webwork的 ServletDispatcher 处理,
这个 viewLogin 会经过 ServletDispatcher->proxy.execute()->Action.execute()->executeResult() 等一系列处理,
因为 viewLogin 的 result-type 是 freemarker, 所以 executeResult() 会调用
com.opensymphony.webwork.views.freemarker.FreemarkerResult 来把 login.ftl 作为Template
写入 response.
FreemarkerResult()在创建FreeMarker Template的时候,会为它创建一个"加强版"的TemplateModel,包含以下对象:
* $stack = OgnlValueStack;
* $webwork = FreemarkerWebWorkUtil, a toolbox providing services like formatting url, accessing the value stack, etc;
* $name-of-property = property retrieved from the value stack.
* $Request = HttpServletRequest;
* $Session = HttpServletResponse;
* $Application = OgnlValueStack.
这个特性是webwork的FreemarkerResult为我们提供的..

这时,webwork的工作基本就结束了,接下来, sitemesh的PageFilter出场了...

PageFilter会根据request找到相应的 decorator, 我们假设它是 main.dec
然后调用

代码
  1. RequestDispatcher dispatcher = context.getRequestDispatcher(decorator.getPage());   
  2. dispatcher.include(request, response);  
render_code();

因为decorator文件是 .dec 后缀, 而web.xml中映射 .dec 到
com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServlet

所以,此时 FreemarkerDecoratorServlet 接管 .dec 文件, 把 reponse(对应 login.ftl ) Include
到 .dec 的reponse...至此,页面的组合工作就完成了...
不过这次, FreeMarker就没有那么好的运气了, 它的TemplateModel不再是"加强版"的~~
decorator文件中所包含的其他 .ftl 模版, 是不能够使用 $stack, $webwork 等对象的...

总结一下:
粗略的流程是这样的

webwork --> FreemarkerResult --> Sitemesh --> FreeMarker

其中,FreeMarker被调用2次(影响系统性能) ,并且第二次被调用时,其TemplateModel不具备webwork特性(即,非加强版)...

2. 在webwork中使用sitemesh

我们理想中的流程应该是这样的:

webwork --> Sitemesh --> FreemarkerResult

webwork执行Action,执行完毕之后,找到对应的result模版,把这个模版交给sitemesh去修饰,组合成一个新的模版,
再把这个新的模版交给 FreemarkerResult 处理. FreemarkerResult 解析组合后的模版文件并写入response.
所谓"交给sitemesh去修饰",只是调用一下sitemesh的Factory得到其配置文件中所对应的decorator文件,组合模版的工作
还是要我们来做...

这样一来,我们就不再需要 sitemesh 的 Filter, 也不再需要 FreemarkerDecoratorServlet.与第一种方案相比,显然会提升系统性能..

并且更重要的是,这个组合后的模版所对应的TemplateModel是加强版的,decorator中所包含的其他 .ftl 文件,
也同样可以使用 $stack, $webwork 等对象.

此方案的限制条件:
-- decorator文件必须是Freemarker模版,不能用JSP等其他文件...
-- decorator文件的部分,必须是写在文件中的,不能是include进来的..
-- 如果 decorator文件的部分是include进来的,则源.ftl中的中不能含有其他${xxx}

实现方法:

我们需要重载
com.opensymphony.webwork.views.freemarker.FreemarkerResult的doExecute方法,
在它执行 Template.process 之前, 让 sitemesh 为我们组合好新的模版, 然后狸猫换太子, 执行
MergedTemplate.process.

具体代码如下:

代码
  1. package com.simba.webwork.views.freemarker;   
  2.   
  3. import java.io.IOException;   
  4. import java.io.StringReader;   
  5. import java.io.StringWriter;   
  6.   
  7. import javax.servlet.http.HttpServletRequest;   
  8.   
  9. import com.opensymphony.module.sitemesh.Config;   
  10. import com.opensymphony.module.sitemesh.Decorator;   
  11. import com.opensymphony.module.sitemesh.Factory;   
  12. import com.opensymphony.module.sitemesh.HTMLPage;   
  13. import com.opensymphony.module.sitemesh.Page;   
  14. import com.opensymphony.module.sitemesh.PageParser;   
  15. import com.opensymphony.module.sitemesh.filter.TextEncoder;   
  16. import com.opensymphony.webwork.ServletActionContext;   
  17. import com.opensymphony.xwork.ActionInvocation;   
  18.   
  19. import freemarker.template.SimpleHash;   
  20. import freemarker.template.Template;   
  21. import freemarker.template.TemplateException;   
  22. import freemarker.template.TemplateModel;   
  23.   
  24. /**   
  25.  *    
  26.  * @author simba   
  27.  *   
  28.  */   
  29. public class SitemeshFreemarkerResult extends FreemarkerResult   
  30. {   
  31.     private static final long serialVersionUID = 6889674369040918834L;   
  32.     private final static TextEncoder TEXT_ENCODER = new TextEncoder();   
  33.        
  34.     public void doExecute(String location, ActionInvocation invocation) throws IOException,   
  35.             TemplateException   
  36.     {   
  37.         this.location = location;   
  38.         this.invocation = invocation;   
  39.         this.configuration = getConfiguration();   
  40.         this.wrapper = getObjectWrapper();   
  41.            
  42.         Template template = configuration.getTemplate(location, deduceLocale());   
  43.         TemplateModel model = createModel();   
  44.   
  45.         /*   
  46.          * 调用mergeTemplate得到组合后的模版...   
  47.          */   
  48.         Template mergedTemplate = mergeTemplate(template, model);   
  49.            
  50.         /*   
  51.          * 执行mergedTemplate的处理   
  52.          */   
  53.            
  54.         // Give subclasses a chance to hook into preprocessing   
  55.         if (preTemplateProcess(mergedTemplate, model))   
  56.         {   
  57.             try   
  58.             {   
  59.                 // Process the template   
  60.                 mergedTemplate.process(model, getWriter());   
  61.             }    
  62.             finally   
  63.             {   
  64.                 // Give subclasses a chance to hook into postprocessing   
  65.                 postTemplateProcess(mergedTemplate, model);   
  66.             }   
  67.         }   
  68.     }   
  69.        
  70.     /**   
  71.      * Get decorator from sitemesh's factory according to specific request,   
  72.      * then get this decorator as a FreeMarker template.   
  73.      * By replace the "${body}" with "body template" (determined by action result),   
  74.      * we get the merged template which will handled by FreeMarkerResult.   
  75.      *    
  76.      * @param template   
  77.      * @return   
  78.      */   
  79.     private Template mergeTemplate(Template template, TemplateModel model)   
  80.     {   
  81.         HttpServletRequest request = ServletActionContext.getRequest();   
  82.            
  83.         //创建sitemesh的Factory实例..   
  84.         Factory factory = Factory.getInstance(new Config(ServletActionContext.getServletConfig()));   
  85.            
  86.         //Determine whether the given path should be excluded from decoration or not.    
  87.         if(factory.isPathExcluded(extractRequestPath(request)))   
  88.             return template;   
  89.            
  90.         //传入request,sitemesh根据request在decorators.xml中寻找匹配的decorator   
  91.         Decorator decorator = factory.getDecoratorMapper().getDecorator(request, null);   
  92.            
  93.         if (decorator == null)    
  94.             return template;           
  95.            
  96.         try   
  97.         {   
  98.             //page是对action传进来的.ftl文件进行解析后得到的.   
  99.             Page page = parsePage(template, factory);   
  100.                
  101.             SimpleHash hash = (SimpleHash) model;   
  102.                
  103.             String title, body, head;   
  104.                
  105.             if(page==null)   
  106.             {   
  107.                 title="No Title";   
  108.                 body="No Body";   
  109.                 head="";   
  110.             }   
  111.             else   
  112.             {   
  113.                 HTMLPage htmlPage = (HTMLPage)page;   
  114.                    
  115.                 title=htmlPage.getTitle();   
  116.                    
  117.                 StringWriter buffer = new StringWriter();   
  118.                 htmlPage.writeBody(buffer);   
  119.                 body=buffer.toString();   
  120.                    
  121.                 buffer = new StringWriter();   
  122.                 htmlPage.writeHead(buffer);   
  123.                 head=buffer.toString();   
  124.   
  125.                 hash.put("page",htmlPage);   
  126.             }   
  127.                
  128.             /*   
  129.              * 这里是为了能让include进来的.ftl模版使用${title},${head}和${base}标签,   
  130.              * 但是如果include进来的.ftl模版的<head>head>中又使用了FreeMarker的标签,   
  131.              * 比如${user.name},这个${user.name}就不会被解析了   
  132.              */   
  133.             hash.put("title",title);   
  134.             hash.put("head",head);   
  135.             hash.put("base",request.getContextPath());   
  136.                
  137.             //将decorator所指向的文件,作为FreeMarker Template载入..   
  138.             Template decTemplate = configuration.getTemplate(decorator.getPage(), deduceLocale());   
  139.                
  140.             String deTemplateString = decTemplate.toString();   
  141.             deTemplateStringdeTemplateString = deTemplateString.replace("${body}", body);   
  142.             deTemplateStringdeTemplateString = deTemplateString.replace("${title}", title);   
  143.             deTemplateStringdeTemplateString = deTemplateString.replace("${head}", head);   
  144.                
  145.                
  146.             return new Template(template.getName(), new StringReader(deTemplateString), configuration);   
  147.         }    
  148.         catch (IOException e)   
  149.         {   
  150.             e.printStackTrace();   
  151.             // log me ...   
  152.             return template;   
  153.         }   
  154.     }   
  155.        
  156.     private Page parsePage(Template template, Factory factory) throws IOException   
  157.     {   
  158.         PageParser pageParser = factory.getPageParser(getContentType()!=null?getContentType():"text/html");   
  159.         return pageParser.parse(TEXT_ENCODER.encode((template.toString()).getBytes(), template.getEncoding()));   
  160.     }   
  161.        
  162.     private String extractRequestPath(HttpServletRequest request)   
  163.     {   
  164.         String servletPath = request.getServletPath();   
  165.         String pathInfo = request.getPathInfo();   
  166.         String query = request.getQueryString();   
  167.         return (servletPath == null ? "" : servletPath) + (pathInfo == null ? "" : pathInfo)   
  168.                 + (query == null ? "" : ("?" + query));   
  169.     }   
  170.        
  171. }   
  172.   
render_code();

只要在webwork-default.xml文件中指定 result-type为:

代码
  1. "freemarker" class="foo.bar.SitemeshFreemarkerResult" default="true"/>  

render_code();
就可以了..

阅读(2435) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~