Worklight中適配器的開(kāi)發(fā)
文章將通過(guò)基于 Eclipse 的 IDE,來(lái)開(kāi)發(fā) Worklight 支持的 HTTP 和 SQL 適配器;并且演示測(cè)試適配器的方法,最后,通過(guò)集成應(yīng)用、適配器和 Web Service 來(lái)體現(xiàn)適配器在開(kāi)發(fā)移動(dòng)應(yīng)用中的重大作用。
Worklight 中適配器的架構(gòu)
WorklightWorklight作為一個(gè)移動(dòng)程序的開(kāi)發(fā)平臺(tái),通過(guò)三種模式(Web、混合、本地)支持了不同需求的客戶端開(kāi)發(fā),但是面臨的一個(gè)現(xiàn)實(shí)問(wèn)題是如何和已有的系統(tǒng)進(jìn)行良好的集成。適配器(adapter)是平臺(tái)的服務(wù)器端組件,作為一個(gè)傳輸層負(fù)責(zé)Worklight 服務(wù)器和不同企業(yè)級(jí)后臺(tái)的連接。通過(guò)平臺(tái)支持的 HTTP 和 SQL 適配器,客戶端應(yīng)用可以與原有系統(tǒng)方便的進(jìn)行數(shù)據(jù)訪問(wèn),這減少了重復(fù)的開(kāi)發(fā)工作。
適配器由三部分組成:基于 XML 的配置文件,用于定義適配器的類型和提供的方法;基于 JS 的方法實(shí)現(xiàn)文件,通過(guò) Mozilla Rhino 解析器實(shí)現(xiàn)對(duì)不同后臺(tái)的訪問(wèn);基于 XSL 文件的數(shù)據(jù)轉(zhuǎn)化規(guī)則,在 HTTP 適配器中存在,用于將獲得的數(shù)據(jù)按照一定規(guī)則進(jìn)行轉(zhuǎn)化。
Worklight支持的適配器提供了如下特性:
- 快速開(kāi)發(fā):開(kāi)發(fā)者可以使用便捷而強(qiáng)大的服務(wù)器端 JavaScript 創(chuàng)建簡(jiǎn)潔易讀的代碼,用于和不同后臺(tái)程序的集成。
 - 只讀操作和事務(wù)操作:平臺(tái)通過(guò)適配器支持對(duì)后臺(tái)系統(tǒng)的只讀和事物操作。
 - 安全:適配器使用靈活的認(rèn)證機(jī)制創(chuàng)建與后臺(tái)的連接,使用的用戶可以是系統(tǒng)用戶也可以是事務(wù)中操作的用戶。
 - 可擴(kuò)展性:適配器通過(guò) cache 機(jī)制減少了與后臺(tái)系統(tǒng)的交互,并且通過(guò)配置,限定和后臺(tái)系統(tǒng)建立的連接數(shù)。
 - 數(shù)據(jù)透明:適配器提供了獲取后臺(tái)數(shù)據(jù)的統(tǒng)一接口,這樣開(kāi)發(fā)者在獲取數(shù)據(jù)時(shí)就不需要關(guān)注數(shù)據(jù)源、格式和傳輸協(xié)議信息。
 
圖 1. 適配器架構(gòu)

圖 1 描述了適配器和前臺(tái)的 app 以及后臺(tái)系統(tǒng)間數(shù)據(jù)交互的過(guò)程,具體的流程如下:
- 適配器暴露一系列的方法,稱之為 procedures,前臺(tái) app 通過(guò) Ajax 方式調(diào)用這些過(guò)程。
 - 這些方法從后臺(tái)獲取相應(yīng)信息。
 - 后臺(tái)系統(tǒng)返回的數(shù)據(jù)格式如下
    
- 如果格式為 JSON,Worklight服務(wù)器直接返回。
 - 如果格式不是 JSON,服務(wù)器將數(shù)據(jù)格式轉(zhuǎn)化為 JSON 后,再返回。
 
 - 這些方法將獲得的 JSON 數(shù)據(jù)進(jìn)行業(yè)務(wù)處理,將最終結(jié)果返回給 app。
 
