??xml version="1.0" encoding="utf-8" standalone="yes"?>
在开始我们的正式讨论之前Q让我们单看一?REST 的定义?/p>
RESTQRepresentational State TransferQ是 Roy Fielding
提出的一个描qC联系l架构风格的名词。ؓ什么称?RESTQWeb 本质上由各种各样的资源组成,资源?URI
唯一标识。浏览器Q或者Q何其它类g览器的应用E序Q将展示资源的一U表现方式,或者一U表现状态。如果用户在该页面中定向到指向其它资源的?
接,则将讉K该资源,q表现出它的状态。这意味着客户端应用程序随着每个资源表现状态的不同而发生状态{U,也即所?REST?/p>
关于 REST 本nQ本文就不再q里q多地讨论,读者可以参?developerWorks 上其它介l?REST 的文章。本文的重点在于通过 REST ?SOAP Web 服务的对比,帮助读者更深刻理解 REST 架构风格的特点,优势?
本文借助于一个应用场景,通过Z REST ?SOAP Web 服务的不同实玎ͼ来对两者进行对比。该应用场景的业务逻辑会尽量保持简单且易于理解Q以有助于把我们的重心放?REST ?SOAP Web 服务技术特质对比上?
q是一个在U的用户理模块Q负责用户信息的创徏Q修改,删除Q查询。用L信息主要包括Q?
需求用例图如下Q?
如图 1 所C,客户?1QClient1Q与客户?2QClient2Q对于信息的存取h不同的权限,客户?1
可以执行所有的操作Q而客L 2 只被允许执行用户查询QQuery UserQ与用户列表查询QQuery User
ListQ。关于这一点,我们在对 REST Web 服务?SOAP Web 服务安全控制Ҏ时会具体谈到。下面我们将分别向您介绍如何使用
REST ?SOAP 架构实现 Web 服务?/p>
本部分将Z Restlet 框架来实现该应用。Restlet 为那些要采用 REST l构体系来构建应用程序的 Java 开发者提供了一个具体的解决Ҏ。关于更多的 Restlet 相关内容Q本文不做深入讨论,误参考资源列表?/p>
我们采用遵?REST 设计原则?ROAQResource-Oriented
ArchitectureQ面向资源的体系架构Q进行设计。ROA 是什么?单点_ROA 是一U把实际问题转换?REST ?Web
服务的方法,它?URI、HTTP ?XML h跟其?Web 应用一L工作方式?/p>
在?ROA q行设计Ӟ我们需要把真实的应用需求{化成 ROA 中的资源Q基本上遵@以下的步骤: 接下来我们按照以上的步骤来设计本文的应用案例?/p>
在线用户理所涉及的数据集是用户信息Q如果映到 ROA 资源Q主要包括两c资源:用户及用户列表。用戯源的 URI ? 客户端通过 User List Resource 提供?LINK 信息 ( ?: 首先l出 Web 服务使用 REST 风格实现的整体架构图Q如下图所C: 接下来,我们基于该架构Q?Restlet l出应用?RESTful Web 服务实现?/p>
下面的章节中Q我们将l出 REST Web 服务实现的核心代码片Dc关于完整的代码清单Q读者可以通过资源列表下蝲?/p>
清单 3 l出的是客户端的核心实现部分Q其主要由四部分l成Q?HTTP PUT 增加、修改用戯源,使用 HTTP GET
得到某一具体用户资源Q?HTTP DELETE 删除用户资源Q?HTTP GET 得到用户列表资源。而这四部分也正对应了?2
关于架构描述的四?HTTP 消息来回。关?UserRestHelper cȝ完整实现Q请读者参见本文所附的代码CZ?/p>
清单 4 l出的是服务器端对于用户资源c(UserResourcQ的实现Q其核心的功能是响应有关用户资源?HTTP GET/PUT/DELETE hQ而这些请求响应逻辑正对应了 UserRestHelper cM关于用户资源cȝ HTTP h?/p>
UserResource cL对用戯源类的抽象,包括了对该资源的创徏修改Qput ҎQ,dQhandleGet Ҏ
Q和删除Qdelete ҎQ,被创建出来的 UserResource cd例被 Restlet 框架所托管Q所有操U资源的Ҏ会在相应?
HTTP h到达后被自动回调?/p>
另外Q在服务端,q需要实C表用户列表资源的资源c?UserListResourceQ它的实C UserResource
cMQ响?HTTP GET hQ读取当前系l内的所有用户信息,形成如清?1 所C的用户列表资源
RepresentationQ然后返回该l果l客L。具体的实现误者参见本文所附的代码CZ? 本文对于 SOAP 实现Q就不再?REST 那样Q具体到代码U别的实现。本节将主要通过 URI,HTTP ?XML 来宏观上表述 SOAP Web 服务实现的技术本质,Z一?REST Web 服务?SOAP Web 服务的对比做铺垫?/p>
同样Q首先给?SOAP 实现的整体架构图Q如下图所C: 可以看到Q与 REST 架构相比QSOAP 架构图明显不同的是:所有的 SOAP 消息发送都使用 HTTP POST ҎQƈ且所?SOAP 消息?URI 都是一LQ这是基?SOAP ?Web 服务的基本实늉征?/p>
Z SOAP 的客L创徏如清?5 所C的 SOAP XML 文Q它通过c?RPC 方式来获得用户列表信息?/p>
客户端将使用 HTTP ?POST ҎQ将上述?SOAP 消息发送至 同样圎ͼ客户端将使用 HTTP ?POST ҎQ将上述?SOAP 消息发送至 实际上,创徏新的用户Q过E也比较cMQ在q里Q就不一一列出Q因两个例子对于本文在选定的点上对?REST ?SOAP 已经_了?/p>
本节从以下几个方面来Ҏ上面两节l出 REST 实现?SOAP 实现?/p>
RESTful Web 服务使用标准?HTTP Ҏ (GET/PUT/POST/DELETE) 来抽象所?Web
pȝ的服务能力,而不同的是,SOAP 应用都通过定义自己个性化的接口方法来抽象 Web 服务Q这更像我们l常谈到?RPC。例如本例中?
getUserList ?getUserByName Ҏ?/p>
RESTful Web 服务使用标准?HTTP Ҏ优势Q从大的斚w来讲Q标准化?HTTP 操作ҎQ结合其他的标准化技术,?
URIQHTMLQXML {,会极大提高pȝ与系l之间整合的互操作能力。尤其在 Web 应用领域QRESTful Web
服务所表达的这U抽象能力更加脓q?Web 本n的工作方式,也更加自然?/p>
同时Q用标?HTTP Ҏ实现?RRESTful Web 服务也带来了 HTTP Ҏ本n的一些优势: HTTP 协议从本质上说是一U无状态的协议Q客L发出?HTTP h之间可以怺隔离Q不存在怺的状态依赖。基?HTTP ?
ROAQ以非常自然的方式来实现无状态服务请求处理逻辑。对于分布式的应用而言QQ意给定的两个服务h Request 1 ?Request 2,
׃它们之间q没有相互之间的状态依赖,׃需要对它们q行怺协作处理Q其l果是:Request 1 ?Request 2
可以在Q何的服务器上执行Q这L应用很容易在服务器端支持负蝲q (load-balance)?/p>
HTTP ?GET、HEAD h本质上应该是安全的调用,卻IGET、HEAD
调用不会有Q何的副作用,不会造成服务器端状态的改变。对于服务器来说Q客LҎ一 URI ?n ơ的 GET、HAED
调用Q其状态与没有做调用是一LQ不会发生Q何的改变?/p>
HTTP ?PUT、DELTE 调用Q具有幂指相{特?, 卻I客户端对某一 URI ?n ơ的 PUT、DELTE 调用Q其效果与做一ơ的调用是一L。HTTP ?GET、HEAD Ҏ也具有幂指相{特性?/p>
HTTP q些标准Ҏ在原则上保证你的分布式系l具有这些特性,以帮助构建更加健壮的分布式系l?/p>
Z说明问题Q基于上面的在线用户理pȝQ我们给定以下场景: 参考一开始我们给出的用例图,对于客户?Client2Q我们只希望它能以只ȝ方式讉K User ?User List 资源Q?Client1 h讉K所有资源的所有权限?/p>
如何做这L安全控制Q?/p>
通行的做法是Q所有从客户?Client2 发出?HTTP h都经q代理服务器 (Proxy
Server)。代理服务器制定安全{略Q所有经q该代理的访?User ?User List 资源的请求只hd权限Q即Q允?
GET/HEAD 操作Q而像h写权限的 PUT/DELTE 是不被允许的?/p>
如果对于 RESTQ我们看看这L安全{略是如何部|的。如下图所C: 一般代理服务器的实现根?(URI, HTTP Method) 两元l来军_ HTTP h的安全合法性?/p>
当发现类gQhttp://localhost:8182/v1/users/{username}QDELETEQ这LhӞ予以拒绝?/p>
对于 SOAPQ如果我们想借助于既有的代理服务器进行安全控Ӟ会比较尴,如下图: 所有的 SOAP 消息l过代理服务器,只能看到Q?code>http://localhost:8182/v1/soap/servlet/messagerouter,
HTTP POSTQ这L信息Q如果代理服务器想知道当前的 HTTP h具体做的是什么,必须?SOAP
的消息体解码Q这L话,意味着要求W三方的代理服务器需要理解当前的 SOAP 消息语义Q而这U?SOAP
应用与代理服务器之间的紧耦合关系是不合理的?/p>
众所周知Q对于基于网l的分布式应用,|络传输是一个媄响应用性能的重要因素。如何用缓存来节省|络传输带来的开销Q这是每一个构建分布式|络应用的开发h员必考虑的问题?/p>
HTTP 协议带条件的 HTTP GET h (Conditional GET)
被设计用来节省客L与服务器之间|络传输带来的开销Q这也给客户端实?Cache 机制 ( 包括在客L与服务器之间的Q何代?)
提供了可能。HTTP 协议通过 HTTP HEADER 域:If-Modified-Since/Last-
ModifiedQIf-None-Match/ETag 实现带条件的 GET h?/p>
REST 的应用可以充分地挖掘 HTTP 协议对缓存支持的能力。当客户端第一ơ发?HTTP GET
hl服务器获得内容后,该内容可能被~存服务?(Cache Server)
~存。当下一ơ客Lh同样的资源时Q缓存可以直接给出响应,而不需要请求远E的服务器获得。而这一切对客户端来说都是透明的?/p>
而对?SOAPQ情况又是怎样的呢Q?/p>
使用 HTTP 协议?SOAPQ由于其设计原则上ƈ不像 REST 那样?Web 的工作方式相一_所以,Z SOAP 应用很难充分发挥 HTTP 本n的缓存能力?/p>
两个因素军_了基?SOAP 应用的缓存机制要q比 REST 复杂Q?/p>
其一、所有经q缓存服务器?SOAP 消息L HTTP POSTQ缓存服务器如果不解?SOAP 消息体,没法知道?HTTP h是否是想从服务器获得数据?/p>
其二、SOAP 消息所使用?URI L指向 SOAP 的服务器Q如本文例子中的 在一个纯?SOAP 应用中,URI 本质上除了用来指C?SOAP 服务器外Q本w没有Q何意义。与 REST 的不同的是,无法通过 URI 驱动 SOAP Ҏ调用。例如在我们的例子中Q当我们通过 getUserList SOAP 消息获得所有的用户列表后,仍然无法通过既有的信息得到某个具体的用户信息。唯一的方法只有通过
WSDL 的指C,通过调用 getUserByName 获得QgetUserList ?getUserByName 是彼此孤立的?/p>
而对?RESTQ情冉|完全不同的:通过 典型的基?SOAP ?Web 服务以操作ؓ中心Q每个操作接?XML 文作ؓ输入Q提?XML
文作ؓ输出。在本质上讲Q它们是 RPC 风格的。而在遵@ REST 原则?ROA 应用中,服务是以资源Z心的Q对每个资源的操作都是标准化?
HTTP Ҏ?/p>
本文主要集中在以上的几个斚wQ对 SOAP ?REST q行了对比,可以看到Q基?REST 构徏的系l其pȝ的扩展能力要Z
SOAPQ这可以体现在它的统一接口抽象、代理服务器支持、缓存服务器支持{诸多方面。ƈ且,伴随着 Web Site as Web Services
演进的趋势,Z REST 设计和实现的单性和强扩展性,有理q信,REST 会成ؓ Web 服务的一个重要架构实践领域?/p>
即用戯?nbsp;A |站时所产生的对 B |站的跨域访问请求均提交?nbsp;A |站的指定页面,p面代替用户面完成交互Q从而返回合适的l果。此Ҏ可以解决现阶D|能够惛_的多数跨域访问问题,但要?nbsp;A |站提供 Web 代理的支持,因此A|站?nbsp;B |站之间必须是紧密协作的Q且每次交互q程QA |站的服务器负担增加Q且无法代用户保?nbsp;Session 状态?br />
Case II. on-Demand方式 (on Server A)
MYMSN 的门户就用的q种方式Q不q?nbsp;MYMSN 中不涉及跨域讉K问题。在面内动态生成新?nbsp;<script>Q将?nbsp;src 属性指向别的网站的|址Q这个网址q回的内容必L合法?nbsp;Javascript 脚本Q常用的?nbsp;JSON 消息。此Ҏ存在的缺hQscript ?nbsp;src 属性完成该调用旉取的方式?nbsp;get 方式Q如果请求时传递的字符串过大时Q可能会无法正常q行。不q此Ҏ非常适合聚合c门户用?br />
<html>
<head>
<script language="javascript" type="text/javascript">
function loadContent()
{
var s=document.createElement('script');
s.src='http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent';
document.body.appendChild(s);
}
function setDivContent(v)
{
var dv = document.getElementById("dv");
dv.innerHTML = v;
}
</script>
<div id=dv></div>
<input onclick=loadContent() type=button value="Click Me">
其中?nbsp;www.anotherdomain.com/TestCrossJS.aspx 是这LQ?br />
<script language="C#" runat="server">
void Page_Load(object sender, EventArgs e)
{
string f = Request.QueryString["f"];
Response.Clear();
Response.ContentType = "application/x-javascript";
Response.Write(String.Format(@"
{0}('{1}');",
f,
DateTime.Now));
Response.End();
}
</script>
点击“Click Me”按钮Q生成一个新?nbsp;script tagQ下载对应的 Javascript 脚本Q结束时回调其中?nbsp;setDivContent()Q从而更新网上一?nbsp;div 的内宏V?br />
~者注Q如?nbsp;Ajax ?nbsp;js 内容?nbsp;B 提供Q则 A 可以利用 B 提供?nbsp;js 方便地访?nbsp;B 的资源。比?nbsp;A 中的 js 代码Q?br />
<script type="text/javascript" src="B/core.js"></script>
Case III. iframe 方式 (on Server A)
查看q醒来在 javaeye 上的一关于跨域访问的帖子Q他提到自己已经?nbsp;iframe 的方式解决了跨域讉K问题。数据提交跟获取Q采用iframeq种方式的确可以了,但由于父H口与子H口之间不能交互Q跨域访问的情况下,q种交互被拒l)Q因此无法完成对父窗口效果的影响?br />
在页面内嵌或动态生成指向别的网站的 IFRAMEQ然后这 2 个网间可以通过改变Ҏ?nbsp;anchor hash fragment 来传输消息。改变一个网늚 anchor hash fragment q不会ɋ览器重新装载网,所以一个网늚状态得以保持,而网|w则可以通过一个计时器(timer)来察觉自?nbsp;anchor hash 的变化,从而相应改变自q状态?br />
1. http://domain1/TestCross.html:
<html>
<head>
<script language="javascript" type="text/javascript">
var url = "http://domain2/TestCross.html"
var oldHash = null;
var timer = null;
function getHash()
{
var hash = window.location.hash;
if ((hash.length >= 1) && (hash.charAt(0) == '#'))
{
hash = hash.substring(1);
}
return hash;
}
function sendRequest()
{
var d = document;
var t = d.getElementById('request');
var f = d.getElementById('alienFrame');
f.src = url + "#" + t.value + "<br/>" + new Date();
}
function setDivHtml(v)
{
var d = document;
var dv = d.getElementById('response');
dv.innerHTML = v;
}
function idle()
{
var newHash = getHash();
if (newHash != oldHash)
{
setDivHtml(newHash);
oldHash = newHash;
}
timer = window.setTimeout(idle, 100);
}
function window.onload()
{
timer = window.setTimeout(idle, 100);
}
</script>
</head>
<body>
hQ?/span><input type="text" id="request">
<input type="button" value="发? onclick="sendRequest()" /><br/>
回复Q?/span><div id="response"></div>
<iframe id="alienFrame" src="http://domain2/TestCross.html"></iframe>
</body>
</html>
2. http://domain2/TestCross.html:
<html>
<head>
<script language="javascript" type="text/javascript">
var url = "http://domain1/TestCross.html"
var oldHash = null;
var timer = null;
function getHash()
{
var hash = window.location.hash;
if ((hash.length >= 1) && (hash.charAt(0) == '#'))
{
hash = hash.substring(1);
}
return hash;
}
function sendRequest()
{
var d = document;
var t = d.getElementById('request');
var f = parent;
//alert(f.document); //试着Lq个注释Q你会得?#8220;Access is denied”
f.location.href = url + "#" + t.value + "<br/>" + new Date();
}
function setDivHtml(v)
{
var d = document;
var dv = d.getElementById('response');
dv.innerHTML = v;
}
function idle()
{
var newHash = getHash();
if (newHash != oldHash)
{
setDivHtml(newHash);
oldHash = newHash;
}
timer = window.setTimeout(idle, 100);
}
function window.onload()
{
timer = window.setTimeout(idle, 100);
}
</script>
</head>
<body>
hQ?/span><input type="text" id="request">
<input type="button" value="发? onclick="sendRequest()" /><br/>
回复Q?/span><div id="response"></div>
</body>
</html>
两个|页基本相同Q第一个网内嵌一?nbsp;IFRAMEQ在点击“发?#8221;按钮后,会将文本框里的内定w过 hash fragment 传给 IFRAME。点?nbsp;IFRAME 里的“发?#8221;按钮后,它会文本框里的内容通过 hash fragment 传给父窗口。因为是只改动了 hash fragmentQ浏览器不会重新 load |页内容Q这里用了一个计时器来检?nbsp;URL 变化Q如果变化了Q就更新其中一?nbsp;div 的内?nbsp;?br />
Case IV. 用户本地转储方式 (local)
IE 本n依附?nbsp;Windows q_的特性ؓ我们提供了一U基?nbsp;iframeQ利用内存来“l行”的方案,即两?nbsp;Window 之间可以在客L通过 Windows 剪脓板的方式q行数据传输Q只需要在接受数据的一方设|?nbsp;Interval q行轮询Q获得结果后清除 Interval 卛_。FF 的^台独立性决定了它不支持剪脓板这U方式,而以往版本?nbsp;FF 中存在的插g漏洞又被 fixed 了,所?nbsp;FF 无法通过内存来完成暗渡陈仓。而由于文件操?nbsp;FF 也没有提供支持(无法通过 Cookie 跨域完成数据传递)Q致使这U技巧性的方式只能?nbsp;IE 中用?br />
Case V: Q其实还是在服务?nbsp;A ?nbsp;iframe 解决了与服务?nbsp;B 通信的问题)
要解决的问题Q发生在用户提交|页 URLQ还包括 Tag, Notes {)l?nbsp;Bookmark 服务器时?br />
关于 URL 的提交至可以有三种方式Q?nbsp;
1. 登陆 Bookmark 服务器的提交面Q将要收藏的 URL 通过该页面提交给服务器?br />
2. 安装览器插Ӟ通过插g?nbsp;URL 提交l服务器?br />
3. ?nbsp;Bookmark 服务器动态加?nbsp;javascript 工具到当前面Q通过它来完成提交工作?br />
W一U方式开发v来最单,但对用户来讲比较ȝQ每ơ都需要先登陆 Bookmark 服务器才能完成提交;W二U方式我q不熟悉插g开发,而且用户也不喜欢太多的插件堆满自q览器;W三U方式开发难度小Q又避免了每ơ登陆服务器的麻烦,所以最l采用它。第三种方式中动态加载的 javascript 工具除了需要生?nbsp;UI 供用户填写信息(URLQtagQnotes {)Q当用户点击提交的时候,q要完成与服务器通信的功能?br />
跨域讉KQ简单来说就?nbsp;A |站?nbsp;javascript 代码试图讉K B |站Q包括提交内容和获取内容。由于安全原因,跨域讉K是被各大览器所默认止的。写q跨域访?nbsp;ajax 的朋友相信都遇到q被告知“没有权限”的情c通过 XMLHttp 来发送数据给 Bookmark 服务器的试p|了。于是,看到|上的一些资料,我又开始尝试用 javascript 工具在用户|页动态创Z个隐藏的 iframe, iframe ?nbsp;src 指向服务器的一?nbsp;servlet Q试N过调用 iframe 中提供的 javascript 来完成与服务器的通信。但不幸的是Q用L中?nbsp;javascript 代码讉K iframe 也被览器归域访问(Ҏ iframe ?nbsp;src 指向其它|站的情形)Q尝试再ơ失败?br />
最l,在一文章中看到Q与 iframe 不同Q如?nbsp;A |站?nbsp;B |站加蝲 javascript Q?nbsp;A |站可以自由的访问该 javascript 的内容,q不会被览器认为是跨域讉K。模仿刚?nbsp;iframe 的思\Q当用户点击提交Ӟ可以动态创Z?nbsp;javascript 对象Q该对象?nbsp;src 指向 Bookmark 服务器的一?nbsp;servletQ注意:URL、Tag、Notes、User、Password {信息被作ؓ src URL 参数传给服务器。请看下面的代码Q?br />
var url = "http://localhost:8080/Deeryard/BookmarkServlet?" +
"url=" + url_source + "&" + "title=" + title + "&" +
"tag=" + tag + "&" + "notes=" + notes + "&" +
"user=" + user + "&" + "password=" + password;
url = encodeURI(url);
//Submit to server with a trick
var js_obj = document.createElement( "script" );
js_obj.type = "text/javascript" ;
js_obj.setAttribute( "src" , url);
//Get response from server by appending it to document
document.body.appendChild(js_obj);
上面例子中, js_obj.setArrribute() 信息作?nbsp;src ?nbsp;URL 参数提交l了 Bookmark servlet 。那么用户又如何取得服务器的响应信息呢?{案是最末一行代码, servlet 的输出必L javascript 代码Q它可以调用用户|页上的其他 javascript 函数Q以及操?nbsp;dom 对象。下面的 servlet 代码生成了一?nbsp;javascript 函数调用Q?br />
out.write("onServerResponse(INADEQUATE_INFORMATION);");
document.body.appendChild(js_obj) 执行?nbsp;onServerResponse( INADEQUATE_INFORMATION) ׃得到执行Q客户|页响应服务器结果。这样一个完整的通信q程完成了?br />
CaseVI:Tomcat + PHP + HTML(含JS)(on Server A)
服务?nbsp;A 上已l装好了 Tomcat, 我们写一?nbsp;test.html(含JS)Q再写一?nbsp;PHP 文gQ由其来完成跨域通信要求Q?br />
更多Q请参考:
* https://www6.software.ibm.com/developerworks/cn/education/xml/x-ajaxtrans/index.html
* http://www.xyhhxx.com/news/net/20061013121041.htm
相关阅读
* http://www-128.ibm.com/developerworks/library/x-securemashups/
]]>
]]>
回页?/strong>
?1. 需求用例图
回页?/strong>
http://localhost:8182/v1/users/{username}
表示Q用户列表资源的 URI ?http://localhost:8182/v1/users
表示。它们的 Representation 如下Q它们都采用了如清单 1 和清?2 所C的 XML 表述方式?/p>
清单 1. 用户列表资源 Representation
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<users>
<user>
<name>tester</name>
<link>http://localhost:8182/v1/users/tester</link>
</user>
<user>
<name>tester1</name>
<link>http://localhost:8182/v1/users/tester1</link>
</user>
</users>
清单 2. 用户资源 Representation
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<user>
<name>tester</name>
<title>software engineer</title>
<company>IBM</company>
<email>tester@cn.ibm.com</email>
<description>testing!</description>
</user>
<link>http://localhost:8182/v1/users/tester</link>
) 获得具体的某?USER Resource?/p>
?2. REST 实现架构
清单 3. 客户端实?/strong>
public class UserRestHelper {
//The root URI of our ROA implementation.
public static final tring APPLICATION_URI = "http://localhost:8182/v1";
//Get the URI of user resource by user name.
private static String getUserUri(String name) {
return APPLICATION_URI + "/users/" + name;
}
//Get the URI of user list resource.
private static String getUsersUri() {
return APPLICATION_URI + "/users";
}
//Delete user resource from server by user name.
//使用 HTTP DELETE Ҏl由 URI 删除用户资源
public static void deleteFromServer(String name) {
Response response = new Client(Protocol.HTTP).delete(getUserUri(name));
……
}
//Put user resource to server.
//使用 HTTP PUT Ҏl由 URI 增加或者修改用戯?br />
public static void putToServer(User user) {
//Fill FORM using user data.
Form form = new Form();
form.add("user[title]", user.getTitle());
form.add("user[company]", user.getCompany());
form.add("user[email]", user.getEmail());
form.add("user[description]", user.getDescription());
Response putResponse = new Client(Protocol.HTTP).put(
getUserUri(user.getName()), form.getWebRepresentation());
……
}
//Output user resource to console.
public static void printUser(String name) {
printUserByURI(getUserUri(name));
}
//Output user list resource to console.
//使用 HTTP GET Ҏl由 URI 昄用户列表资源
public static void printUserList() {
Response getResponse = new Client(Protocol.HTTP).get(getUsersUri());
if (getResponse.getStatus().isSuccess()) {
DomRepresentation result = getResponse.getEntityAsDom();
//The following code line will explore this XML document and output
//each user resource to console.
……
} else {
System.out.println("Unexpected status:"+ getResponse.getStatus());
}
}
//Output user resource to console.
//使用 HTTP GET Ҏl由 URI 昄用户资源
private static void printUserByURI(String uri) {
Response getResponse = new Client(Protocol.HTTP).get(uri);
if (getResponse.getStatus().isSuccess()) {
DomRepresentation result = getResponse.getEntityAsDom();
//The following code line will explore this XML document and output
//current user resource to console.
……
} else {
System.out.println("unexpected status:"+ getResponse.getStatus());
}
}
}
清单 4. 服务器端实现
public class UserResource extends Resource {
private User _user;
private String _userName;
public UserResource(Context context, Request request, Response response) {
//Constructor is here.
……
}
//响应 HTTP DELETE h逻辑
public void delete() {
// Remove the user from container.
getContainer().remove(_userName);
getResponse().setStatus(Status.SUCCESS_OK);
}
//This method will be called by handleGet.
public Representation getRepresentation(Variant variant) {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_XML)) {
Document doc = createDocument(this._user);
result = new DomRepresentation(MediaType.TEXT_XML, doc);
}
return result;
}
//响应 HTTP PUT h逻辑?br />
public void put(Representation entity) {
if (getUser() == null) {
//The user doesn't exist, create it
setUser(new User());
getUser().setName(this._userName);
getResponse().setStatus(Status.SUCCESS_CREATED);
} else {
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
}
//Parse the entity as a Web form.
Form form = new Form(entity);
getUser().setTitle(form.getFirstValue("user[title]"));
getUser().setCompany(form.getFirstValue("user[company]"));
getUser().setEmail(form.getFirstValue("user[email]"));
getUser().setDescription(form.getFirstValue("user[description]"));
//Put the user to the container.
getApplication().getContainer().put(_userName, getUser());
}
//响应 HTTP GET h逻辑?br />
public void handleGet() {
super.handleGet();
if(this._user != null ) {
getResponse().setEntity(getRepresentation(
new Variant(MediaType.TEXT_XML)));
getResponse().setStatus(Status.SUCCESS_OK);
} else {
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
}
}
//build XML document for user resource.
private Document createDocument(User user) {
//The following code line will create XML document according to user info.
……
}
//The remaining methods here
……
}
回页?/strong>
?3. SOAP 实现架构
清单 5. getUserList SOAP 消息
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<p:getUserList xmlns:p="http://www.exmaple.com"/>
</soap:Body>
</soap:Envelope>
http://localhost:8182/v1/soap/servlet/messagerouter
URIQSOAP SERVER 收到?HTTP POST hQ通过解码 SOAP 消息定需要调?getUserList Ҏ完成?WEB 服务调用Q返回如下的响应Q?/p>
清单 6. getUserListResponse 消息
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<p:get
UserListResponse xmlns:p="http://www.exmaple.com">
<Users>
<username>tester<username>
<username>tester1<username>
......
</Users>
<p: getUserListResponse >
</soap:Body>
</soap:Envelope>
清单 7. getUserByName SOAP 消息
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<p:getUserByName xmlns:p="http://www.exmaple.com">
<username>tester</username>
</p:getUserByName >
</soap:Body>
</soap:Envelope>
http://localhost:8182/v1/soap/servlet/messagerouter
URIQSOAP SERVER 处理后返回的 Response 如下Q?/p>
清单 8. getUserByNameResponse SOAP 消息
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<p:getUserByNameResponse xmlns:p="http://www.exmaple.com">
<name>tester</name>
<title>software engineer</title>
<company>IBM</company>
<email>tester@cn.ibm.com</email>
<description>testing!</description>
</p:getUserByNameResponse>
</soap:Body>
</soap:Envelope>
回页?/strong>
?4. REST 与代理服务器 (Proxy Servers)
?5. SOAP 与代理服务器 (Proxy Servers)
?6. REST 与缓存服务器 (Cache Server)
?7. SOAP 与缓存服务器 (Cache Server)
http://localhost:8182/v1/soap/servlet/messagerouter
Q这q没有表辄实的资源 URIQ其l果是缓存服务器Ҏ不知道那个资源正在被hQ更不用谈进行缓存处理?/p>
http://localhost:8182/v1/users
URI 获得用户列表Q然后再通过用户列表中所提供?LINK 属性,例如 <link>http://localhost:8182/v1/users/tester</link>
获得 tester 用户的用户信息。这L工作方式Q非常类g你在览器的某个面上点L?hyperlink, 览器帮你自动定向到你想讉K的页面,q不依赖MW三方的信息?/p>
回页?/strong>
回页?/strong>
描述 名字 大小 下蝲Ҏ
本文代码CZ
code_rest.zip
10 KB
HTTP
关于下蝲Ҏ的信?/a>
]]>
这些代码保存在check.js的文件中
function checkIdcard(idcard){
var Errors=new Array(
"w䆾证验证通过!",
"w䆾证号码位C?",
"w䆾证号码出生日期超围或含有非法字符!",
"w䆾证号码校验错?",
"w䆾证地区非?"
);
var area={11:"北京",12:"天|",13:"沛_",14:"p",15:"内蒙?,21:"辽宁",22:"吉林",23:"黑龙?,31:"上v",32:"江苏",33:"江",34:"安徽",35:"徏",36:"江西",37:"׃",41:"沛_",42:"湖北",43:"湖南",44:"q东",45:"q西",46:"南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏",61:"陕西",62:"甘肃",63:"青v",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"}
var idcard,Y,JYM;
var S,M;
var idcard_array = new Array();
idcard_array = idcard.split("");
//地区?br />
if(area[parseInt(idcard.substr(0,2))]==null) return Errors[4];
//w䆾L位数及格式检?br />
switch(idcard.length){
case 15:
if ( (parseInt(idcard.substr(6,2))+1900) % 4 == 0 || ((parseInt(idcard.substr(6,2))+1900) % 100 == 0 && (parseInt(idcard.substr(6,2))+1900) % 4 == 0 )){
ereg=/^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$/;//试出生日期的合法?br />
} else {
ereg=/^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$/;//试出生日期的合法?br />
}
if(ereg.test(idcard)) return Errors[0];
else return Errors[2];
break;
case 18:
//18位n份号码检?br />
//出生日期的合法性检?br />
//闰年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))
//q_月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))
if ( parseInt(idcard.substr(6,4)) % 4 == 0 || (parseInt(idcard.substr(6,4)) % 100 == 0 && parseInt(idcard.substr(6,4))%4 == 0 )){
ereg=/^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$/;//闰年出生日期的合法性正则表辑ּ
} else {
ereg=/^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$/;//q_出生日期的合法性正则表辑ּ
}
if(ereg.test(idcard)){//试出生日期的合法?br />
//计算校验?br />
S = (parseInt(idcard_array[0]) + parseInt(idcard_array[10])) * 7
+ (parseInt(idcard_array[1]) + parseInt(idcard_array[11])) * 9
+ (parseInt(idcard_array[2]) + parseInt(idcard_array[12])) * 10
+ (parseInt(idcard_array[3]) + parseInt(idcard_array[13])) * 5
+ (parseInt(idcard_array[4]) + parseInt(idcard_array[14])) * 8
+ (parseInt(idcard_array[5]) + parseInt(idcard_array[15])) * 4
+ (parseInt(idcard_array[6]) + parseInt(idcard_array[16])) * 2
+ parseInt(idcard_array[7]) * 1
+ parseInt(idcard_array[8]) * 6
+ parseInt(idcard_array[9]) * 3 ;
Y = S % 11;
M = "F";
JYM = "10X98765432";
M = JYM.substr(Y,1);//判断校验?br />
if(M == idcard_array[17]) return Errors[0]; //ID的校验位
else return Errors[3];
}
else return Errors[2];
break;
default:
return Errors[1];
break;
}
}
function check(obj){
alert(checkIdcard(obj.value));
}
function dosubmit(){
var i = checkIdcard(document.getElementById('character_id').value);
if(i != "w䆾证验证通过!"){
return false;
} else {
document.getElementById('submit').submit;
}
}
下面是表单处的写?br />
<form action="" method="post" name="setICP">
<tr>
<td>中文?/span></td>
<td><input type='text' name='username_cn' value='<%{$res2[list].user_name_cn }%>' /> 您n份证上的名字</td>
</tr>
<tr>
<td>w䆾证号</td>
<td><input type='text' name='character_id' value='<%{$res2[list].character_id }%>' /> 您的w䆾证号?/span></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="submit" value="提交" onclick="dosubmit()" />
</td>
</tr>
</form>
q要记得在html文g头部引用那个js文g
<script language="JavaScript" type="text/javascript" src="/tpl/js/union_user_beian.js"></script>
]]>
]]>
一.?br />
什么是SOAP?SOAP是一个应用程序间的基于XML的通信工具和规?SOAP最初由MicroSoft和Userland公司开?已经演化了好几个版本,当前的版本SOAP1.1正飞速发?W3C的XML协议工作l正努力把它发展Z个真正的开发的标准.SOAP1.2草案已经发布,它能澄清 1.1版规范的Ҏh的地?
什么是Axis?AxisM上是一个SOAP引擎,但又不仅仅是个引?它还:1)是一个简单的独立的服务器2)是一个可插入到servlet引擎(如Tomcat)中的服务3)可扩展的支持WSDL4)能根据WSDL产生JAVA文g/c?)包括一些例子程?)包括一个可以监控TCP/IP包的工具
Axish于IBM的SOAP4J,是Apache SOAP的第三代产品,相对于以前的版本,它有如下Ҏ?1)快?它用了Z事g的SAX解析机制.2)灉|,用户可以灉|定制扩展.3)E_,接口会变动很小.4)Zlg开?5)支持WSDL1.1
?准备
应用Axis开发Web ServicesQ你需要安装如下YӞ
1.jdk1.5
2.安装Tomcat5.0.28,q是当前E_版本.Tomcatq行?0端口.讉Khttp://localhost/8080查Tomcat是否安装成功.
3.下蝲AXIS:
官方站点:http://xml.apache.org/axis/index.html
下蝲软g:axis-bin-1_2_1.tar.gz
4.下蝲相关?
a.mail.jar 下蝲地址:http://java.sun.com/products/javamail
b.activation.jar 下蝲地址:http://java.sun.com/products/javabeans/glasgow/jaf.html
c.xerces.jar 下蝲地址:http://xml.apache.org/xerces-j/index.html
d.xmlsec-1.2.1.jar 下蝲地址:http://xml.apache.org/security/
?安装
1.jdk1.5.0_04安装在D:\jdk1.5.0_04?当然安装路径可Q意?好后Q环境变量配|如下:
JAVA_HOME=D:\jdk1.5.0_04
CLASSPATH=.;D:\jdk1.5.0_04\jre\lib\rt.jar;D:\jdk1.5.0_04\jre\lib\tools.jar
Path=D:\jdk1.5.0_04\bin
2.tomcat-5.0.28安装在D:\tomcat-5.0.28?安装路径可Q?好后Q环境变量配|如下:
CATALINA_BASE=D:\tomcat-5.0.28
CATALINA_HOME=D:\tomcat-5.0.28
3.axis的安?br />
a.下载的Axis软g包解压羃Q将其中?#8220;webapps”目录下的“axis”目录整个拯到Tomcat安装目录下的“webapps”目录下?br />
b.mail.jar、activation.jar、xerces.jar、xmlsec-1.2.1.jar四个?全部拷到D:\tomcat-5.0.28\webapps\axis\WEB-INF\lib?br />
c.环境变量配置
AXIS_HOME=D:\tomcat-5.0.28
AXIS_LIB=%AXIS_HOME%\WEB-INF\lib
AXISCLASSPATH=%AXIS_LIB%\activation.jar;%AXIS_LIB%\axis.jar;%AXIS_LIB%\axis-ant.jar;%AXIS_LIB%\commons-discovery-0.2.jar;%AXIS_LIB%\commons-logging-1.0.4.jar;%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar;AXIS_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar;%AXIS_LIB%\mail.jar;AXIS_LIB%\wsdl4j-1.5.1.jar;%AXIS_LIB%\xerces.jar;%AXIS_LIB%\xmlsec-1.2.1.jar
CLASSPATH=%AXIS_HOME%\WEB-INF\lib
AXISCLASSPATH=%AXIS_LIB%\activation.jar;%AXIS_LIB%\axis.jar;%AXIS_LIB%\axis-ant.jar;%AXIS_LIB%\commons-discovery-0.2.jar;%AXIS_LIB%\commons-logging-1.0.4.jar;%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar;AXIS_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar;%AXIS_LIB%\mail.jar;AXIS_LIB%\wsdl4j-1.5.1.jar;%AXIS_LIB%\xerces.jar;%AXIS_LIB%\xmlsec-1.2.1.jar
q里有几炚w要注意:中间不要有空|包的名字可能有不同的版本Q这旉要修改一下包名?/p>
?试
安装配置完毕后,应测试一下是否tomcat、Axis可以正确q行了?br />
启动Tomcat服务器,在浏览器中访?a href="http://localhost:8080/axis/happyaxis.jsp">http://localhost:8080/axis/happyaxis.jspQ如果页面显C有错误Q则需要回头检查一下相关配|是否正,如果览面能正显C出pȝlg、属性等参数配置信息Q则表示安装成功。现在可以开始开发你的Web Services应用了。
五、服务的发布
Axis提供了两U服务发布方式,一U是x发布QInstant DeploymentQ,一U是定制发布QCustom DeploymentQ?/p>
1. 使用x发布 Java Web Service(JWS)
对即时发布的支持是Axis的特色之一Q用即时发布用户只需有提供服务的Javacȝ源代码,卛_其q速发布成Web服务。每当用戯用这cL务的时候,Axis会自动进行编译,即服务器重启了也不必对其做M处理Q用非常简单快捗?/p>
使用x发布首先需要一个实现服务功能的Java源文Ӟ其扩展名改?jwsQJava Web Service的羃写)Q然后将该文件放?#8220;……\webapps\axis”目录下即可?br /> 在此l出一个HelloWorldE序Q其源码如下Q?/p>
HelloWorld.java
public class HelloWorld {
public String sayHello()
{
return "HELLO WORLD!";
}
}
其攑ֈ“……\webapps\axis”目录Q通过讉Khttp://localhost:8080/axis/HelloWorld.jws?wsdl可以看到q个服务的WSDL描述文gQ这说明HelloWorld服务被成功发布了?br />
现在写个客户端程序访问一下:
下面我们介绍W二U发布方式,q是常用的?/p>
我们的第二种发布方式Q?br /> 1、将HelloWorld.java~译成HelloWorld.class,攑ֈ%TOMCAT_HOME%\webapps\axis\WEB-INF\classes
?/p>
2、在%TOMCAT_HOME%\webapps\axis\WEB-INF下新建deploy.wsdd文gQ即SOAP服务发布描述文g
deploy.wsdd
<deployment xmlns="
<parameter name="className" value="HelloWorld"/>
<parameter name="allowedMethods" value="sayHello"/>
</service>
</deployment>
在DOS下{换目录到%TOMCAT_HOME%\webapps\axis\WEB-INFQ命令:
java org.apache.axis.client.AdminClient deploy.wsdd 后会出现
Processing file deploy.wsdd
<Admin>Done processing</Admin>
q时你会发现目录下多了一个server-config.wsdd文gQ这是AXIS的配|文Ӟ以后所有的服务发布描述都会在里面找到。(当然Q你可以直接修改它,不用再写deploy.wsddQ然后打开览?a href="http://localhost:8080/axis/services/HelloWorld?wsdl">http://localhost:8080/axis/services/HelloWorld?wsdlQ你׃看到你的服务已发?/p>
同样用客LE序讉K一下:Q注意和上边的差别!Q)
?ȝ
Web Services是未来网l应用的发展方向QSOAP和WSDL是Web Services的核心协议,Axisl出了一个很好的SOAP实现Q它使得开发Web Services应用变得L而有?/p>
<property name="jmeter.save.saveservice.bytes" value="true"/> |
![]() |
Q?xml version="1.0"?Q?br />Qproject name="Hello world" default="doc"Q?/font> Q?-- properies --Q?br />Qproperty name="src.dir" value="src" /Q?br />Qproperty name="report.dir" value="report" /Q?br />Qproperty name="classes.dir" value="classes" /Q?br />Qproperty name="lib.dir" value="lib" /Q?br />Qproperty name="dist.dir" value="dist" /Q?br />Qproperty name="doc.dir" value="doc"/Q?/font> Q?-- 定义classpath --Q?br />Qpath id="master-classpath"Q?br />Qfileset file="${lib.dir}/*.jar" /Q?br />Qpathelement path="${classes.dir}"/Q?br />Q?pathQ?/font> Q?-- 初始化Q?--Q?br />Qtarget name="init"Q?br />Q?targetQ?/font> Q?-- ~译 --Q?br />Qtarget name="compile" depends="init" description="compile the source files"Q?br />Qmkdir dir="${classes.dir}"/Q?br />Qjavac srcdir="${src.dir}" destdir="${classes.dir}" target="1.4"Q?br />Qclasspath refid="master-classpath"/Q?br />Q?javacQ?br />Q?targetQ?/font> Q?-- 试 --Q?br />Qtarget name="test" depends="compile" description="run junit test"Q?br />Qmkdir dir="${report.dir}"/Q?br />Qjunit printsummary="on" haltonfailure="false" failureproperty="tests.failed" showoutput="true"Q?br />Qclasspath refid="master-classpath" /Q?br />Qformatter type="plain"/Q?br />Qbatchtest todir="${report.dir}"Q?br />Qfileset dir="${classes.dir}"Q?br />Qinclude name="**/*Test.*"/Q?br />Q?filesetQ?br />Q?batchtestQ?br />Q?junitQ?br />Qfail if="tests.failed"Q?br />*********************************************************** **** One or more tests failed! Check the output ... **** *********************************************************** Q?failQ?br />Q?targetQ?/font> Q?-- 打包成jar --Q?br />Qtarget name="pack" depends="test" description="make .jar file"Q?br />Qmkdir dir="${dist.dir}" /Q?br />Qjar destfile="${dist.dir}/hello.jar" basedir="${classes.dir}"Q?br />Qexclude name="**/*Test.*" /Q?br />Qexclude name="**/Test*.*" /Q?br />Q?jarQ?br />Q?targetQ?/font> Q?-- 输出api文 --Q?br />Qtarget name="doc" depends="pack" description="create api doc"Q?br />Qmkdir dir="${doc.dir}" /Q?br />Qjavadoc destdir="${doc.dir}" author="true" version="true" use="true" windowtitle="Test API"Q?br />Qpackageset dir="${src.dir}" defaultexcludes="yes"Q?br />Qinclude name="example/**" /Q?br />Q?packagesetQ?br />QdoctitleQ<![CDATA[Qh1QHello, testQ?h1Q]]Q</doctitleQ?br />QbottomQ<![CDATA[QiQAll Rights Reserved.Q?iQ]]Q</bottomQ?br />Qtag name="todo" scope="all" description="To do:" /Q?br />Q?javadocQ?br />Q?targetQ?br />Q?projectQ?/font> |
![]() |
![]() |
![]() |
Buildfile: F:\eclipse-projects\Hello\build.xml init: compile: [mkdir] Created dir: F:\eclipse-projects\Hello\classes [javac] Compiling 2 source files to F:\eclipse-projects\Hello\classes test: [mkdir] Created dir: F:\eclipse-projects\Hello\report [junit] Running example.HelloTest [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.02 sec pack: [mkdir] Created dir: F:\eclipse-projects\Hello\dist [jar] Building jar: F:\eclipse-projects\Hello\dist\hello.jar doc: [mkdir] Created dir: F:\eclipse-projects\Hello\doc [javadoc] Generating Javadoc [javadoc] Javadoc execution [javadoc] Loading source files for package example... [javadoc] Constructing Javadoc information... [javadoc] Standard Doclet version 1.4.2_04 [javadoc] Building tree for all the packages and classes... [javadoc] Building index for all the packages and classes... [javadoc] Building index for all classes... [javadoc] Generating F:\eclipse-projects\Hello\doc\stylesheet.css... [javadoc] Note: Custom tags that could override future standard tags: @todo. To avoid potential overrides, use at least one period character (.) in custom tag names. [javadoc] Note: Custom tags that were not seen: @todo BUILD SUCCESSFUL Total time: 11 seconds |
q中间每ơ修攚w要经q重复的无数ơ手工copy,paste...q程Q好累,好花旉。可我确实不惛_
学脚本编写,unix,linux下我q得重学Q太累?/font>
如果你和我一样希望自动执行预定义的动作却又不惛_累,那末Q我觉得ANT工具不错Q?
它可以以相同的用法用在不同^?跨^?。它是JAVA做的Q免费的Q开源的Q据说网上已l成了事实的JAVA
构徏标准Q还听说高手都用?..... ^_^
我花?天时间才掌握了它的基本用法,我希望你能通过我的文章和试验花半天旉掌握它?/font>
内容Q?/font>
下蝲Q安?
又下?又是一大堆参数变量? :(
http://jakarta.apache.org/ant/index.html 它是apache的java子项?jakarta"的子目.你可以选择
当前的版本,目前我是1.5?window版,以下q它讲?/font>
解压后ant_home用来方便讉K。ƈ保你也讄了java_home ?
set ant_home=D:\java\kit\ant\jakarta-ant-1.5.1 q是我的目录
p些,单吧?
hello ant
我们要开发一个javac:其内容只有一句,输出"hello ant"字符丌Ӏƈ使用ant完成~译和运行工作,q个例子只是Z跑通antQ不附加多余的东ѝ?/font>
下图为文件组l,请徏立相应的目录Qƈ~写HelloAnt.java
按照人家老外的文件组l规则咱也照搬?/font>
hello.ant.HelloAnt.java |
在项目根目录(hello-ant\)?个文Ӟant执行配置文gbuild.xml
build.xml |
ok,一切大功告成,哦,不,q没有运行它?/font>
dos下进入hello-ant的目录,即build.xml所在的目录Q我们要用ant工具执行?Q?/font>
执行: %ant_home%/bin/ant -file build.xml 用ant工具执行当前目录下的配置文gbuild.xml
或?Qant -file build.xml 你如果设|?ant_home%/bin到path?/font>
q次ok了,q是{案Q?/font>
命o提示W窗?/font> |
D:\temp\hello-ant>ant -file build.xml Buildfile: build.xml main: [javac] Compiling 1 source file to D:\temp\hello-ant\build\classes [java] hello ant,ant 的第一ơ接触,好棒Q? BUILD SUCCESSFUL Total time: 2 seconds D:\temp\hello-ant> |
查一下build/classes目录Q哦Q看到编译过的文件就在这?
build/classes/hello/ant/HelloAnt.class.
hello ant q
(此段比较废话Q可以略q?
你也怼_q末单的工作写个批处理不得了,又xml又ant的,把我的时间都费完了Q我用jbuild?
webShpere不就得了Q怎末说你才明白呢Q反正网上开源项目大多数都用ant,你M能给人家?.jpx吧,
而且q样的工具太贵,受不?当然用D的兄弟不怕^_^ )Q而且ant可以让你明确的管理和自动化所有的东西:
~译-实施-试...,哎,E微ȝ一点点Q但节约你以前花在零的copy,paste上的旉.而且我发现管?
代码的质量有所提高.
我们要改qbuild.xmlQ让它做更多的事情:
凡事都讲I^衡,你要antl你做更多事Q当然要累一点点Q不q只用篏一ơ,以后的代码修改后的构建都?一键式"完成,我们制作一个hello的简单例子,你可以自己做j2ee的练习?/font>
我们要扩充目录结构,使它更像回事Q?/font>
ant处理~译之前的目录:
ant处理之后的目录:
图中Q\src,\docs,\lib是自ql的文gl构Q\build,\dist是ant动态生成的成品?/font>
\src 源文Ӟjava源,script源,jsp源,xml配置.....
\src\main java?
\src\script window,unix,liunx的执行scriptQ我们的单只有一个:
run.bat: java hello.ant.HelloAnt
\docs 手写说明文
\lib E序所需cd的jar,比如j2ee.jar,mail,jar...
\build 用ant动态生成的构徏目录
\build\classes ~译的类文g
\build\docs copy "\docs"的手写说明文,和ant生成的api文
\build\lib 攄我们自己的HelloAnt.class打包成品hello-ant.jar
\dist\bin copy "\src\script" 得执行文?
\dist\docs copy "\build\docs" 的文?
\dist\lib 除了copy "\build\lib"下的hello-ant.jar外,
q应copy "\lib"的程序所需jarQ这里我们没有?/font>
以上是我学老外的文件组l,大家可以按照自己的爱好组l?/font>
我们~写必要的文Ӟ
hello.ant. HelloAnt.java |
已有 |
\src\script.bat |
\docs\index.html 随便写一个手写的文 |
hello ant 软g目手册docs |
\build.xml 配置文g |
build.xml多了些,但其实很单:(注释比较详细可以参照Q这里再单说一?
一个build.xml包含一个工E的自动化处理的完整xml说明Qƈ且基本由3U东东组成:
<project >
1.全局变量的定?
<property/>
2.dl?
<target>
3.许多单项d... 像copy,delete,javac,jar...
<task1/>
<task2/>
<task3/>
</target>
</project>
Xmlhttp是MS推的一Ҏ术,功能很复杂,可以做很多事情,比如客户端可以在单的HTML中打开HTTPq接Q主动向serverh数据q获得返回数据,是DOM技术一个非帔R要的应用Q利用它来写无刷新的动态页面简直是so easyQ做qweb开发的兄弟应该明白它的意义有多么重大?br />
一、 session监听
servlet中对session的监听有很多接口Q功能很灉|Q最常用的是监听Session和Attribute。这里要澄清一下概念,servlet中的session监听和Attribute监听含义有差别,session监听指的不是我们一般所理解的放|一个session或者销毁一个sessionQ这是Attribute监听的功能,因ؓservlet中放|session的语法是session.setAttribute(“session名?要放入的对象)。而session监听Q监听的是HTTPq接Q只要有用户与serverq接Q就连接的是一个空白的jsp面Q也会触发session事gQ所以此处的session实际上指的是connectionQ用来统计当前在U用h最合适了。不知道我说清楚了没有。下面分别讲解这两种监听方式?br />
1、 session监听
首先~写一个session监听c,实作HttpSessionListener接口Q它的作用是计算当前有多个在线用户Q?br />
mselect.xml
<?xml version="1.0" encoding="GB2312" ?>
<Troot>
<Item id="1" pid="0" c="1">大学</Item>
<Item id="2" pid="0" c="3">中学</Item>
<Item id="3" pid="0" c="3">学</Item>
<Item id="4" pid="2" c="2">高中</Item>
<Item id="5" pid="2" c="5">初中</Item>
<Item id="6" pid="15" c="3">清华大学</Item>
<Item id="7" pid="15" c="4">北京大学</Item>
<Item id="8" pid="5" c="3">天|铁三?lt;/Item>
<Item id="9" pid="4" c="3">天|市二?lt;/Item>
<Item id="10" pid="16" c="2">天|音乐学院</Item>
<Item id="11" pid="15" c="5">天|商学?lt;/Item>
<Item id="12" pid="4" c="3">耀华中?lt;/Item>
<Item id="13" pid="3" c="6">昆纬路小?lt;/Item>
<Item id="14" pid="2" c="6">七中</Item>
<Item id="15" pid="1" c="1">l合c院?lt;/Item>
<Item id="16" pid="1" c="1">艺术c院?lt;/Item>
<Item id="17" pid="15" c="4">ȝ大学</Item>
<Item id="18" pid="15" c="4">天|师范大学</Item>
<Item id="19" pid="15" c="23">天|大学</Item>
<Item id="20" pid="15" c="7">南开大学</Item>
<Item id="21" pid="4" c="23">天|铁一?lt;/Item>
<Item id="22" pid="5" c="5">天|铁一?lt;/Item>
<Item id="23" pid="3" c="3">天|市铁路职工子弟第三小?lt;/Item>
<Item id="24" pid="3" c="3">天|市铁路职工子弟第一学</Item>
<Item id="25" pid="16" c="3">术学院</Item>
<Item id="26" pid="16" c="3">体育学院</Item>
</Troot>
test.html
<HTML>
<HEAD>
<TITLE>XML版本的多U联?lt;/TITLE>
<SCRIPT LANGUAGE=javascript>
<!--
/***********************************************
//
// 用DOM实现Q基于XML的动态NU联?BR>//
//**********************************************/
// 最l版权归 DSclubQQ兀Q拥有,您可以在未授权的情况下用,但请保留此信?BR>//
// EMailQ?A href="mailto:dsclub@hotmail.com">dsclub@hotmail.com
// QQQ?967030
// Nick Name: DSclub(兀儿-q部)
// 姓名QQ兀
// 多联动的Select集合
var objSelects = new Array();
// 创徏应有的Select对象
function funCreateSelectEl(passPid)
{
// 创徏DOMDocument对象
var xmlSrc = new ActiveXObject("MicroSoft.XMLDOM");
xmlSrc = xmlSource.XMLDocument;
// root为文对象的根节?BR>var root = xmlSrc.documentElement;
// 得到所传父ID的所有节?BR>var currentItems = root.selectNodes("http://Troot/Item[@pid = " + passPid + "]");
var iItems = currentItems.length;
if(iItems > 0)
{
// 创徏Selectq把OnChange事g写好
var newChild = document.createElement("<SELECT onchange='eventSltChange()' id='slt" + objSelects.length + "'>");
// 向集合中d新徏的Select对象
objSelects[objSelects.length] = newChild;
// 向Select对向d所有的Option
var i;
for(i = 0; i < iItems; i++)
{
var oOption = document.createElement("OPTION");
oOption.text = currentItems[i].text;
oOption.value = currentItems[i].attributes[0].text;
newChild.options.add(oOption);
}
// 新建的Select攑ֈ目标?BR> oDIV.appendChild(newChild);
// 先置一个空白空?BR> newChild.value = "";
}
}
// Select的OnChange事g响应函数
function eventSltChange()
{
// 删除全部本层下的孩子
var i;
for(i = objSelects.length - 1; i > parseInt(event.srcElement.id.replace("slt",""), 10); i--)
{
oDIV.removeChild(objSelects[i]);
objSelects.pop();
}
// 响应新的选择
funCreateSelectEl(event.srcElement.value);
// 联动的选择l果
var resultArray = new Array();
for(i = 0; i < objSelects.length; i++)
{
resultArray[i] = objSelects[i].value;
}
// 输出选定
OUTPUT.innerText = resultArray.join("-");
}
//-->
</SCRIPT>
</HEAD>
<BODY onload="funCreateSelectEl(0)">
<DIV ID="oDIV"></DIV>
<XML ID="xmlSource" src="mselect.xml"/>
<BR/>
选定l果Q?lt;SPAN ID="OUTPUT"></SPAN>
</BODY>
</HTML>