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

圖 1 描述了適配器和前臺的 app 以及后臺系統間數據交互的過程,具體的流程如下:
- 適配器暴露一系列的方法,稱之為 procedures,前臺 app 通過 Ajax 方式調用這些過程。
- 這些方法從后臺獲取相應信息。
- 后臺系統返回的數據格式如下
- 如果格式為 JSON,Worklight服務器直接返回。
- 如果格式不是 JSON,服務器將數據格式轉化為 JSON 后,再返回。
- 這些方法將獲得的 JSON 數據進行業務處理,將最終結果返回給 app。
基于 Eclipse 的適配器開發
根據適配器連接的后臺種類,它被分為 HTTP 適配器和 SQL 適配器。在實際的應用中 SQL 適配器用于連接不同的后臺數據庫,并通過 SQL 語句或存儲過程進行訪問。適配器目前支持 MySQL 和 Oracle 的數據庫,并且在幾個月后將支持 DB2 數據庫。HTTP 適配器支持訪問 soap 服務和 rest 服務,并且可以根據指定的 XSL 文件對獲得的數據片段進行轉化,獲得用戶需要的數據和格式。
為了更好的演示不同適配器的作用和相互合作的方法,文章將構建如下場景:
- 首先創建 SQL 適配器,并且提供對于 SQL 語句和存儲過程的訪問。
- 然后創建 HTTP 適配器,用于訪問由 Axis2 框架構建的 Web Service。
- 通過 Eclipse 提供的測試環境,對于前面創建的適配器進行測試,并且解釋測試返回信息的含義。
- 最后通過開發一個應用,并且整合上述的適配器,完成一個完整的功能。
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 下開發適配器通過下面的步驟可以完成:
新建工程和 SQL 適配器:
- 點擊 File-> New -> Project -> Worklight -> Worklight Project
- 輸入工程名稱:HelloAdapterProj,點擊確認,完成工程創建
- 點擊 File-> New -> Other -> Worklight -> Worklight Adapter
- 輸入應用名稱:MySQLAdapter,選擇 HelloAdapterProj 作為工程,在適配器類型中選擇 SQL Adapter,點擊確認,完成 SQL 適配器創建
創建完畢的適配器其文件結構如下:
- MySQLAdapter.xml:在文件中定義了很多的屬性,比如適配器在Worklight 控制臺中顯示的名稱和簡單描述,最大的連接數等等,其中最重要的是下列兩個參數:
- dataSourceJNDIName 定義了適配器所連接的 JNDI 的名稱,具體的配置將在下面敘述
- procedure 定義適配器向外提供的方法名稱(需要通過 .js 文件實現)
- MySQLAdapter-impl.js:在文件中實現了兩個方法,getAllCountries 用于調用數據庫中的存儲過程;getOneCountryID 通過調用 SQL 語句獲取相應的信息
對于適配器需要的 JNDI 參數,可以通過修改 Worklight 的配置文件實現。在 Worklight 框架中存在一個默認的配置文件 default.worklight.properties,用戶可以通過修改 worklight.properties 文件來覆蓋默認配置文件中的一些屬性。清單 1 描述了為連接本地的 SQL 數據庫,在 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 屬性的參數均為Worklight 保留的參數,適配器部署到服務器后,系統會讀取 XML 文件中的 dataSourceJNDIName 屬性,然后根據 JNDI 配置的信息來建立數據庫的連接。
清單 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 適配器方法實現
- 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 文件中,函數通過調用 WL.Server.invokeSQLStoredProcedure 方法執行后臺數據庫提供的存儲過程,調用 WL.Server.invokeSQLStatement 方法實現了對 SQL 語句的執行,方法需要的參數和函數的具體說明,用戶可以在Worklight 提供的開發者手冊上了解它的詳細使用過程。
HTTP 適配器
建立 HTTP 適配器的過程類似 SQL 適配器:
- 點擊 File-> New -> Other -> Worklight -> Worklight Adapter
- 輸入應用名稱:HTTPAdapter,選擇 HelloAdapterProj 作為工程,在適配器類型中選擇 HTTP Adapter,點擊確認,完成 HTTP 適配器創建
HTTP 適配器的內容比 SQL 適配器略微復雜一些,除了上面介紹的 XML 和 JS 文件之外,還包括一個 XSL 文件。XSL 文件將根據規則對獲取的內容進行轉化,去除不需要的數據,改變數據的變量名,這樣可以對數據進行先期的編輯。
清單 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 適配器方法實現
- 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 中調用 WL.Server.invokeHttp 方法完成對后臺數據的提取和轉化。雖然 JS 方法的參數只有一個,但是參數是 JSON 格式,其中包含著單獨的變量,其定義如下:
- method: 確認調用的方法是 get 還是 post
- returnedContentType: HTTP 服務返回的數據格式,這影響到應用解析數據的方式
- returnedContentEncoding: 返回數據格式的編碼格式,默認為 utf-8
- path: 指定 WS 的路徑和方法
- headers: HTTP 請求的頭
- cookies: HTTP 請求的 cookie 信息
- body: 在方法是 post 時編寫相應的內容
- transformation: 轉化數據的參數,包括 XSL 文件的位置和轉化的格式
數據的轉化
在 第 1 節 中描述了適配器和前臺的 app 以及后臺系統間數據交互的過程,在第三步中,如果開發者定義了相應的 XSL 文件,那么轉化成 JSON 格式的過程就要分為兩步:
- 服務器首先將后臺的數據轉化為 XML 格式
- 通過 XSL 文件轉化后,再將數據轉化為 JSON 格式
清單 6. XSL 文件轉化
- <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 是根據 Axis2 框架編寫的,所以返回的 XML 文件中的標簽會有前綴,為了實現只對其中部分數據的抽取,需要使用一定的 XSL 函數進行匹配(在示例中使用了 local-name 函數),具體的 XSL 函數說明參見 XSL 文檔。
適配器的測試
在開發平臺上,還提供了適配器的測試方法,為了測試適配器,需要按照如下方法進行:
- 右擊適配器 -> Run As -> Deploy Worklight Adapter
- 在部署成功后,右擊適配器 -> Run As -> Invoke Worklight Procedure
系統會顯示如下的測試環境,根據選取的方法和填入的參數,完成對適配器的測試。
圖 2. 適配器測試界面

