【www.gdgbn.com--php应用】
J2EE应用用户接口开发(二)
DAVID B. WEISS
出处 J2EE and XML Development第五章
地址 <http://www.manning.com/gabrick>
本文是J2EE和XML开发用户接口的第二部分,如果对文中的例子不熟悉请参见本文的第一部分--J2EE和XML开发用户接口(一)
四.J2EE联合XML的解决方案
首先我们要接触的XML架构是联合使用XSLT和J2EE表示层组件。XSLT提供了一种通用的方法,可以将XML文档转换成为各种输出格式。这使得瘦客户端用户接口开发变得十分简易。XSLT处理器的输出格式由XSL样式定义的转换规则决定。我们的例子需要HTML和WML格式的输出。如果你队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文件的区别,最主要的是它将整个文档用
列表10 美国地区生成HTML的样式Your Stock Price Watch List
Hello,
Here are the latest price quotes for
your watch list stocks.
Price quotes were obtained atStock Symbol Company Name Last Price Easy Actions
$$
http://www.exampleco.com/buyStock?symbol=
buy
http://www.exampleco.com/sellStock?symbol=
sell
4.2 分析结果
XSLT架构使我们的例子获得更好模块化和伸缩性。它允许我们创建单一的,整体的表示层,这就能为各种客户类型和地区提供合适的服务。当需要加入新的客户类型和地区时,我们仅仅需要向框架添加额外的样式并且适当的选取它们。通过使样式的选取变得可配置,我们能够减少为了创建单一文档而加入的扩展过程并且更新我们的Web应用配置文件使得过滤器可以被使用。
此架构的另外一个主要的优势是它可以更加有效地划分开发角色。显示页面作者可以实现XSL并且开发者能够集中精力在生成XML的过程上。这些任务能在互相独立的情况下完成。
然而,角色分离的优势面临着自身的挑战。例如,使用XSL开发用户接口比使用标准的HTML要困难的多,并且需要更多网页设计师不熟悉的编程技巧。人们期待着不久的将来图形工具能处理这个问题。实际运用这个架构的另一个挑战是,当前在IT产业中还比较缺乏足够的XSLT技术支持。虽然这将随着时间而改变,但是它已经成为将XML整合到表示层的障碍。
导读
本文的第三部分,将针对文章的例子介绍如何使用XSLT为XML生成PDF文档以及Web发布框架Cocoon。
更多信息
1.
2.
本文由starchu1981保留版权,如果需要转贴请写明作者和出处。