基于 Eclipse 的適配器開(kāi)發(fā)
根據(jù)適配器連接的后臺(tái)種類,它被分為 HTTP 適配器和 SQL 適配器。在實(shí)際的應(yīng)用中 SQL 適配器用于連接不同的后臺(tái)數(shù)據(jù)庫(kù),并通過(guò) SQL 語(yǔ)句或存儲(chǔ)過(guò)程進(jìn)行訪問(wèn)。適配器目前支持 MySQL 和 Oracle 的數(shù)據(jù)庫(kù),并且在幾個(gè)月后將支持 DB2 數(shù)據(jù)庫(kù)。HTTP 適配器支持訪問(wèn) soap 服務(wù)和 rest 服務(wù),并且可以根據(jù)指定的 XSL 文件對(duì)獲得的數(shù)據(jù)片段進(jìn)行轉(zhuǎn)化,獲得用戶需要的數(shù)據(jù)和格式。
為了更好的演示不同適配器的作用和相互合作的方法,文章將構(gòu)建如下場(chǎng)景:
- 首先創(chuàng)建 SQL 適配器,并且提供對(duì)于 SQL 語(yǔ)句和存儲(chǔ)過(guò)程的訪問(wèn)。
 - 然后創(chuàng)建 HTTP 適配器,用于訪問(wèn)由 Axis2 框架構(gòu)建的 Web Service。
 - 通過(guò) Eclipse 提供的測(cè)試環(huán)境,對(duì)于前面創(chuàng)建的適配器進(jìn)行測(cè)試,并且解釋測(cè)試返回信息的含義。
 - 最后通過(guò)開(kāi)發(fā)一個(gè)應(yīng)用,并且整合上述的適配器,完成一個(gè)完整的功能。
 
 adapter-jndi-name = ${custom-db.1.jndi-name} 
 custom-db.1.relative-jndi-name = jdbc/worklight_adapter 
 custom-db.1.driver = com.mysql.jdbc.Driver 
 custom-db.1.url = jdbc:mysql://localhost:3306/worklight_adapter 
 custom-db.1.username = root 
 custom-db.1.password = root 
在安裝有Worklight 插件的 Eclipse 下開(kāi)發(fā)適配器通過(guò)下面的步驟可以完成:
新建工程和 SQL 適配器:
- 點(diǎn)擊 File-> New -> Project -> Worklight -> Worklight Project
 - 輸入工程名稱:HelloAdapterProj,點(diǎn)擊確認(rèn),完成工程創(chuàng)建
 - 點(diǎn)擊 File-> New -> Other -> Worklight -> Worklight Adapter
 - 輸入應(yīng)用名稱:MySQLAdapter,選擇 HelloAdapterProj 作為工程,在適配器類型中選擇 SQL Adapter,點(diǎn)擊確認(rèn),完成 SQL 適配器創(chuàng)建
 
創(chuàng)建完畢的適配器其文件結(jié)構(gòu)如下:
- MySQLAdapter.xml:在文件中定義了很多的屬性,比如適配器在Worklight 控制臺(tái)中顯示的名稱和簡(jiǎn)單描述,最大的連接數(shù)等等,其中最重要的是下列兩個(gè)參數(shù):
    
- dataSourceJNDIName 定義了適配器所連接的 JNDI 的名稱,具體的配置將在下面敘述
 - procedure 定義適配器向外提供的方法名稱(需要通過(guò) .js 文件實(shí)現(xiàn))
 
 - MySQLAdapter-impl.js:在文件中實(shí)現(xiàn)了兩個(gè)方法,getAllCountries 用于調(diào)用數(shù)據(jù)庫(kù)中的存儲(chǔ)過(guò)程;getOneCountryID 通過(guò)調(diào)用 SQL 語(yǔ)句獲取相應(yīng)的信息
 
對(duì)于適配器需要的 JNDI 參數(shù),可以通過(guò)修改 Worklight 的配置文件實(shí)現(xiàn)。在 Worklight 框架中存在一個(gè)默認(rèn)的配置文件 default.worklight.properties,用戶可以通過(guò)修改 worklight.properties 文件來(lái)覆蓋默認(rèn)配置文件中的一些屬性。清單 1 描述了為連接本地的 SQL 數(shù)據(jù)庫(kù),在 worklight.properties 添加的配置信息:
清單 1. JNDI 配置
- adapter-jndi-name = ${custom-db.1.jndi-name}
 - custom-db.1.relative-jndi-name = jdbc/worklight_adapter
 - custom-db.1.driver = com.mysql.jdbc.Driver
 - custom-db.1.url = jdbc:mysql://localhost:3306/worklight_adapter
 - custom-db.1.username = root
 - custom-db.1.password = root
 
