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保留版权,如果需要转贴请写明作者和出处。