WebService开发指南
WebServiceInAurora
Web Service是一种面向服务的架构的技术,通过标准的Web协议提供服务,目的是保证不同平台的应用服务可以互操作。在Aurora框架中可以方便的提供Web Service服务及调用由他人发布的Web Service。
1. 从
Aurora-framework上下载最新的aurora.jar
2. 添加web service监听器。打开
WEB-HOME\WEB-INF\aurora.feature\service-listener.config,在 <participant-list category="service"> 节点下,添加 <participant class="aurora.service.ws.SOAPServiceInterpreter"/> 子节点。
3. 取消此svc的登录校验。在系统注册svc文件时,设置不需要登录的权限。对于webservice作为一个独立部署的工程,可以取消service-procedure.config配置文件,这样所有的文件都不需要登录校验。
4. 撰写svc文件,提供给他人调用即可。
我们提供一个svc,此svc把请求的数据插入到一个数据库表中。
1. create table account_test(ACCOUNT_ID number,ACCOUNT_OTHER_CODE varchar2(100)); 新建一个对应的结果表
2. 建立一个此表的bm:
<?xml version="1.0" encoding="UTF-8"?> <!-- $Author: linjinxiao $Date: 2011-11-9 上午10:42:34 $Revision: 1.0 $Purpose: --> <bm:model xmlns:bm="http://www.aurora-framework.org/schema/bm" alias="t1" baseTable="ACCOUNT_TEST"> <bm:fields> <bm:field name="ACCOUNT_ID" databaseType="NUMBER" datatype="java.lang.Long" physicalName="ACCOUNT_ID" prompt="ACOUNT_TEST.ACCOUNT_ID"/> <bm:field name="ACCOUNT_OTHER_CODE" databaseType="VARCHAR2" datatype="java.lang.String" physicalName="ACCOUNT_OTHER_CODE" prompt="ACOUNT_TEST.ACCOUNT_OTHER_CODE"/> </bm:fields> </bm:model>
3. 在modules/sys目录下新建ws_test.svc文件,并输入以下内容
<?xml version="1.0" encoding="UTF-8"?> <a:service xmlns:a="http://www.aurora-framework.org/application"> <a:init-procedure> <a:model-insert model="fnd.account_test"/> </a:init-procedure> <a:service-output output="/parameter"/> </a:service>
以上内容就完成了服务器的编写,下面提供客户端的调用代码示例
4. 使用开源框架axis2为例
public class LowLevelClient { public static void main(String[] args) throws AxisFault { ServiceClient client = new ServiceClient(); Options options = new Options(); options.setTo(new EndpointReference( "http://127.0.0.1:8080/newhec/modules/sys/ws_test.svc"));//修正为实际工程的URL client.setOptions(options); OMElement request = makeRequest(); OMElement response = client.sendReceive(request); System.out.println("ok:"+response.toString()); } private static OMElement makeRequest() { OMFactory factory = OMAbstractFactory.getOMFactory(); OMElement request = factory.createOMElement(new QName( "", "parameter")); request.addAttribute("ACCOUNT_ID", "123", null); request.addAttribute("ACCOUNT_OTHER_CODE", "test", null); return request; }
5. 查看插入数据库记录是否成功和控制台的结果是否正确。
这个例子处理有头行结构的例子。譬如,调用方发送过来的请求是如下格式
<requestHead seqNo="1"/> <requestBody> <records> <record ACCOUNT_ID="22" ACCOUNT_OTHER_CODE="test"/> <record ACCOUNT_ID="33" ACCOUNT_OTHER_CODE="test2"/> </records> </requestBody>
返回的结构要求是如下格式
<responseHead xmlns="http://aurora.org" seqNo="1" /> <requestBody responsedate="3008900"> <records> <record ACCOUNT_ID="22" ACCOUNT_OTHER_CODE="test" REQUEST_RESULT="successful" /> <record ACCOUNT_ID="33" ACCOUNT_OTHER_CODE="test2" REQUEST_RESULT="successful" /> </records> </requestBody>
1. 我们创建一个pkg:
create or replace package ACCOUNT_TEST_PKG is procedure insert_account_test(p_account_id number, p_account_other_code varchar2, p_request_result out varchar2); end ACCOUNT_TEST_PKG; create or replace package body "ACCOUNT_TEST_PKG" is procedure insert_account_test(p_account_id number, p_account_other_code varchar2, p_request_result out varchar2) is begin insert into account_test (account_id, account_other_code) values (p_account_id, p_account_other_code); p_request_result := 'successful'; end; end ACCOUNT_TEST_PKG; /
2. 在bm中调用此pkg:
<bm:operations> <bm:operation name="insert"> <bm:update-sql><![CDATA[ begin ACCOUNT_TEST_PKG.insert_account_test ( p_account_id => ${@ACCOUNT_ID}, p_account_other_code => ${@ACCOUNT_OTHER_CODE}, p_request_result => ${@REQUEST_RESULT} ); end; </bm:update-sql> <bm:parameters> <bm:parameter name="ACCOUNT_ID"/> <bm:parameter name="ACCOUNT_OTHER_CODE"/> <bm:parameter name="REQUEST_RESULT" output="true" outputPath="@REQUEST_RESULT"/> </bm:parameters> </bm:operation> </bm:operations>
3. 撰写svc如下:
<?xml version="1.0" encoding="UTF-8"?> <a:service xmlns:a="http://www.aurora-framework.org/application" xmlns:p="uncertain.proc"> <a:init-procedure> <p:echo/> <!-- 对数据进行循环操作--> <batch-apply sourcepath="/parameter/requestBody/records"> <a:model-insert model="fnd.account_test"/> </batch-apply> <!--更改节点的名称和namespace--> <p:set-element target="/parameter/requestHead" name="responseHead" namespace="http://aurora.org" /> <!-- 获得当前的时间--> <a:model-query fetchAll="true" fethOneRecord="true" model="ccic.systest" rootPath="/parameter/requestBody"/> </a:init-procedure> <a:service-output output="/parameter/"/> </a:service>
4. 撰写客户端调用代码,同样是利用开源框架axis2:
private static OMElement makeRequest2() { OMFactory factory = OMAbstractFactory.getOMFactory(); OMElement request = factory.createOMElement(new QName("soap:Body")); OMElement requestHead = factory.createOMElement(new QName("requestHead")); requestHead.addAttribute("seqNo", "1", null); OMElement requestBody = factory.createOMElement(new QName("requestBody ")); OMElement records = factory.createOMElement(new QName("records ")); OMElement record = factory.createOMElement(new QName("record ")); record.addAttribute("ACCOUNT_ID", "11", null); record.addAttribute("ACCOUNT_OTHER_CODE", "test", null); records.addChild(record); requestBody.addChild(records); request.addChild(requestHead); request.addChild(requestBody); return request; }
5. 执行此客户端,并查询数据库记录和返回结果是否正确。
假设服务端要求提交的格式如下:
<axis2ns1:cancat xmlns:axis2ns1="http://www.aurora.org/simple/"> <s1>abc</s1> <s2>def</s2> </axis2ns1:cancat>
返回的格式如下:
<ns1:syncAccResponse xmlns:ns1=\http://service.test.com\> <ns1:out> <ns2:Account xmlns:ns2=\http://dto.test.com\> <ACCOUNT_ID xmlns=\http://dto.test.com\>1681</ACCOUNT_ID> <ACCOUNT_OTHER_CODE xmlns=\http://dto.test.com\></ACCOUNT_OTHER_CODE> </ns2:Account> <ns2:Account xmlns:ns2=\http://dto.test.com\> <ACCOUNT_ID xmlns=\http://dto.test.com\>1682</ACCOUNT_ID> <ACCOUNT_OTHER_CODE xmlns=\http://dto.test.com\></ACCOUNT_OTHER_CODE> </ns2:Account> </ns1:out> </ns1:syncAccResponse>
1. 模拟第三方发布服务,以axis2为例
1. 下载
axis2
2. 把
simpleWebService部署到
AXIS2_HOME/repository/services下,然后启动服务
2. 编写客户端代码
1. 编写systest.bm文件
<?xml version="1.0" encoding="UTF-8"?> <bm:model xmlns:bm="http://www.aurora-framework.org/schema/bm"> <bm:operations> <bm:operation name="query"> <bm:query-sql><![CDATA[select '3008900' exp_report_number, '222222222' employee_code, '测试' name, 'MAS' unit_code, 'test@hand-china.com' email, '0' type from dual </bm:query-sql> </bm:operation> </bm:operations> </bm:model>
2. 编写svc文件
<?xml version="1.0" encoding="UTF-8"?> <a:service xmlns:a="http://www.aurora-framework.org/application" xmlns:p="uncertain.proc" xmlns:rs="aurora.database.rsconsumer" > <a:init-procedure> <!-- 构建符合的请求的格式--> <a:model-query fetchAll="true" prefix="ns1" nameSpace="http://www.aurora.org/simple" localName="cancat" model="sys.systest" rootPath="/model/result1"/> <a:model-query fetchAll="true" model="sys.systest" localName="s1" attribAsCdata="true" attribAsCdataList="unit_code"rootPath="/model/result1/cancat"/> <a:model-query fetchAll="true" model="sys.systest" localName="s2" attribAsCdata="true" attribAsCdataList="employee_code" rootPath="/model/result1/cancat"/> <!-- 请求WebService--> <a:ws-invoker raiseExceptionOnError="false" url="http://localhost:8080/axis2/services/simple" inputPath="/model/result1/cancat" returnPath="/model/syncAccResponse"/> <!--把子节点中cdata的内容整合成父节点中的一个属性 --> <p:method-invoke className="uncertain.composite.CompositeUtil" methodName="collapse"> <p:arguments> <p:argument path="/model/syncAccResponse/out" type="uncertain.composite.CompositeMap"/> </p:arguments> </p:method-invoke> <as:SetParameterParsed /> <!--循环处理子节点 --> <batch-apply sourcepath="/model/syncAccResponse/out"> <a:model-insert model="fnd.account_test"/> </batch-apply> </a:init-procedure> <a:service-output output="/parameter/"/>
3. 调用此svc,并查看日志和数据库记录是否正确。
最基本的四个功能:model-query,model-insert,model-update,model-delete
把单条记录查询结果返回的属性直接更新到指定的节点上: fetchOneRecord="true" 。例子:
<a:model-query fetchAll="true" fetchOneRecord="true" model="sys.systest" rootPath="/model/result1/cancat"/>
如果不加此属性,返回的结果是
<model> <result1> <concat> <record employee_cod="test"/> </concat> </result1> </model>
加了此属性后,返回结果就是:
<model> <result1> <concat employee_cod="test"/> </result1> </model>
注意:此属性仅对返回单条记录有效。
批量循环处理:batch-apply 。例子:
<batch-apply sourcepath="/model/syncAccResponse/out"> <a:model-insert model="fnd.account_test"/> </batch-apply>
表示对/model/syncAccResponse/out下面的所有子节点执行model-insert操作。
更改查询返回的数据格式。例子:
<a:model-query fetchAll="true" model="sys.systest" localName="s2" attribAsCdata="true" attribAsCdataList="employee_code" prefix="ns1" nameSpace="http://www.aurora.org/simple" rootPath="/model/result1/cancat"/> </a:model-query>
如果不加localName="s2"...nameSpace="http://www.aurora.org/simple" 那段,那么原本的返回结果可能是
<model> <result1> <concat> <record employee_cod="test"/> </concat> </result1> </model>
经过处理后,就变成
<model> <result1> <concat> <ns1:s2 xmlns:ns1="http://www.aurora.org/simple">test</ns1:s2> </concat> </result1> </model>
把cdata的内容变成父节点的一个属性,例子:
<p:method-invoke className="uncertain.composite.CompositeUtil" methodName="collapse"> <p:arguments> <p:argument path="/model/syncAccResponse/out" type="uncertain.composite.CompositeMap"/> </p:arguments> </p:method-invoke>
原先的数据格式可能是这样的:
<model> <syncAccResponse> <out> <s1>yes</s1> <s2>test</s2> </out> </syncAccResponse1> </model>
处理后的格式就是这样的
<model> <syncAccResponse> <out s1="yes" s2="test"/> </syncAccResponse1> </model>
复制元素属性,例子
<p:method-invoke className="uncertain.composite.CompositeUtil" methodName="copyAttributes" > <p:arguments> <p:argument path="/model/result4" type="java.util.Map"/> <p:argument path="/parameter" type="java.util.Map"/> </p:arguments> </p:method-invoke>
原先的格式可能是这样的
<model> <result4 s1="yes" s2="test"/> </model> <parameter s3="aurora" />
执行后就变成这个效果
<model> <result4 s1="yes" s2="test"/> </model> <parameter s1="yes" s2="test" s3="aurora" />
更改元素名称、namespace和前缀:set-element。
例子
<p:set-element target="/model/syncAccResponse/out" name="ok" prefix="right" namespace="http://aurora.org" childLevel="0"/>
原先的格式可能是这样的:
<model> <syncAccResponse> <out> <s1>yes</s1> <s2>test</s2> </out> </syncAccResponse1> </model>
执行后变成:
<model> <syncAccResponse> <right:ok xmlns:right="http://aurora.org"> <s1>yes</s1> <s2>test</s2> </right:ok> </syncAccResponse1> </model>
其中childLevel表示子节点的层次,如果是0,代表本节点,如果是1,表示下一级子节点。假设把上面的childLevel改成1,然后结果会变成:
<model> <syncAccResponse> <out> <right:ok xmlns:right="http://aurora.org" >yes</right:o> <right:ok xmlns:right="http://aurora.org">test</right:o> </out> </syncAccResponse1> </model>