所有帶有 custom 屬性的參數(shù)均為Worklight 保留的參數(shù),適配器部署到服務(wù)器后,系統(tǒng)會(huì)讀取 XML 文件中的 dataSourceJNDIName 屬性,然后根據(jù) JNDI 配置的信息來(lái)建立數(shù)據(jù)庫(kù)的連接。
清單 2. SQL 適配器配置
- <connectivity>
 - <connectionPolicy xsi:type="sql:SQLConnectionPolicy">
 - <dataSourceJNDIName>${adapter-jndi-name}</dataSourceJNDIName>
 - </connectionPolicy>
 - <loadConstraints maxConcurrentConnectionsPerNode="5" />
 - </connectivity>
 - <procedure name="getAllCountries"/>
 - <procedure name="getOneCountryID"/>
 
清單 3. SQL 適配器方法實(shí)現(xiàn)
- var procedure1Statement =
 - WL.Server.createSQLStatement("select * from country where name = ?;");
 - function getAllCountries() {
 - return WL.Server.invokeSQLStoredProcedure({
 - procedure : "getAllCountries",
 - parameters : []
 - });
 - }
 - function getOneCountryID(name) {
 - var result = WL.Server.invokeSQLStatement({
 - preparedStatement : procedure1Statement,
 - parameters : [name]
 - });
 - var id = result.resultSet[0].index1;
 - return getLeagueInfo(id);
 - }
 
在 JS 文件中,函數(shù)通過(guò)調(diào)用 WL.Server.invokeSQLStoredProcedure 方法執(zhí)行后臺(tái)數(shù)據(jù)庫(kù)提供的存儲(chǔ)過(guò)程,調(diào)用 WL.Server.invokeSQLStatement 方法實(shí)現(xiàn)了對(duì) SQL 語(yǔ)句的執(zhí)行,方法需要的參數(shù)和函數(shù)的具體說(shuō)明,用戶可以在Worklight 提供的開(kāi)發(fā)者手冊(cè)上了解它的詳細(xì)使用過(guò)程。
HTTP 適配器
建立 HTTP 適配器的過(guò)程類似 SQL 適配器:
- 點(diǎn)擊 File-> New -> Other -> Worklight -> Worklight Adapter
 - 輸入應(yīng)用名稱:HTTPAdapter,選擇 HelloAdapterProj 作為工程,在適配器類型中選擇 HTTP Adapter,點(diǎn)擊確認(rèn),完成 HTTP 適配器創(chuàng)建
 
HTTP 適配器的內(nèi)容比 SQL 適配器略微復(fù)雜一些,除了上面介紹的 XML 和 JS 文件之外,還包括一個(gè) XSL 文件。XSL 文件將根據(jù)規(guī)則對(duì)獲取的內(nèi)容進(jìn)行轉(zhuǎn)化,去除不需要的數(shù)據(jù),改變數(shù)據(jù)的變量名,這樣可以對(duì)數(shù)據(jù)進(jìn)行先期的編輯。
清單 4. HTTP 適配器配置
- <connectivity>
 - <connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
 - <protocol>http</protocol>
 - <domain>localhost</domain>
 - <port>8081</port>
 - </connectionPolicy>
 - <loadConstraints maxConcurrentConnectionsPerNode="2" />
 - </connectivity>
 - <procedure name="getLeauge"/>
 
清單 5. HTTP 適配器方法實(shí)現(xiàn)
- function getLeauge(id) {
 - var input = {
 - method : 'get',
 - returnedContentType : 'xml',
 - path : "axis2/services/LeagueService/getLeague",
 - parameters : {
 - 'index' : id,
 - },
 - transformation : {
 - type : 'xslFile',
 - xslFile : 'filtered.xsl'
 - }
 - };
 - return WL.Server.invokeHttp(input);
 - }
 
在 XML 文件中配置 WS 的地址,定義提供的方法;然后在 JS 中調(diào)用 WL.Server.invokeHttp 方法完成對(duì)后臺(tái)數(shù)據(jù)的提取和轉(zhuǎn)化。雖然 JS 方法的參數(shù)只有一個(gè),但是參數(shù)是 JSON 格式,其中包含著單獨(dú)的變量,其定義如下:
- method: 確認(rèn)調(diào)用的方法是 get 還是 post
 - returnedContentType: HTTP 服務(wù)返回的數(shù)據(jù)格式,這影響到應(yīng)用解析數(shù)據(jù)的方式
 - returnedContentEncoding: 返回?cái)?shù)據(jù)格式的編碼格式,默認(rèn)為 utf-8
 - path: 指定 WS 的路徑和方法
 - headers: HTTP 請(qǐng)求的頭
 - cookies: HTTP 請(qǐng)求的 cookie 信息
 - body: 在方法是 post 時(shí)編寫(xiě)相應(yīng)的內(nèi)容
 - transformation: 轉(zhuǎn)化數(shù)據(jù)的參數(shù),包括 XSL 文件的位置和轉(zhuǎn)化的格式
 
