现 在已经到了用 Ajax 技术开发三个应用程序层的最后阶段。我们要使用 Zend Core 和 Eclipse IDE 工具构建、部署和测试应用程序。还要研究 MySQL、PHP、Ajax(XHTML、CSS、JavaScript、XHR)、REST、JSON、XML 和一个 Web 服务客户机。
您 可能会注意到,提供特定银行出纳员功能的所有 HTML 表单都将用户数据发送给一个基于 REST 的中间层服务。我们将在另一个 PHP 模块中实现这个服务,这个模块接受银行出纳员的浏览请求,包括存款、取款和当前股票组合价值查询。这个 PHP 模块作为银行操作的请求分配器,它解析银行出纳员的请求并调用适当的 Bank Logic 函数以处理这个请求。您会看到用 PHP 编写这样的 REST 服务是多么容易。
在开发用于 REST 请求分派的 PHP 服务之后,我们的重点将转到一个可以通过互联网访问的基于 .NET 的 Web 服务。这个免费的 Web 服务提供给定股票的当前价格。我们将在 PHP 中间层中开发一个 Web 服务客户机,这样就可以从基于 PHP 的银行操作请求分配器远程调用这个 Web 服务,从而获得股票的报价。然后,银行操作请求分配器将用当前股票价格调用 Bank Logic PHP 模块,计算出给定帐户持有人的当前股票组合价值。您将学习使用 SOAP 访问 Web 服务的技术,还要学习 XML 和 JavaScript Object Notation(JSON)这两种流行的数据交换格式。
Web 服务 是一个描述一组操作的接口,可以用 XML 和 JSON 等标准化消息格式通过网络访问这些操作。Web 服务主要用于程序之间的交互。Web 服务使应用程序可以更快速、轻松而且成本低廉地集成起来。通常在协议栈的较高层,基于以业务服务语义为中心的消息进行集成。这样就可以在企业内外以通用的 方式对业务功能进行松散的集成。
实现通用 Web 服务模型的关键是一组标准。比较重要的标准包括 HTTP、SOAP、REST、XML、JSON 和 WSDL(Web Services Description Language)。
在 WSDL 服务接口中,类型定义服务消费者和服务提供者之间的交互所用的定制 XML 数据类型。消息指定在服务消费者和服务提供者之间传输的有效负载的各个部分的 XML 数据类型。操作指定在 Web 服务交互的输入和输出中可以出现哪些消息。端口类型定义允许的 Web 服务操作。最后,绑定描述协议和数据格式。
银 行出纳员操作之一是获得给定的帐户持有人的当前股票组合价值。当银行出纳员从 Web 页面上选择这个选项时,一个 HTTP 请求被发送到中间层 PHP 服务。在调用 Bank Logic PHP 模块计算股票组合的总价值之前,需要将当前股票价格作为参数发送给它。为了获得帐户持有人股票组合中一只股票的当前股票价格,需要访问互联网上的一个远程 Web 服务。
为了访问远程 Web 服务,必须使用 WSDL 生成一个 Web 服务客户机代理。在 PHP 中,只需几行代码就可以完成这个任务。通过使用 PHP SOAP Client 库,就可以从服务提供者提供的 WSDL 动态地生成 Web 服务客户机。在这个示例中,我们要使用 WebserviceX.net 在互联网上免费提供的一个基于 .NET 的股票报价服务。您应该访问 参考资料 一节中提供的 WebServiceX.net 链接,花点儿时间研究这个远程股票报价服务的 WSDL 文件。
按照以下步骤用 WebserviceX.net 上发布的股票报价服务 WSDL 文件在 PHP 中创建一个 Web 服务客户机:
/* ============================================================ Project: End-to-End-Ajax application development
Purpose: This is an example scenario to be used in an IBM developerWorks article.
Last modified: May/17/2007.
This PHP module provides the logic to access a remote Web service hosted on the Internet. This Web service will return the complete and current stock details about a given stock ticker symbol.
This module shows how easy it is to consume a remote Web Service (running on a .NET platform) using PHP dynamically by pointing to a remote WSDL file for that Web service.
It uses PHP SOAP Client library to accomplish that. ============================================================ */ // Get the required Bank Logic PHP module. require_once("BankLogic.php");
This function accepts an account holder name as input. In this scenario, all the account holders have a single stock in their respective portfolio. The code here obtains the ticker symbol of the stock held by the given account holder. Then it uses PHP SOAP client to remotely invoke the .NET based stock quote Web service and obtains the XML-based response returned by that service. It parses the XML-based stock details response and gets the current stock price of that stock. It then calls another function inside of the Bank Logic PHP module to update the database with the new portfolio value. ============================================================ */ function getStockPortfolioValue($accountHolderName) { // From the Bank Logic module, obtain all the account // information stored in the database. // [If someone wants to optimize it, a new function // could be written directly to return the account // information for the given account holder, instead of // retrieving all the account information.] $finalResult = getAllAccountInformation();
// Ensure that all the account information was read from the DB. if ($finalResult["ResultCode"] != 0) { // Some error occurred. return now. return($finalResult); } // End of if ($finalResult["ResultCode"] != 0)
// Get the account info of the customer whom we are dealing with now. $accountInfoArray = $finalResult["AccountInfo"]; $tickerSymbol = null;
// Loop through all the available account information and filter // the account information for the account holder's name that was // passed as a parameter to the function we are in now. foreach ($accountInfoArray as $accountInfo) { // Is it the account for the person of interest? if (strcasecmp($accountInfo["AccountHolderName"], $accountHolderName) == 0) { // Get the stock ticker symbol of the only stock held in this portfolio. $tickerSymbol = $accountInfo["StockName"]; // We got it. Skip out of the loop now. break; } // End of if (strcasecmp($accountInfo["AccountHolderName"] ... } // End of foreach ($accountInfoArray as $accountInfo)
// If there was an error in getting the ticker symbol, return now. if ($tickerSymbol == null) { $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "Unable to get ticker symbol from the account of $accountHolderName."; return($finalResult); } // End of if ($tickerSymbol == null)
// This is the WSDL published by the service provider. $wsdl = "http://www.webservicex.net/stockquote.asmx?WSDL"; // The magic happens here in the next four lines to execute // a fairly complex function remotely over the Internet. // It is really cool. Why can't the other middleware runtimes follow // the simplicity of PHP to do such things. // Instantiate the PHP SOAP client library by pointing directly to // remote WSDL URL. $proxy = new SoapClient($wsdl, array("trace"=>1,"exceptions"=>0)); // Set the required input parameter for the remote service. // In our case, it is just the ticker symbol. $param['symbol'] = $tickerSymbol; // Execute the remote logic. It is that simple. $result = $proxy->GetQuote($param); // You have the XML-based response string now. $quoteResult = $result->GetQuoteResult;
// Convert the XML formatted string into a DOM object. // PHP also does XML processing so elegantly and simply. $xml = simplexml_load_string($quoteResult); $stockPrice = 0.0;
// The XML-based stock quote response is somewhat elaborative. // It contains information about different aspects of the given stock. // We are interested only in the current market price of that stock. // That information is available as: // ...56.34... // All we have to do is just parse the value of the XML element. // See for yourself how easy it is to do this in PHP. if (property_exists($xml, "Stock") == true) { // We have the element in the Web service response. $stockInfo = $xml->Stock;
// Now, check if the element contains a child named . if (property_exists($stockInfo, "Last")) { // Just retrieve the last market price of this stock. $stockPrice = $stockInfo->Last; } else { $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "Unable to get current stock price of " . "$accountHolderName's stock $tickerSymbol."; return($finalResult); } // End of if (property_exists($stockInfo, "Last")) } else { $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "Unable to get current stock price of " . "$accountHolderName's stock $tickerSymbol."; return($finalResult); } // End of if (property_exists($xml, "Stock") == true)
// Now that we have the current stock price, compute the // portfolio value and update it in the DB. // Return the result to the caller of this function. $finalResult = portfolioValue($accountHolderName, $stockPrice); return($finalResult); } // End of function getStockPortfolioValue ?>
PHP Web 服务客户机中的逻辑
前 一节中创建 Web 服务客户机的过程只是 PHP SOAP 客户机库支持的简单方式之一。GetStockPrice.php 中的逻辑让 Web 服务客户机访问 URL http://www.webserviceX.net 上的股票报价服务。PHP SOAP 客户机库直接指向服务提供者发布的 WSDL URL,并使用 WSDL 中的服务描述信息在内存中动态地创建一个内部服务代理。然后,代理代码进行远程调用;因此,对于客户机而言,股票报价功能就像是在本地一样。在这个示例 中,客户机代理使用 SOAP 访问协议与远程股票报价服务进行交互,它发送一个股票代号作为输入,然后接收股票报价服务输出的一个 XML 文档。
GetStockPrice.php 中的逻辑很简单,它只包含一个函数:getStockPortfolioValue。这个 PHP 模块需要通过 BankLogic.php 模块让这里的数据库访问函数可用。getStockPortfolioValue 函数接受一个帐户持有人姓名参数,并为这个帐户计算和更新股票组合价值。它获取数据库中存储的所有帐户信息,并过滤出属于给定帐户持有人的信息。可以不获 取所有帐户的信息,而是只获取特定帐户持有人的帐户信息。(这个修改留给您作为练习。)在过滤后的帐户信息中,获得股票组合中一只股票的代号。然后,实例 化一个 PHP SoapClient 对象,并将股票报价服务 WSDL URL 作为参数传递给类构造器。股票代号作为这个 Web 服务的输入参数。然后调用代理上的 GetQuote 方法,这个方法会对远程 Web 服务进行 SOAP 调用。调用成功时,Web 服务返回一个 XML 格式的字符串。PHP 提供了将 XML 字符串转换为 XML DOM 对象结构的简便方法。在这个 XML 树中, 元素包含股票当前的市场价格。解析 的值。最后,调用 Bank Logic PHP 模块中的 portfolioValue 函数,并以帐户持有人姓名和当前股票价格作为参数。这个函数使用当前股票价格计算新的股票组合价值,更新数据库并返回一个关联数组,其中包含以前的股票组合价值和当前的价值。这个关联数组也返回给 getStockPortfolio 函数的调用者。
远程股票报价服务的结果是一个 XML 文档,其中包含关于指定的股票代号的许多信息。清单 3 给出股票报价服务对于股票代号 IBM 的输出示例。输出的 XML 包含许多信息,比如报告股票价格的日期和时间、开盘价和收盘价、价格变化的百分比、本交易日的最高价和最低价、成交量、一年内的价格波动范围、每股收入和 股票的 P/E 比率。在所有这些信息当中,我们只关心最近的价格,这一信息包含在 XML 元素中。getStockPrice 方法中其余的代码使用 DOM 解析器对 Web 服务的整个 XML 结果进行解析。
清单 3. 远程股票报价 Web 服务的 XML 响应示例
IBM 101.17 4/27/2007
+0.27 100.30 101.17 100.06 6141359 152.3B 100.90 +0.27% 72.73 - 101.17 6.262 16.11 INTL BUSINESS MAC
/* ============================================================ Project: End-to-End-Ajax application development
Purpose: This is an example scenario to be used in an IBM developerWorks article.
Last modified: May/17/2007.
This module is the REST service request handler for the server-side of the bank scenario. It receives the REST (HTTP POST) requests from the single-page based bank teller browser clients. It dispatches different request requests to different bank logic functions. It also sends and receives application-specific data in a previously agreed JSON format. ============================================================ */ // Get the other required PHP modules. // JSON.php is an open source JSON parser in PHP. // It can be obtained from: : http://mike.teczno.com/JSON/JSON.phps require_once("JSON.php"); // Bank Logic module has all the core bank teller server-side functions. require_once("BankLogic.php"); // GetStockPrice is a Web service proxy client to a remote Web service. require_once("GetStockPrice.php");
// Define all the constants that will be used here. define ("BANK_TELLER_COMMAND_KEY", "Bank_Teller_Command"); define ("BANK_TELLER_POST_DATA_KEY", "Post_Data"); define ("GET_ALL_ACCOUNTS_INFO", "Get_All_Accounts_Info"); define ("DEPOSIT_TO_ACCOUNT", "Deposit_To_Account"); define ("DEBIT_FROM_ACCOUNT", "Debit_From_Account"); define ("GET_STOCK_PORTFOLIO_VALUE", "Get_Stock_Portfolio_Value");
// XHTML form data from the bank teller Ajax browser client will be // available in one of the PHP superglobals // i.e. $_REQUEST associative array. // Each REST request from the client will have two key-value pairs. // First one is the bank teller command that tells if the client // wants to perform deposit, debit, portfolio value etc. // The second key-value pair is the application specific data sent as // JSON-formatted string. $bankTellerCommand = $_REQUEST[BANK_TELLER_COMMAND_KEY]; $bankTellerPostData = $_REQUEST[BANK_TELLER_POST_DATA_KEY];
// Using JSON in PHP is as easy as it can get. // Instantiate the JSON parser (available through JSON.php) $json = new Services_JSON(); // In one statement, convert the JSON formatted text to // an equivalent PHP class structure. That is it. $bankTellerInput = $json->decode($bankTellerPostData); $responseToBeSent = ""; $bankActionResult = null;
// Switch through the bank teller command issued by the browser client. switch($bankTellerCommand) { case GET_ALL_ACCOUNTS_INFO: // Go and get all the account information stored in the database. $bankActionResult = getAllAccountInformation(); break;
case DEPOSIT_TO_ACCOUNT: // Perform the deposit account transaction. // Pass the account holder name and the deposit amount as // sent by the browser client. Third parameter of 1 indicates DEPOSIT. // This function is available in BankLogic.php $bankActionResult = accountTransaction( $bankTellerInput->accountHolderName, $bankTellerInput->amount, 1); break;
case DEBIT_FROM_ACCOUNT: // Perform the debit account transaction. // Pass the account holder name and the debit amount as // sent by the browser client. Third parameter of 2 indicates DEBIT. // This function is available in BankLogic.php $bankActionResult = accountTransaction( $bankTellerInput->accountHolderName, $bankTellerInput->amount, 2); break;
case GET_STOCK_PORTFOLIO_VALUE: // Perform the "Update Stock Portfolio Value" transaction. // This will require going out on the Internet to a remote Web service. // This function is available in GetStockPrice.php $bankActionResult = getStockPortfolioValue($bankTellerInput->accountHolderName); break;
default: // Invalid Bank teller command. $bankActionResult["ResultCode"] = 1; $bankActionResult["ResultMsg"] = "Invalid Teller Command"; } // End of switch($bankTellerCommand)
// We completed everything that the client asked us to do. // Let us return the final results in JSON formatted text. // It takes one line of code in PHP to convert a // PHP associative array data structure into JSON formatted text. $responseToBeSent = $json->encode($bankActionResult); // Set the HTTP header to indicate that we are sending JSON plain text data. // There are also efforts underway at this time (MAY/2007) to define a // new content type called text/json. header("Content-Type: text/plain"); // That is it. Return the JSON formatted response in // RESTful way back to the browser now. echo($responseToBeSent); ?>
集成银行场景组件
现在,已经开发了银行场景所需的 Ajax 客户机、PHP 中间层服务和 MySQL 数据库组件,如下所示:
BankDB(基于 MySQL 的银行数据库)
BankLogic(一个 PHP 模块中银行出纳员函数的代码逻辑)
BankPortal(使用 Ajax 技术的银行出纳员 Web 客户机)
BankActions(用 PHP 编写的银行出纳员操作 REST 服务)
Stock Quote Web 服务客户机(使用 PHP SOAP Client 库的 SOAP 代理)
这 些组件分别处理银行场景中的一项任务。正如在这个系列中看到的,每个组件依赖于其他一些组件,从而连接成银行场景的端到端体系结构。在典型的三层 Web 应用程序中,为了集成各个应用程序组件,.NET 这样的底层框架需要详细配置依赖项。但是,用于实现这个场景的技术不需要这么复杂的集成过程。在实现这些组件时,已经考虑到了必需的所有依赖项。Ajax (XHTML、CSS、JavaScript、REST、JSON)、PHP 或 MySQL 都不需要应用程序特有的运行时集成,因此大大减少了端到端应用程序的开销和复杂性。
部署银行场景组件
既然已经开发并集成了各个银行场景组件,就该在 Zend Core PHP 服务器上部署它们了。请记住,Zend Core 是在 Apache Web 服务器中运行的。
在 本系列的三篇文章 中,我们研究了开发端到端 Ajax 应用程序的各个阶段。本系列的目标之一是让您体会一下 Ajax Web 应用程序提供的许多改进。下一节列出了一部分改进。如果您能够通过我们的端到端 Ajax 应用程序体会到它们的效果,本系列的目标就实现了。
要点
在这个 Ajax 应用程序中,需要单击链接并等待页面刷新吗?不需要。
由于 Web 应用程序中的所有客户端逻辑都放在本地,所以实现了快速响应。
在单击 Deposit、Debit 和 Get Stock Portfolio Value 按钮时,这个应用程序与 PHP 中间层服务器进行通信,交换少量数据。(这会显著改进网络带宽利用率。)
在使用这个应用程序时,用户看起来是在不同的页面之间导航。但实际上,只有一个 HTML 页面,只需隐藏和显示这个页面中的不同 DIV 元素,就可以实现页面切换效果。
以这种 Ajax 风格设计的 Web 应用程序可以在浏览器中实现丰富的用户交互方式。这会改进客户和用户的满意度。
采用这种体系结构的 Web 应用程序还会减少中间层服务器上的负载,因为服务器不再需要处理不必要的 HTML 页面刷新。相反,应用程序只在需要数据交换时才会联系服务器。
在操作这个应用程序时,您看到浏览器 URL 发生变化了吗?它没有变,整个应用程序只有这一个 URL。这说明它是一个单页面浏览器应用程序。表示内容(HTML、CSS 和 JavaScript)只在启动时从 Web 服务器下载一次。
结束语
developerWorks Ajax resource center 在 Ajax 资源中心 可以找到关于 Ajax 编程模型的各种信息,包括文章、教程、论坛、blog、wiki、活动和新闻。
您 刚刚开发了一个跨越三层(客户机层、中间层和数据层)的 Ajax 应用程序。我们使用几种开放源码技术以构建和部署这个应用程序。在这个过程中,使用了 Zend Core 的一些核心特性和一些 Eclipse IDE 工具。我们讨论了 MySQL、PHP、Ajax(XHTML、CSS、JavaScript、XHR)、REST、JSON、XML 和 Web 服务客户机的概念。还了解了 Eclipse IDE 中 PHP 和 Aptana 透视图的使用方法,学习了如何用 Zend Core/Apache 服务器进行部署和测试。本文提供了一个可下载的文件(见 下载),其中包含本文中使用的 JavaScript 客户端代码、PHP Web 服务客户机、PHP REST 请求分配器和股票报价服务 WSDL。
这个银行场景覆盖了 Ajax、PHP 和 MySQL 的基本概念。掌握了本系列提供的基本知识之后,可以继续学习这些强大的开放源码技术,用它们进行更高级的 Web 应用程序开发。
总 之,本系列演示了 Zend Core 和 Eclipse(PDT 和 Aptana)开发环境的强大功能。对于中小型企业而言,它们是非常可靠的技术组合,它们可以作为开放源码中间件,还可以帮助企业从 .NET 环境迁移到开放源码环境。还可以通过这些运行时环境和基于 Eclipse 的工具对厂商提供的商业软件和服务进行扩展。最后,由于 Zend 和 Aptana 的开发人员在 Zend Core、Eclipse PDT 和 Aptana Web IDE 项目上的努力,这种新的 Web 开发方式得到了简化。
回页首
下载
名字
大小
下载方法
wa-aj-end2end3.zip
28KB
HTTP
关于下载方法的信息
参考资料
学习
您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
阅读 “端到端 Ajax 应用程序开发” 系列中的其他文章。
阅读 Jesse James Garrett 所写的 “Ajax: A New Approach to Web Applications”,他在这篇文章中首创了 Ajax 这个词。
阅读 IBM developerWorks Ajax 资源中心 中关于 Ajax 的文章和教程。
阅读 IBM developerWorks PHP 资源中心 中关于 PHP 的文章和教程。
阅读 PHP Manual Web 站点 上的 PHP API 文档。
了解 Eclipse PDT 项目。
了解 Aptana Web IDE。
了解 Zend Core。
阅读关于 XMLHttpRequest 的 W3C Working Draft。
阅读 PHP MySQLi API 文档。
通过 CSS Zen Garden 中令人惊异的演示,体会 CSS 的强大功能。
了解 JSON 的所有相关信息。
“用 PHP 将 XML 转换成 JSON”(Senthil Nathan、Edward J Pring 和 John Morar,developerWorks,2007 年 1 月):学习在 PHP 中如何将 XML 转换为 JSON。
阅读 Roy Fielding 关于 REST 的研究报告。
在 WebserviceX.net 上查看远程股票报价 Web 服务的 WSDL 文件。
浏览 PEAR 上的开放源码 PHP 库。
获得产品和技术
从 Mozilla 下载 Web 站点 下载 Mozilla Firefox 浏览器。
从 MySQL 下载 Web 站点 下载 MySQL 数据库服务器。
从 Zend 下载 Web 站点 下载 Zend Core。
从 Zend PDT 下载 Web 站点 下载 Eclipse PDT(PHP Development Tools)。
从 MySQL 连接器下载 Web 站点 下载 MySQL PHP 连接器。
从 JSON Web 站点 下载 Douglas Crockford 编写的 JavaScript JSON 解析器。
下载 PHP JSON 解析器。
关于作者
Senthil Nathan 是位于纽约 Hawthorne 的 IBM T.J. Watson Research Center 的一位高级软件工程师。在为不同类型的企业应用程序构建软件方面,他有 22 年经验。他当前感兴趣的领域包括 SOA、Web 服务、Java 2 Platform, Enterprise Edition(J2EE)、PHP、Ruby On Rails、Web 2.0 和 Ajax 开发。
访问权限是java中一个比较中要的知识点,它规定者什么方法可以访问,什么不可以访问
一:包访问权限;
自定义包:
package com.wj.control;
//包
public class Demo {
//定义一个无参的方法
public void DemoPackage(){
System.out.println("调用
用户自定义聚合函数,用户提供的多个入参通过聚合计算(求和、求最大值、求最小值)得到一个聚合计算结果的函数。
问题:UDF也可以提供输入多个参数然后输出一个结果的运算,比如加法运算add(3,5),add这个UDF需要实现UDF的evaluate方法,那么UDF和UDAF的实质分别究竟是什么?
Double evaluate(Double a, Double b)
在利用tomcat-redis-session-manager做session同步时,遇到了在session保存一个自定义对象时,修改该对象中的某个属性,session未进行序列化,属性没有被存储到redis中。 在 tomcat-redis-session-manager的github上有如下说明: Session Change Tracking
As noted in the &qu
关于Table Driven Approach的一篇非常好的文章:
http://www.codeproject.com/Articles/42732/Table-driven-Approach
package com.ljn.base;
import java.util.Random;
public class TableDriven {
public