J2EE应用用户接口开发(二) 作者 KURT A. GABRICK DAVID B. WEISS 出处 J2EE and XML Development第五章 地址 <http://www.manning.com/gabrick>
本文是J2EE和XML开发用户接口的第二部分,如果对文中的例子不熟悉请参见本文的第一部分--J2EE和XML开发用户接口(一) <http://www.csdn.net/develop/article/19/19844.shtm>。
四.J2EE联合XML的解决方案 首先我们要接触的XML架构是联合使用XSLT和J2EE表示层组件。XSLT提供了一种通用的方法,可以将XML文档转换成为各种输出格式。这使得瘦客户端用户接口开发变得十分简易。XSLT处理器的输出格式由XSL样式定义的转换规则决定。我们的例子需要HTML和WML格式的输出。如果你队XSLT使用不是很清楚,可以参见<http://www.zvon.org>网站提供的XSLT在线教程。
4. 1 将XSLT加入Web开发流程 在第一部分中,我们创建了一个servlet控制器,一个定制标记(同时也是一个JavaBean)和四个JSP页。将XSLT处理加进来,对我们的设计可以产生下列四个影响: ·不再需要使用我们设计的JSP ·不再需要使用我们设计的定制标记(同时也是JavaBean) ·我们需要一个新的出filter组件处理XSLT ·我们需要修改我们的WatchListJSPServlet移除JSP的转发语句
Filter过滤器是J2EE表示层框架中的新成员。它们对于将Web工作流程连接起来很有用处。一个过滤器可以被应用于特定的请求或者你的整个应用中。过滤器可以对请求进行预处理(当请求到达servlet前)或者后处理。在本文的例子中,我们只对任何被我们的servlet控制器处理的请求的后处理感兴趣。 XSLT请求处理流程开始于我们的servlet控制器接受到一个询问股价的请求。Servlet通过ListBuilder类与应用逻辑层交换信息,而ListBuilder类的返回一个JDOM文档。视图的选择逻辑现在将由我们新的过滤器组件处理,它将从XSL样式表中选择一个,而不是原来的JSP。 例子应用全新的体系结构如下图所描述。
 4.1.1 XSLT过滤器处理过程 我们通过开发过滤器来开始我们XSLT的例子,这个过滤器将管理样式表的选择以及XSLT的转换过程。以下是有关与此过滤器如何工作的概括: ·每个从Watch List页来的Web请求都被过滤器拦截并且处理完成后交给控制器。 ·JDOM文档经由HttpRequest对象返回给过滤器。 ·过滤器决定设备的类型和用户所在地区。 ·过滤器选择最合适的XSL样式并且调用XSLT处理器将JDOM结果转换为目标格式。 ·过滤器发送经过XSLT转换的结果到客户的设备。在那里提交并显示。