數(shù)據(jù)的轉(zhuǎn)化
在 第 1 節(jié) 中描述了適配器和前臺(tái)的 app 以及后臺(tái)系統(tǒng)間數(shù)據(jù)交互的過(guò)程,在第三步中,如果開(kāi)發(fā)者定義了相應(yīng)的 XSL 文件,那么轉(zhuǎn)化成 JSON 格式的過(guò)程就要分為兩步:
- 服務(wù)器首先將后臺(tái)的數(shù)據(jù)轉(zhuǎn)化為 XML 格式
 - 通過(guò) XSL 文件轉(zhuǎn)化后,再將數(shù)據(jù)轉(zhuǎn)化為 JSON 格式
 
清單 6. XSL 文件轉(zhuǎn)化
- <xsl:template match="/">
 - {
 - Items: [
 - <xsl:for-each select="//*[local-name() = 'return']">
 - {
 - ranking: '<xsl:value-of select="*[local-name() = 'ranking']"/>',
 - name: '<xsl:value-of select="*[local-name() = 'name']"/>',
 - round: '<xsl:value-of select="*[local-name() = 'round']"/>',
 - win: '<xsl:value-of select="*[local-name() = 'win']"/>',
 - draw: '<xsl:value-of select="*[local-name() = 'draw']"/>',
 - lose: '<xsl:value-of select="*[local-name() = 'lose']"/>'
 - },
 - </xsl:for-each>
 - ]
 - }
 - </xsl:template>
 
在示例中,由于 WS 是根據(jù) Axis2 框架編寫(xiě)的,所以返回的 XML 文件中的標(biāo)簽會(huì)有前綴,為了實(shí)現(xiàn)只對(duì)其中部分?jǐn)?shù)據(jù)的抽取,需要使用一定的 XSL 函數(shù)進(jìn)行匹配(在示例中使用了 local-name 函數(shù)),具體的 XSL 函數(shù)說(shuō)明參見(jiàn) XSL 文檔。
適配器的測(cè)試
在開(kāi)發(fā)平臺(tái)上,還提供了適配器的測(cè)試方法,為了測(cè)試適配器,需要按照如下方法進(jìn)行:
- 右擊適配器 -> Run As -> Deploy Worklight Adapter
 - 在部署成功后,右擊適配器 -> Run As -> Invoke Worklight Procedure
 
系統(tǒng)會(huì)顯示如下的測(cè)試環(huán)境,根據(jù)選取的方法和填入的參數(shù),完成對(duì)適配器的測(cè)試。
圖 2. 適配器測(cè)試界面

從返回的數(shù)據(jù)可以看到,如果測(cè)試成功,那么系統(tǒng)將返回完整的結(jié)果集(resultSet)并且將 isSuccessful 標(biāo)志位置為 true;如果失敗,在 errors 信息位中,有詳細(xì)的信息,并且 isSuccessful 標(biāo)志位置為 false。
在示例中,在 WS 沒(méi)有啟動(dòng)的情況下,測(cè)試系統(tǒng)會(huì)得到服務(wù)器不可連接的錯(cuò)誤;而當(dāng)設(shè)置的 XSL 規(guī)則有問(wèn)題時(shí),則會(huì)提示描述解析 XML 文件發(fā)生錯(cuò)誤。
圖 3. 適配器測(cè)試錯(cuò)誤(連接)
 
圖 4. 適配器測(cè)試錯(cuò)誤(解析)

從上面的描述可以看出,適配器的測(cè)試是易于實(shí)現(xiàn)的,并且在平臺(tái)的支持下,提供了詳細(xì)的調(diào)試信息,但是單純的適配器只能返回?cái)?shù)據(jù),為了展示返回的數(shù)據(jù),必須和客戶端的應(yīng)用集成,所以接下來(lái)的示例將描述應(yīng)用和適配器集成的方法。
適配器的集成
為了訪問(wèn)部署在服務(wù)器上的適配器,應(yīng)用可以通過(guò)Worklight 提供的 JS 函數(shù)來(lái)訪問(wèn)也可以通過(guò)Worklight 支持的本地語(yǔ)言來(lái)訪問(wèn)。在 JS 函數(shù)情況下方法的參數(shù)分為兩個(gè)部分:
- invocationData: 調(diào)用適配器需要的參數(shù)
    