從返回的數據可以看到,如果測試成功,那么系統將返回完整的結果集(resultSet)并且將 isSuccessful 標志位置為 true;如果失敗,在 errors 信息位中,有詳細的信息,并且 isSuccessful 標志位置為 false。
在示例中,在 WS 沒有啟動的情況下,測試系統會得到服務器不可連接的錯誤;而當設置的 XSL 規則有問題時,則會提示描述解析 XML 文件發生錯誤。
圖 3. 適配器測試錯誤(連接)
圖 4. 適配器測試錯誤(解析)

從上面的描述可以看出,適配器的測試是易于實現的,并且在平臺的支持下,提供了詳細的調試信息,但是單純的適配器只能返回數據,為了展示返回的數據,必須和客戶端的應用集成,所以接下來的示例將描述應用和適配器集成的方法。
適配器的集成
為了訪問部署在服務器上的適配器,應用可以通過Worklight 提供的 JS 函數來訪問也可以通過Worklight 支持的本地語言來訪問。在 JS 函數情況下方法的參數分為兩個部分:
- invocationData: 調用適配器需要的參數
- adapter 表示適配器的名稱
- procedure 表示適配器中的函數名稱
- parameters 表示傳遞給適配器的參數
- 回調函數 : 當調用適配器成功或者失敗時,需要提供相應的回調函數。onSuccess 函數在調用適配器成功時使用,onFailure 函數在調用適配器失敗時使用
清單 7. 客戶端對于適配器調用
- 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
- });
- }
當應用啟動時,系統調用 wlCommonInit 函數,通過對 SQL 適配器的訪問,調用存儲過程,當用戶從選擇項中,確認一個數據后,調用 SQL 語句獲取結果。為了程序流程的簡潔,應用只和 SQL 適配器交互,但是當需要調用 HTTP 適配器時,在 SQL 適配器中定義了和 HTTP 適配器交互的代碼。
上述流程帶來了好處是:因為 HTTP 適配器和 SQL 適配器均部署在服務器端,所以在訪問 HTTP 適配器時,不涉及客戶端應用和 HTTP 適配器的交互,減少訪問的流量;同時暴露的接口也只有 SQL 適配器,方便了管理;更為重要的是,適配器可以利 cache 功能,儲存相應的數據,減少系統的負載。
通過本地程序訪問適配器時,根據語言的不同,實現的方式也不同,在 Java 中需要繼承 WLResponseListener 接口,在 Objective-C 中需要繼承 WLDelegate 協議,詳細的過程可以在開發相應的程序時查看Worklight 的開發文檔。
結束語
適配器作為Worklight 平臺的一個關鍵模式,不但方便了開發者將新的應用和原有的系統集成,還為移動應用的開發提供了統一的數據接口。基于這個模式,程序員可以將關鍵的流程處理放在服務器端,對于客戶端只暴露接口,提供輸入和輸出的定義,這符合編程模式中服務優先的原則。
適配器還提供了兩種很重要的功能:
1. 適配器保護功能,在安全配置上,對重要的適配器方法進行保護,這樣訪問相應的方法就需要進行用戶驗證;
2. 適配器驗證功能,在安全驗證上,原有的系統可能有各種驗證方式,使用適配器可以在服務器端編程,用于和原系統的驗證模式結合,這樣就可以適應各種不同的驗 證方式。
