4.1.2 修改servlet 因为我们的过滤器将选择合适的样式并且没有使用JSP的需要,所以我们修改WatchListServlet使它变得相当简单。它的源代码在列表8中显示。现在servlet与ListBuilder接口交互并且在HttpRequest对象中存储JDOM文档。 列表8 import org.jdom.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** * The stock watchlist servlet with XSLT */ public class WatchListServlet extends HttpServlet { private ListBuilder builderInterface = new ListBuilder(); private ServletConfig config; private ServletContext context; public WatchListServlet() { super(); } public void init(ServletConfig config) throws ServletException { this.config = config; this.context = config.getServletContext(); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(false); if (session == null) { context.getRequestDispatcher("/login.jsp").forward(request, response); return; } String userId = (String) session.getAttribute("userId"); Document quoteList = builderInterface.getWatchList(userId); request.setAttribute("quoteList", quoteList);//不需要使用JavaBean包装文档 } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
4.1.2 建造过滤器 我们的过滤器是一个实现了javax.servlet.Filter接口的类。J2EE Web容器使用基于URL的模式匹配调用过滤器和servlet。当一个过滤器和特定的URL模式匹配时,容器调用doFilter方法,它的签名如下: public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException; FilterChain参数是在请求时所有需要被处理的过程的集合,包括servlet、JSP和其他的可能被调用的过滤器。因为我们的过滤器做的是后处理,所以方法doFilter的所有操作之前必须执行FilterChian: chain.doFilter(request,response); 这个实际上通过Web容器调用了WatchListServlet,由它在请求对象中存储我们所需的JDOM文档。然后,我们在doFilter方法中获得此文档。 HttpServletRequest httpRequest = (HttpServletRequest) request; Document outputDoc = (Document) httpRequest.getAttribute("quoteList");
下一步,我们调用一些辅助方法决定选取哪一个样式用做转换 String outputFormat = getOutputFormat(httpRequest); String locale = getLocaleString(httpRequest); String stylesheetPath = getStylesheet(outputFormat, locale); 这些方法的主体在列表9中。它们和第一部分的那些方法很相似。现在我们已经有了XML文档以及使用哪一个样式,我们可以使用JAXP API进行转换了。 TransformerFactory myFactory = TransformerFactory.newInstance(); Transformer myTransformer = myFactory.newTransformer(new StreamSource(stylesheetPath)); JDOMResult result = new JDOMResult(); myTransformer.transform( new JDOMSource(outputDoc ),result );
现在只剩下利用HttpResponse对象将XSLT的输出写会客户端的逻辑了。 Document resultDoc = result.getDocument(); XMLOutputter xOut = new XMLOutputter(); if (outputFormat.equals("wml")) response.setContentType("text/vnd.wap.wml"); PrintWriter out = response.getWriter(); xOut.output( resultDoc, out );
列表9提供了完整的XSLTFilter类的实现代码 import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import org.jdom.*; import org.jdom.output.*; import org.jdom.transform.*; import javax.xml.transform.*; import javax.xml.transform.stream.*; public class XSLTFilter implements Filter { private FilterConfig filterConfig; public void init(FilterConfig filterConfig)throws ServletException { this.filterConfig = filterConfig; } public FilterConfig getFilterConfig() { return this.filterConfig; } public void setFilterConfig(FilterConfig filterConfig) { this.filterConfig = filterConfig; } public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException { try { chain.doFilter(request,response); HttpServletRequest httpRequest=(HttpServletRequest) request; Document outputDoc=(Document) httpRequest.getAttribute("quoteList"); if (outputDoc == null) return; String outputFormat = getOutputFormat(httpRequest); String locale = getLocaleString(httpRequest); String stylesheetPath=getStylesheet(outputFormat, locale); TransformerFactory myFactory=TransformerFactory.newInstance(); Transformer myTransformer= myFactory.newTransformer(new StreamSource(stylesheetPath)); JDOMResult result = new JDOMResult(); myTransformer.transform(new JDOMSource( outputDoc ), result ); Document resultDoc = result.getDocument(); XMLOutputter xOut = new XMLOutputter(); if (outputFormat.equals("wml")) response.setContentType("text/vnd.wap.wml"); PrintWriter out = response.getWriter(); xOut.output( resultDoc, out ); } catch (Exception e) { System.out.println("Error was:" + e.getMessage()); } } private String getOutputFormat(HttpServletRequest request) { String userAgent = request.getHeader("User-Agent"); // this is where your robust user-agent lookup should happen if (userAgent.indexOf("UP.Browser") >= 0) return "wml"; return "html"; } private String getLocaleString(HttpServletRequest request) { Enumeration locales = request.getHeaders("Accept-Language"); while (locales.hasMoreElements()) { String locale = (String) locales.nextElement(); if (locale.equalsIgnoreCase("en_GB")) return "en_GB"; } return "en_US"; } private String getStylesheet(String outputFormat, String locale) { if (locale.equals("en_US")) { if (outputFormat.equals("html")) return "watchlist.html.en_US.xsl"; else return "watchlist.wml.en_US.xsl"; } else { if (outputFormat.equals("html")) return "watchlist.html.en_GB.xsl"; else return "watchlist.wml.en_GB.xsl"; } } public void destroy() {} }
4.1.3 开发样式 最后,我们需要四个新的,供XSLT使用的XSL样式,提供XML到输出的转换功能。我们需要把前一部分开发的JSP、JavaBean和定制标记转换为四个XSLT转换规则。尽管有许多不同的方法开发XSL样式,最直接的方式就是基于模板方法。XSL样式的这种方式与我们JSP中的模板方式十分类似。 列表10包含XSL样式,它将股票引用列表XML文档转换在为HTML格式,地区是美国。注意这个文件与HTML文件的区别,最主要的是它将整个文档用<xsl:sytlesheet>元素和全局的<xsl:template>元素包装起来。<xsl:stylesheet>元素指明这个文件是一组转换规则,而<xsl:template>元素是应用与XML文档根节点的全局转换规则。对于XSLT开发的分析超出了本文的范围,但它是使用不同方式转换XML的强有力的工具。
列表10 美国地区生成HTML的样式 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl=<http://www.w3.org/1999/XSL/Transform> version="1.0"> <xsl:output method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"/> <xsl:template match="/"> <!-全局转换规则的开始à <html> <head><title>Your Watch List</title></head> <body> <h1>Your Stock Price Watch List</h1> <h3>Hello, <xsl:value-of select="/customer[@first-name]"/>! <!-使用Xpath选取用户名à </h3> <h3> Here are the latest price quotes for your watch list stocks. </h3> <p><i> Price quotes were obtained at <xsl:value-of select="/quote-list[@time]"/> <!-从quote-list文档中获得产生时间à on <xsl:value-of select="/quote-list[@date]"/> <!-从quote-list文档中获得产生日期à </i></p> <table cellpadding="5" cellspacing="0" border="1"> <tr> <th>Stock Symbol</th> <th>Company Name</th> <th>Last Price</th> <th>Easy Actions</th> </tr> <xsl:for-each select="//quote"> <!-迭代文档中所有的quote元素à <tr> <td><xsl:value-of select="@symbol"/></td> <td><xsl:value-of select="@name"/></td> <td>$$ <xsl:value-of select="./price[@currency=USD]"/> </td> <td> <a href="http://www.exampleco.com/buyStock?symbol= <xsl:value-of select="@symbol"/>"> buy </a> <a href="http://www.exampleco.com/sellStock?symbol= <xsl:value-of select="@symbol"/>"> sell </a> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
4.2 分析结果 XSLT架构使我们的例子获得更好模块化和伸缩性。它允许我们创建单一的,整体的表示层,这就能为各种客户类型和地区提供合适的服务。当需要加入新的客户类型和地区时,我们仅仅需要向框架添加额外的样式并且适当的选取它们。通过使样式的选取变得可配置,我们能够减少为了创建单一文档而加入的扩展过程并且更新我们的Web应用配置文件使得过滤器可以被使用。 此架构的另外一个主要的优势是它可以更加有效地划分开发角色。显示页面作者可以实现XSL并且开发者能够集中精力在生成XML的过程上。这些任务能在互相独立的情况下完成。 然而,角色分离的优势面临着自身的挑战。例如,使用XSL开发用户接口比使用标准的HTML要困难的多,并且需要更多网页设计师不熟悉的编程技巧。人们期待着不久的将来图形工具能处理这个问题。实际运用这个架构的另一个挑战是,当前在IT产业中还比较缺乏足够的XSLT技术支持。虽然这将随着时间而改变,但是它已经成为将XML整合到表示层的障碍。
导读 本文的第三部分,将针对文章的例子介绍如何使用XSLT为XML生成PDF文档以及Web发布框架Cocoon。
更多信息 1. <http://www.theserverside.com/> 2. <http://www.javaworld.com/>
本文由starchu1981保留版权,如果需要转贴请写明作者和出处。 
|