- adapter 表示適配器的名稱
 - procedure 表示適配器中的函數(shù)名稱
 - parameters 表示傳遞給適配器的參數(shù)
 
 - 回調(diào)函數(shù) : 當(dāng)調(diào)用適配器成功或者失敗時(shí),需要提供相應(yīng)的回調(diào)函數(shù)。onSuccess 函數(shù)在調(diào)用適配器成功時(shí)使用,onFailure 函數(shù)在調(diào)用適配器失敗時(shí)使用
 
清單 7. 客戶端對(duì)于適配器調(diào)用
- function wlCommonInit() {
 - busyIndicator = new WL.BusyIndicator("AppBody");
 - $('nationList').observe('change', nationSelectionChange);
 - getNationList();
 - }
 - function getNationList() {
 - busyIndicator.show();
 - var invocationData = {
 - adapter : 'MySQLAdapter',
 - procedure : 'getAllCountries',
 - parameters : []
 - };
 - WL.Client.invokeProcedure(invocationData, {
 - onSuccess : getNationListSuccess,
 - onFailure : getNationListFailure
 - });
 - }
 
當(dāng)應(yīng)用啟動(dòng)時(shí),系統(tǒng)調(diào)用 wlCommonInit 函數(shù),通過(guò)對(duì) SQL 適配器的訪問(wèn),調(diào)用存儲(chǔ)過(guò)程,當(dāng)用戶從選擇項(xiàng)中,確認(rèn)一個(gè)數(shù)據(jù)后,調(diào)用 SQL 語(yǔ)句獲取結(jié)果。為了程序流程的簡(jiǎn)潔,應(yīng)用只和 SQL 適配器交互,但是當(dāng)需要調(diào)用 HTTP 適配器時(shí),在 SQL 適配器中定義了和 HTTP 適配器交互的代碼。
上述流程帶來(lái)了好處是:因?yàn)?HTTP 適配器和 SQL 適配器均部署在服務(wù)器端,所以在訪問(wèn) HTTP 適配器時(shí),不涉及客戶端應(yīng)用和 HTTP 適配器的交互,減少訪問(wèn)的流量;同時(shí)暴露的接口也只有 SQL 適配器,方便了管理;更為重要的是,適配器可以利 cache 功能,儲(chǔ)存相應(yīng)的數(shù)據(jù),減少系統(tǒng)的負(fù)載。
通過(guò)本地程序訪問(wèn)適配器時(shí),根據(jù)語(yǔ)言的不同,實(shí)現(xiàn)的方式也不同,在 Java 中需要繼承 WLResponseListener 接口,在 Objective-C 中需要繼承 WLDelegate 協(xié)議,詳細(xì)的過(guò)程可以在開(kāi)發(fā)相應(yīng)的程序時(shí)查看Worklight 的開(kāi)發(fā)文檔。
結(jié)束語(yǔ)
適配器作為Worklight 平臺(tái)的一個(gè)關(guān)鍵模式,不但方便了開(kāi)發(fā)者將新的應(yīng)用和原有的系統(tǒng)集成,還為移動(dòng)應(yīng)用的開(kāi)發(fā)提供了統(tǒng)一的數(shù)據(jù)接口?;谶@個(gè)模式,程序員可以將關(guān)鍵的流程處理放在服務(wù)器端,對(duì)于客戶端只暴露接口,提供輸入和輸出的定義,這符合編程模式中服務(wù)優(yōu)先的原則。
適配器還提供了兩種很重要的功能:
1. 適配器保護(hù)功能,在安全配置上,對(duì)重要的適配器方法進(jìn)行保護(hù),這樣訪問(wèn)相應(yīng)的方法就需要進(jìn)行用戶驗(yàn)證;
2. 適配器驗(yàn)證功能,在安全驗(yàn)證上,原有的系統(tǒng)可能有各種驗(yàn)證方式,使用適配器可以在服務(wù)器端編程,用于和原系統(tǒng)的驗(yàn)證模式結(jié)合,這樣就可以適應(yīng)各種不同的驗(yàn) 證方式。















 
 
 






 
 
 
 