Bea_WeblogicPortal_Ajax2
上一篇 / 下一篇 2006-08-30 16:23:00 / 天气: 晴朗 / 心情: 高兴 / 个人分类:Bea
在BEA WebLogic Portal 8.1中进行Ajax编程,第2部分Linux宝库6e:\a!P&DUUi
摘要
Linux宝库0U_1[~ZwAjax是一种异步编程范型,借助于它,开发人员可以创建高度交互式的Web站点,从而在提高用户效率的同时减轻服务器的负载。Ajax结合了Web services、JavaScript和动态HTML编程技术,可以创造丰富的客户端体验,并提高本地应用程序的可用性。本系列文章的第一篇介绍了这些相关概念以及在BEA WebLogic Portal中实现一个Ajax解决方案所涉及的架构考虑事项。Linux宝库,qLeF)[(A*k R+H
P-bI W+Ry1C0 本文是此系列文章的最后一篇,提供了一组复杂的示例portlet,从一些基本的查找portlet开始,再到一个使用动态表作为前端数据库表的高级例子,最后以一个有趣的例子结尾,最后这个例子在没有使用<object>或<iframe>标签的情况下把一些页面动态嵌入到另一个页面中。
C!J4E XQ0NP^,l0*VDr%o]0 所有例子均给出了完整的源代码,并包括安装脚本和文档。Linux宝库;gkp8@:|;vI;}
关于示例Portlet
Linux宝库"lAg\k$rk本文在这里给出了各种支持Ajax的portlet,并逐行对其源代码(在本文的下载部分中可以找到)进行了解释。本文还包括了一个可重用Java servlet的源代码,这个servlet可用于降低Mozilla Firefox和Microsoft Internet Explorer (IE)中固有的安全限制级别。这个servlet实现了第1部分中描述的代理设计模式。
W c+p.X4~ Y5[0Linux宝库D7o-n'?yZU&?F有一点需要指出,我在代码中使用了最简单的Ajax库,因为我想说明整个过程到底是怎么回事。还有大量可用的库可以极大地减少需要编写的代码量。我在一些代码中使用了Sarissa库,主要用于处理一些跨浏览器的问题。参见Ajax简介,可以了解有关Ajax库的更多信息,比如DWR。当设计和构建自己的生产应用程序时,我不建议您使用这里给出的底层构件。本文中使用的Ajax库可以很好地处理跨浏览器的问题和Ajax绑定(wiring)。
-[-ss6t2e RE"Gx5h0Linux宝库Rg-w8L'Oo$x8yH5j(E本文的余下部分逐个描述了每个示例portlet。我们鼓励您浏览源代码,从而更深入地了解这些portlet是如何实现的。Linux宝库R[*m6v1hEh(|f
Zip Code Lookup
T[!cL"q(J&H2o0 Zip Code Lookup(区号查找)portlet说明了两个可用于调用远程Web服务(例如,一个与Web服务器位于不同计算机上的Web服务)的方法。这两个方法(direct和proxy)是通过使用两个直接命名的按钮Direct和Proxy来调用的。Direct方法直接从Web浏览器调用Web服务,这通常会导致违反Internet Explorer中的安全规则,而在Firefox中则根本无法使用。Proxy方法使用了本文稍后将会描述的Java代理servlet。Linux宝库+o!N(AFk`9H
Linux宝库B)gIIo
Ar5]&o*xL)I0 图 1:调用远程Web服务的Zip Code Lookup portletLinux宝库U.y z[,p m}
Linux宝库*Hf(CEAY'b,c!?注意Zip Code标签旁边的输入栏。我们在这一栏中获得输入值,然后把它传递给后端的Web服务。当我们从该Web服务获得城市和州的信息时,就会把这些信息放在City和State标签右边的相应位置上。Linux宝库(^;iH.S,`:U bQQ ul
%qV$R6H8Z yx0 为了能够引用JavaScript代码中的元素,我们需要为每个元素指定一个惟一的ID。下面给出了zipcode.jsp的相关HTML代码:
R M9?N\`X {0<body style="font-family:helvetica;font-size:10pt;">
<p>Type in a 5-digit zip code to get information on that zip code.</p>
<form name="zipcode" method="post" action="">
<table cellspacing="0" cellpadding="4" frame="box" bordercolor="#dcdcdc" rules="none" style="border-collapse: collapse;">
<tr>
<td>Zip Code</td>
<td><input type="text" size="5" name="zipcode_USZip" id="zipcode_USZip"></td>
<td><input type="button" onclick="zipcode_updateDirect();" value="Direct"/></td>
<td><input type="button" onclick="zipcode_updateProxy();" value="Proxy"/></td>
</tr>
</table>
</form>
<hr color="blue"/>
<table>
<tr>
<td>City</td>
<td><div id="zipcode_city" style="color:blue;"></div></td>
</tr>
<tr>
<td>State</td>
<td><div id="zipcode_state" style="color:blue;"></div></td>
</tr>
<tr>
<td colspan="2">
<form>
<button onclick="zipcode_showResults();">Returned XML <span id="zipcode_status" style="color:blue;"></span>
</button>
</form>
</td>
</tr>
</table>
</body>Linux宝库c/~+y-|
a2y/^'W*m注意,所有ID标签都使用了portlet名称作为前缀。这是一个相当好的最佳实践,您必须将其深植到您的编码风格中。为什么这很重要呢?因为当BEA WebLogic Portal呈现一个门户页面时,它会把所有的portet HTML拷贝和粘贴到一个聚合页面上。所以,如果您有两个portlet,而每个portlet都有一个ID为“mylabel”的元素,那么呈现出来的门户页面中将会有两个元素具有相同的惟一ID。当使用JavaScript/DOM函数getElementById('mylabel')时,返回的元素将会是文档中“mylabel” ID的第一个实例,而这很可能不是您所期望的结果!
6i ?A[HPg$c_"}0BQ;C"}A0 还要注意使用<div>标签作为动态内容的占位符。这是一个样式选择问题,但是我发现,<div>标签具有成为任何内容的容器的巨大潜力,而<td>标签的限制就要严格得多。更多地使用CSS绝对有助于减少使用嵌入的样式标签,但这将会是留给读者的一道练习题。但是,我要指出的是,明智地使用CSS样式可以极大地减少最终要编写的JavaScript数量。记住,CSS可以用于改变一个元素的颜色、位置、可见性、透明度以及其他许多可视化特性。简单的样式交换通常可以让图形化用户界面变得更加具有交互性。在Employee Listing portlet中,当变化被提交给数据库之后,我使用了这项技术来通知用户。
L#j] oO*Y01fQ [W$G*]S'mf0 在这个portlet中,我们将从文本栏中获得输入,调用一个Web服务查找与区号相关的城市和州信息,然后使用该Web服务返回的信息动态更新City 和State <div>元素。页面不会刷新——只有City和State元素会更新。Linux宝库DfC aHK4H
调用Web服务
Linux宝库GT&c.mJNb将被这个portlet调用的Web服务有一个端点在http://www.webservicex.net/uszip.asmx/GetInfoByZIP上,并带有一个参数USZip。所以,获取区号60118的相关城市和州信息的调用应该是http://www.webservicex.net/uszip.asmx/GetInfoByZIP?USZip=60118。这是使用Ajax调用Web服务的惟一方法。您可以创建一条相应的SOAP消息,但是这已经超出了本文的讨论范围。
J7IgIg5v0Cn[%|)W8z|g0 如果我们通过把上面的查询键入到浏览器中,来使用USZip=60118手动调用Web服务,则该Web服务将使用下面的XML文档做出响应。Linux宝库n |2ioB?
<?xml version="1.0" encoding="utf-8" ?>
<NewDataSet>
<Table>
<CITY>Dundee</CITY>
<STATE>IL</STATE>
<ZIP>60118</ZIP>
<AREA_CODE>847</AREA_CODE>
<TIME_ZONE>C</TIME_ZONE>
</Table>
</NewDataSet>7JY|?FM v*V}H0 我们想提取出CITY和STATE元素,并把它们放到我们的Web页面中相应的占位符中。Linux宝库`5d @6dqGJ6j
b-I6Zz9Io0 我们将使用Direct按钮的onclick处理程序来执行一小段嵌入的JavaScript zipcode_updateDirect()。下面给出这些JavaScript代码:
(};W StE&~0function zipcode_updateDirect() {
var url = "http://www.webservicex.net/uszip.asmx/GetInfoByZIP?USZip=";
var zipValue = document.getElementById("zipcode_USZip").value;
// Open a URL connection using the XMLHttpObject. The third parameter specifies that the
// call should be made asynchronously. Set this to false to make this call synchronous.
zipcode_http.open("GET", url + escape(zipValue), false);
// Set a callback handler to a local JavaScript method
zipcode_http.onreadystatechange = zipcode_handleHttpResponse;
// Make the call. You can replace the null value with XML request data if you are doing
// a SOAP-style call instead of using HTTP request parameters.
zipcode_http.send(null);
}?%sJ)[VI0 下面列出了使用XmlHttpObject调用远程Web服务的(几乎是)最简单的过程:
Pb/JOt+FP+{0- 调用XmlHttpObject.open()。
- 为XmlHttpObject设置一个回调处理程序。
- 调用XmlHttpObject.send()。
.^ D/vV7Jxn0mm0L;Ej0 在这种情况下,Web服务是通过使用常规的服务端点直接调用的。当然,这会导致Firefox中出现一些安全问题。在Internet Explorer中,用户将会看到一个对话框,询问他们是否允许这项操作。Linux宝库` It,MXnV
Proxy servlet
$Ix,[9E3Pv]'I0 那么我们应该如何避免这个问题呢?我们使用代理设计模式来创建一个中间服务,该服务驻留在Web服务器上,并负责调用远程服务,然后以XML的形式返回结果(或文本形式,内容是什么无关紧要)。对于浏览器来说,服务似乎与Web站点位于同一台主机上,所以不会出现安全问题。有多种实现这个代理的方式,包括实现为Web服务或Java servlet。我使用了一个servlet,因为它具有快速和易用的特点。该servlet应该尽可能简单,而且调用起来要和原来的Web服务一样简单。所以,我们让这个servlet在查询字符串中使用一个任意的参数列表,其中有一个特定参数叫做“url”,它代表着服务端点的URL。来自Web服务的结果应该被逐字返回给调用方。Linux宝库h+W D.z1^$s+b)j
Linux宝库.mI&il&} Q参照第一篇文章中的架构图(图1和图2),您可以更加直观地了解到direct和proxy调用之间的差别。Linux宝库/R(?|glJ6E~
W._3xEJ$pj*N/a0 下面给出了ProxyServlet的相关代码,我把这些代码放到了我的Portal项目中的WEB-INF/src/demo目录下:Linux宝库^MdF-o(R+em5F
package demo;
import java.io.*;
import java.net.*;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class ProxyServlet extends javax.servlet.http.HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String surl = URLDecoder.decode(request.getParameter("url"));
Enumeration enum = request.getParameterNames();
boolean first = true;
if(request.getParameterMap().size() > 1) {
surl += "?";
}
while(enum.hasMoreElements()) {
String name = (String) enum.nextElement();
if(!name.equals("url")) {
if(!first) surl += "&";
surl += name + "=" + request.getParameter(name);
first = false;
}
}
System.out.println("Redirecting to " + surl);
URL url = new URL(surl);
InputStream is = url.openConnection().getInputStream();
while(true) {
byte[] bytes = new byte[128];
int read = is.read(bytes);
if(read <= 0) break;
response.getOutputStream().write(bytes, 0, read);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
}C-^0T Qw(`0 对于这个要公开的servlet来说,需要修改WEB-INF中的web.xml文件。下面给出了添加的相关代码:
0z m9~k.\9X0<servlet> <servlet-name>ProxyServlet</servlet-name> <servlet-class>demo.ProxyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ProxyServlet</servlet-name> <url-pattern>/ProxyServlet</url-pattern> </servlet-mapping>
X~:K2YR0 代理servlet带有常规的Web服务URL,并使用它作为url参数的值。所以,我们对区号查找Web服务的调用将转换为对下面这个代理servlet的调用:/Portal/ProxyServlet?url=http://www.webservicex.net/uszip.asmx/GetInfoByZIP&USZip=60118。Linux宝库0}0Km~]3J,a6yL3Y
#r:?f1x{ x)}0 从JavaScript代码的角度来看,对代理servlet的调用有点像直接调用Web服务,但是还是有一些小的修改:Linux宝库U6F'~3H*[R/zFvl
function zipcode_updateProxy() {
// This is the endpoint for the zip code Web service
var endurl = escape("http://www.webservicex.net/uszip.asmx/GetInfoByZIP");
// This is the endpoint of the proxy servlet that will make the call on our behalf
var localurl = "/Portal/ProxyServlet";
// This is the input parameter for the zip code
var zipValue = document.getElementById("zipcode_USZip").value;
// This is the actual URL that will be called using the XmlHttpRequest object
var url = localurl + "?url=" + endurl + "&USZip=" + zipValue; // The server-side script
// Open a url connection using the XMLHttpObject. The third parameter specifies that the
// call should be made asynchronously. Set this to false to make this call synchronous.
zipcode_http.open("GET", url, true);
// Set a callback handler to a local JavaScript method
zipcode_http.onreadystatechange = zipcode_handleHttpResponse;
// Make the call. You can replace the null value with XML request data if you are doing
// a SOAP-style call instead of using HTTP request parameters.
zipcode_http.send(null);
}处理异步返回
Linux宝库5m6p;r#V`%L3n&no6U1fWeb服务调用的异步处理与Ajax技术的正确使用是密不可分的。这让事情变得稍微有一点复杂,但并没有您所想的那么复杂。异步调用的一个额外好处就是,您可以以可视化的方式向用户显示调用的状态(在本文稍后的Page Loader例子中,这显得更加重要)。一些Ajax帮助器库可以让使用响应处理程序的过程变得更加简单,但是这样做会让您失去处理不同就绪状态的能力,而您可以使用这些就绪状态向用户提供反馈。Linux宝库(z"eq*B!W%w
Linux宝库)pWbC~3o;z9Vb,Nw下面给出了用于处理一个Ajax回调的模板代码:
&]-_},t-@|-UCy0function zipcode_handleHttpResponse() {
if (zipcode_http.readyState == 4) {
if(zipcode_http.status == 200) {
// Do something with the results
}
else {
// Show the user a status message
}
}
else {
// Show the user a status message
}
}Linux宝库,w-D0WhE m这个模板相当简单,但是功能很强大。第一个分支检查了查询的readyState。想要了解有关就绪状态和响应状态的更多信息,请参见David Teare的Ajax简介。
*EvAY(X?p0更新DOM元素
,_z'w0Q$h_ m0 更新一个DOM元素(比如<div>标签)的过程通常是很直观的。我说“通常”,是因为在大多数用例中,这个过程是简单的,而且可以在浏览器之间进行移植。可以使用下面的代码从返回的XML中提取CITY和STATE元素,并把它们放到Web页面上的<div>元素中。
:Y8X bCsf0// Turned the returned XML indo a DOM document that
// we can manipulate
var doc = (new DOMParser()).parseFromString(
zipcode_http.responseText, "text/xml");
document.getElementById('zipcode_status').innerHTML =
zipcode_http.statusText;
var cityObj = doc.getElementsByTagName('CITY')
.item(0).firstChild.data;
var stateObj = doc.getElementsByTagName('STATE')
.item(0).firstChild.data;
// Set the form fields with the new data
document.getElementById('zipcode_city').innerHTML=zipcode_city;
document.getElementById('zipcode_state').innerHTML=zipcode_state;Linux宝库n0Mz{6Ee j4g~是不是很简单呢?下面这个portlet与这个portlet有很多相同之处,为了简短起见,我们将忽略这些相同的元素。
0E9E7T;I*up1k\b0Stock Quote Lookup Portlet
0z7V4]/Vt0 这个示例portlet(Stock Quote Lookup,股票报价查找)十分类似于Zip Code Lookup portlet,但是返回的XML使用了命名空间,这需要Internet Explorer进行不同的处理。请参见图2中的用户界面。
rF te/l0
-m7b0]h{3|0 图 2:使用命名空间调用远程Web服务的Stock Quote Lookup portletLinux宝库/s-{bS$eiNf
&YP zB2RzT&Pc4^w0 所调用的服务是http://ws.cdyne.com/delayedstockquote/delayedstockquote.asmx /GetQuote?LicenseKey=0&StockSymbol=beas。下面是从该服务返回的XML:Linux宝库;Di3h"\1Hg
<?xml version="1.0" encoding="utf-8" ?>
<QuoteData xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://ws.cdyne.com/">
<StockSymbol>BEAS</StockSymbol>
<LastTradeAmount>9.01</LastTradeAmount>
<LastTradeDateTime>2005-08-26T14:00:00.0000000-04:00</LastTradeDateTime>
<StockChange>-0.03</StockChange>
<OpenAmount>9.02</OpenAmount>
<DayHigh>9.05</DayHigh>
<DayLow>8.97</DayLow>
<StockVolume>1719106</StockVolume>
<MktCap>3.541B</MktCap>
<PrevCls>9.04</PrevCls>
<ChangePercent>-0.33%</ChangePercent>
<FiftyTwoWeekRange>6.42 - 9.86</FiftyTwoWeekRange>
<EarnPerShare>0.358</EarnPerShare>
<PE>25.25</PE>
<CompanyName>BEA SYSTEMS INC</CompanyName>
<QuoteError>false</QuoteError>
</QuoteData>Xq'[ ZjBe0KZ0 把这个结果与Zip Code例子返回的XML进行比较。命名空间在上面是以高亮显示的。Internet Explorer中对于命名空间的处理与Firefox中不同,必须对处理过程进行一些修改。下面给出了用于处理每个版本的代码:
4kXj4T+y/r3Um0if(isMSIE) {
// I have no idea why this is necessary for this particular XML result set,
// but it is. If you can come up with a cross-browser way to make this one work,
// please send me an email at jmargagl@bea.com!
var nodes = doc.documentElement.childNodes;
for(var i = 0; i < nodes.length; i++) {
if(nodes[i].baseName == 'CompanyName') name = nodes[i].text;
if(nodes[i].baseName == 'LastTradeAmount') last = nodes[i].text;
if(nodes[i].baseName == 'StockChange') change = nodes[i].text;
}
}
else {
name = doc.getElementsByTagName('CompanyName')[0].firstChild.data;
last = doc.getElementsByTagName('LastTradeAmount')[0].firstChild.data;
change = doc.getElementsByTagName('StockChange')[0].firstChild.data;
}Linux宝库6EU)xIQ注意,Firefox在找到标签的方式方面没有任何特别之处,因为默认的命名空间已经在顶级元素处的XML中指定了。下一个例子还涉及到了特殊的命名空间,即命名空间中的每个元素的作用域都限定在该命名空间中。处理过程是类似的,但是这两种情况比较而言,Firefox中的实现要简单和清晰得多。Linux宝库"Y8DMq WhMA"Sp.V
Employee Management Portlet
pY4dVu8hv'VX0 现在要给出的这个portlet(Employee Management,员工管理)与上面的完全不同。Employee Management portlet(参见图3)使用Ajax来调用位于WebLogic实例上的一个Web服务。然后,这个Web服务从本地的Pointbase数据库中查询一个表中的记录,并把这些记录返回为格式化的XML文档,而且这些XML文档的架构已经创建好了。它还具有使用相同的Web服务来维护这些记录的能力。Linux宝库YYEGh*Y
Linux宝库G)Hx*WHc*a
图 3:使用一个 Web服务调用数据库后端的Employee Management portlet
QwK+fM/f0 首先来看一看我们要使用的数据库表。下面这段脚本用于创建必需的表和插入一些测试行(位于create.sql文件中):
/p*v&] z#p0CREATE TABLE "WEBLOGIC"."DEMO_EMPLOYEE" (
ID INTEGER NOT NULL,
FirstName VARCHAR (32) NOT NULL,
LastName VARCHAR (50) NOT NULL,
Email CHARACTER (100),
CONSTRAINT demo_employee_pk PRIMARY KEY ( ID)
);
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (1, 'John', 'Doe', 'jdoe@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (2, 'Jane', 'Doe', 'jdoe2@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (3, 'Bill', 'Huevo', 'bhuevo@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (4, 'Larry', 'Ellison', 'lellison@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (5, 'Bob', 'Loblaw', 'bloblaw@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (6, 'Sarah', 'Johnson', 'sjohnson@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (7, 'Kelly', 'Cobain', 'kcobain@nowhere.com');
INSERT INTO WEBLOGIC.DEMO_EMPLOYEE VALUES (8, 'Rusty', 'Ziebart', 'rziebart@nowhere.com');Linux宝库~Z0q0| d在这个例子中,Web服务由处理数据库更新的数据库控件提供支持。下面给出了这个Web服务的代码:Linux宝库7\qz7D _ma.F;yS
package WebServices.EmployeeListing;
import demo.EmployeesDocument;
import java.util.Collection;
import java.util.Iterator;
/**
* @common:xmlns namespace="demo" prefix="ns0"
*/
public class EmployeeListing implements com.bea.jws.WebService
{
/**
* @common:control
*/
private WebServices.EmployeeListing.EmployeeDBControl employeeControl;
static final long serialVersionUID = 1L;
public static class Employee {
String firstName;
String lastName;
public Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
/**
* @common:operation
* @jws:return-xml schema-element="ns0:Employees"
*/
public demo.EmployeesDocument getEmployees()
{
WebServices.EmployeeListing.Employee[] employees = employeeControl.getEmployees();
demo.EmployeesDocument doc = demo.EmployeesDocument.Factory.newInstance();
EmployeesDocument.Employees emps = doc.addNewEmployees();
EmployeesDocument.Employees.Employee emp;
for(int i = 0; i < employees.length; i++) {
emp = emps.addNewEmployee();
emp.setId(employees[i].getID());
emp.setFirstName(employees[i].getFirstName().trim());
emp.setLastName(employees[i].getLastName().trim());
emp.setEmail(employees[i].getEmail().trim());
}
return doc;
}
/**
* @common:operation
*/
public void deleteEmployee(int ID)
{
employeeControl.removeEmployee(ID);
}
/**
* @common:operation
*/
public int addEmployee(String firstName, String lastName, String email)
{
int ID = (int) System.currentTimeMillis();
employeeControl.addEmployee(ID, firstName, lastName, email);
return ID;
}
/**
* @common:operation
*/
public int updateEmployee(int ID, String firstName, String lastName, String email)
{
employeeControl.updateEmployee(ID, firstName, lastName, email);
return ID;
}
}t'Q9s D TQj0 要使用getEmployees方法调用Web服务,需要使用这个URL:http://localhost:7001/Portal/WebServices/EmployeeListing /EmployeeListing.jws/getEmployees,所用的XML模式是:Linux宝库3AOMDnB0G
<xs:schema targetNamespace="demo"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="Employees">
<xs:annotation>
<xs:documentation>Employee Listing</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="Employee" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="id" type="xs:int"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>Linux宝库Qc-h3f"R1fnI调用这个Web服务所返回的XML如下:
8yo,Y7E/CB+LM*q2|0<?xml version="1.0" encoding="utf-8" ?>
<demo:Employees xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:demo="demo">
<demo:Employee>
<demo:id>1</demo:id>
<demo:firstName>John</demo:firstName>
<demo:lastName>Doe</demo:lastName>
<demo:email>jdoe@nowhere.com</demo:email>
</demo:Employee>
<demo:Employee>
<demo:id>2</demo:id>
<demo:firstName>Jane</demo:firstName>
<demo:lastName>Doe</demo:lastName>
<demo:email>jdoe2@nowhere.com</demo:email>
</demo:Employee>
</demo:Employees>Linux宝库XVs(|x/@7|}*k如果您试着在JavaScript/DOM代码中使用标准的getElementsByTagName()方法,将获得奇怪的结果。这是由于Internet Explorer和Firefox实现命名空间解析的方式不同。Firefox实现了一次单独的调用来解析命名空间,而Internet Explorer也使用同样的调用,但是带有的参数不同。下面给出了处理这个结果集的正确方式:
2l7W$U Cu9xn6_0var html = '<table class="flashy"><tr>';
html += '<th class="flashy">ID</th>';
html += '<th class="flashy">First Name</th>';
html += '<th class="flashy">Last Name</th>';
html += '<th class="flashy">Email</th>';
html += '</tr>';
var elem;
if(isMSIE)
elem = doc.getElementsByTagName("demo:Employee");
else
elem = doc.getElementsByTagNameNS("demo", "Employee");
var i = 0;
for(i = 0; i < elem.length; i++) {
html += '<tr>';
var ID, firstName, lastName, email;
if(isMSIE) {
ID = elem[i].getElementsByTagName("demo:id")[0].firstChild.data;
firstName = elem[i].getElementsByTagName("demo:firstName")[0].firstChild.data;
lastName = elem[i].getElementsByTagName("demo:lastName")[0].firstChild.data;
email = elem[i].getElementsByTagName("demo:email")[0].firstChild.data;
}
else {
ID = elem[i].getElementsByTagNameNS("demo","id")[0].firstChild.data;
firstName = elem[i].getElementsByTagNameNS("demo","firstName")[0].firstChild.data;
lastName = elem[i].getElementsByTagNameNS("demo","lastName")[0].firstChild.data;
email = elem[i].getElementsByTagNameNS("demo","email")[0].firstChild.data;
}
html += '<td class="flashy">' + ID + '</td>';
html += '<td class="flashy">' + firstName + '</td>';
html += '<td class="flashy">' + lastName + '</td>';
html += '<td class="flashy">' + email + '</td>';
html += '</tr>'
}
html += '</table>';
document.getElementById('employee_table').innerHTML = html;现在动态修改内容怎么样呢?我们需要添加用于添加、修改和删除记录的Ajax方法,使之成为一个真正有用的portlet。顺便说一句,这是一个重用性很强的模板。以这种方式,您可以对多种类型的可重复性数据项进行建模,从而减少用户敲击键盘和页面刷新的次数。Linux宝库QZ3T)ss ~u
Linux宝库xJ.BGxUUW-T您可能注意到了,在本节开始部分的屏幕快照上,有一行是以黄色显示的。这是因为,我为此portlet添加了一项新功能,使用黄色显示数据尚未提交给数据库的行。实际上,一行显示为黄色的时间还不到一秒钟,但是如果它长时间保持黄色,用户就会知道系统速度很慢或者干脆已经崩溃,那么这时或许有必要通知IT部门。
H x ] y h2a8|'|0#S;p7w\q(Q+x?+C/o N0 为了实现这项功能,我使用了JavaScript技术来修改一个元素的CSS样式,从而改变其外观。一开始,我为TD元素定义了两种CSS样式,flashy和flashy-new。第一种样式适用于表中的现有单元格。下面给出了样式定义:
RD6L Pb3^cJ;h0td.flashy {
background-color: #DDDDDD;
foreground: blue;
margin: 3mm;
padding-left: 2mm;
padding-right: 2mm;
border: none;
}Linux宝库"~-zg'EdvX t现在,我们需要使用另一种样式来高亮显示一行:
ULb,j0o$rw0td.flashy-new {
background-color: yellow;
foreground: blue;
margin: 3mm;
padding-left: 2mm;
padding-right: 2mm;
border: none;
}(hk?O0l!\$b0 当加入新的一行时,我使用了下面的代码来更新表和发送信息给Web服务:Linux宝库G.u)cXX7?
3bwR$Z:I0
G#k(t;[6@8_b%`i.{#qla)fG0function employee_onAdd() {
window.status = "onAdd() called";
// Get the values to add
var firstName = document.getElementById("employee_FN:new").value;
var lastName = document.getElementById("employee_LN:new").value;
var email = document.getElementById("employee_E:new").value;
// Call the Web service addEmployee method
var url = '/Portal/WebServices/EmployeeListing/EmployeeListing.jws/addEmployee?';
url += "firstName=" + Sarissa.escape(firstName) + "&";
url += "lastName=" + Sarissa.escape(lastName) + "&";
url += "email=" + Sarissa.escape(email);
var table, tbody, row;
try {
table = document.getElementById("employee_table");
row = document.createElement("TR");
row.setAttribute("id", "employee_TR:-1");
var cell, field;
field = createInput("text", firstName, "employee_FN:-1");
cell = createCell(field);
row.appendChild(cell);
field = createInput("text", lastName, "employee_LN:-1");
cell = createCell(field);
row.appendChild(cell);
field = createInput("text", email, "employee_E:-1");
cell = createCell(field);
row.appendChild(cell);
// Create the buttons
var modifyButton = createInput("button", "Modify", "employee_M:-1");
var deleteButton = createInput("button", "Delete", "employee_D:-1");
cell = document.createElement("TD");
if(isMSIE) {
cell.setAttribute("className", "flashy-new");
}
else {
cell.setAttribute("class", "flashy-new");
}
cell.appendChild(modifyButton);
cell.appendChild(document.createTextNode(" ")); // Need a little space between buttons.
cell.appendChild(deleteButton);
row.appendChild(cell);
tbody = document.getElementById("employee_tbody");
tbody.appendChild(row);
}
catch(ex) {
window.alert("Error: " + ex);
tbody.deleteChild(row);
}
try {
window.status = "Sending AJAX request to " + url;
employee_http.open("GET", url, true);
employee_http.onreadystatechange = employee_handleAddReturn;
employee_http.send(null);
}
catch(ex) {
window.alert("Could not send request");
}
employee_clearEntryFields();
}Bsh?EkQ#S0 注意,添加新行时,您将通过调用setAttribute来设置CSS样式。还要注意,在Internet Explorer和Firefox中,用于设置CSS类的调用是不同的。Internet Explorer调用的是CSS样式className,而Firefox调用的则是class。Linux宝库4r,^b(A7o6g/Ym
Linux宝库#?3U5W kzznL:D在这个例子中,还有用于在出现错误时删除新行的代码。在JavaScript中,您也可以使用try/catch处理程序,就像在Java中一样。记住,当使用JavaScript编码时,要使用最佳实践,就像使用Java一样。企业应用程序的良好程度取决于其最弱的一环——不要让您的Ajax代码中出现这样的一环。Linux宝库"Q'dtEI4Haq
Linux宝库(wI U.B C3E z&_ml.a这个portlet比前面任何例子都要复杂得多。我强烈建议您仔细研究本文中所包含的源代码,以便更好地了解DOM/CSS编程是怎么一回事。Linux宝库DNjI(i7h
Page Loader Portlet
Linux宝库;oG5A#g:ZY;]AMB现在,我们将要讨论的是一个容易而且有趣的portlet(Page Loader,页面加载器),您可以把它作为另一个Web页面的输入,并把它加载到<div>元素中。假如内容管理系统是插入到WebLogic Portal中的,那么这个portlet不是特别有用。
;x ~f6UA m0Linux宝库'C[wD#o注意,某些Web站点不能正确加载图片。目标Web页面无法使用指向资源的相关链接,使之正确工作。站点仍然会进行加载,但是图片将会被图片占位符和ALT文本所代替。通过采用从Web服务调用返回的结果并给所有相关链接添加基址引用,就可以解决这个问题。无疑,这是留给读者的又一道练习题。Linux宝库FWq@e@8U6U/z2J
Linux宝库\)PG'g?r3w为了使页面变得更加美观,并说明如何使用从XmlHttpObject异步返回的状态码,我们在页面右上方加上了一个小圆点,用于说明页面加载时的状态:灰色代表没有连接,绿色代表已连接/正在处理,而红色代表出现了错误。当页面完全加载时,该圆点将变回灰色。请参见图4。Linux宝库m ` o+B{

*sO(GwQ
A\q7Z%E0图 4: Page loader portlet使用Proxy servlet来加载任意URL
我们将使用ProxyServlet来获取被请求的页面。这样使用Ajax有些奇怪,但是它说明了涉及到Web时,所有内容都是服务!毕竟,Web页面本质上是HTML,而HTML是XML的一个子集:Linux宝库`7U#dn.d;L
function pageloader_update() {
// Change the stoplight so that the user knows what is going on
document.images.stoplight.src = "/Portal/resources/images/greenlight.gif";
// This is the input parameter for the page to load
var page = document.getElementById("page_to_load").value;
// This is the URL that the user entered
var endurl = escape(page);
// This is the endpoint of the proxy servlet that will make the call on our behalf
var localurl = "/Portal/ProxyServlet";
// This is the actual URL that will be called using the XmlHttpRequest object
var url = localurl + "?url=" + endurl; // The server-side script
// Open a url connection using the XMLHttpObject. The third parameter specifies that the
// call should be made asynchronously. Set this to false to make this call synchronous.
pageloader_http.open("GET", url, true);
// Set a callback handler to a local JavaScript method
pageloader_http.onreadystatechange = pageloader_handleHttpResponse;
// Make the call. You can replace the null value with XML request data if you are doing
// a SOAP-style call instead of using HTTP request parameters.
pageloader_http.send(null);
}8g3r}"gO}7O0 到现在为止,这些内容应该都是您很熟悉的。用于处理返回的页面的代码很简单:Linux宝库-^"oq(D3l q\w
function pageloader_handleHttpResponse() {
if (pageloader_http.readyState == 4) {
if(pageloader_http.status == 200) {
var doc = Sarissa.getDomDocument();
doc = (new DOMParser()).parseFromString(pageloader_http.responseText, "text/xml");
document.getElementById('pageloader_html').innerHTML = pageloader_http.responseText;
// Change the stoplight so that the user knows what is going on
document.images.stoplight.src = "/Portal/resources/images/graylight.gif";
}
else {
document.getElementById('pageloader_html').innerHTML = pageloader_http.responseText;
document.images.stoplight.src = "/Portal/resources/images/redlight.gif";
}
}
else {
// Change the stoplight so that the user knows what is going on
document.images.stoplight.src = "/Portal/resources/images/greenlight.gif";
document.getElementById('pageloader_html').innerHTML = "Loading...";
}
}w9B:{!BZ O.B&V.u0 只要把来自XmlHttpObject的responseText映射为想要用作浏览器部件的<div>元素的innerHTML即可。
$PCD'u1DT~0eU6fyk$zb a4oSA0 注意,默认情况下,portlet将会调整大小以适应返回的整个Web页面。通过使用某些样式,可以使<div>标签看起来更像是一个窗口:Linux宝库)|.^BX8o
<div id="pageloader_html"
style="border: thin gold dashed;
height: 450px;
overflow-x: hidden;
overflow-y: auto;
width: 98%;
padding: 5px">
<p>This is where the Web page will be displayed</p>
</div>下载
w&[0ZtRJ0 ajax_demo.zipLinux宝库8Q K)I8M!B]\ k?
结束语
Linux宝库~/m&Ij6DGl'MQ用户需要响应灵敏度更高、内容更加丰富的Web应用程序。人们可以接受的性能指标每天都在变化。Ajax编程可以帮助开发人员在程序与用户的交互方面实现更大的灵活性。通过使用标准技术和一些最佳实践,Ajax可以帮助您解决很多让您头痛的难题,让您可以开始创建高度交互式的、用户驱动的Web站点,从而在竞争中立于不败之地。
sQ]i*L.]@3zK/R k0+u%y)V/|;{XJ0 随着Ajax获得的关注越来越多,我们正在期望出现大量优秀的开源和专用工具,让Ajax编程变得更加轻松。BEA正在把Ajax集成到它即将推出的门户产品中,而且很多公司已经发行了功能全面的Ajax开发环境,比如JackBe。
)_Os c[$M*q(e0参考资料
- JSR 168(JCP)——Java Portlet规范
- An Introduction To Ajax,作者David Teare(中文版,Dev2Dev,2005年11月)
- A Backbase Ajax Front-end for J2EE Applications,作者Mark Schiefelbein(中文版,Dev2Dev,2005年11月)
- Developing Ajax Applications That Preserve Standard Browser Functionality,作者Mark Schiefelbein (Dev2Dev, 2006年1月)
- Google Maps——Ajax编程的一个优秀例子
- Dev2DevWebLogic Portal产品中心
- Ajax Programming in BEA WebLogic Portal, Part 1,作者John Margaglione(中文版,Dev2Dev,2006年4月)
TAG: 电脑网络 weblogic portal Bea ajax
