日期:2010-12-26  浏览次数:20936 次

程序员可能会经常碰到这样的事情:建立一个servlet应用程序,它与公司的数据库相连接,为客户提供一种特定的服务,这个应用程序受到一个强大的验证机制保护,全世界有成千上万的客户都在使用它。现在就出现了一个问题:当应用程序处在公司的防火墙之外时,你将如何从应用程序提供用户对数据库的访问?你知道,网络管理员是不会专门为你的应用程序与数据库相连接而打开一个特殊端口的。

HTTP隧道技术和XML
如何越过防火墙与客户/服务器应用程序相连接这个问题已经困扰程序员很久了。在多数情况下,一个公司的防火墙总是尽可能少地打开端口。一般情况下,你能够使用的唯一端口就是80,这也就是Web所使用的端口。

解决这个问题的方法就是使用HTTP隧道技术(HTTP tunneling)。这个方法首先将请求包装在一个HTTP POST请求中,然后这个请求再由一个在防火墙内的Web 服务器上的CGI 应用程序(例如一个servlet)来处理。

Servlet恢复原始的请求,执行它,然后将结果插入到HTTP响应流中。防火墙将这种相互作用解释为对一个 Web页面的常规请求,并允许对它继续进行处理。这就象特洛伊木马一样:看起来是一个普通的请求,但其中隐藏着预料不到的负载。

下一个问题是如何对请求进行格式化?当然,使用XML是将这些负载送入一个HTTP请求中去的最佳选择。这个观念很有独创性,HTTP之上的XML是一个热门的新兴领域,一些新的规则正在编写中,将来可以成为分布式应用程序的标准通讯协议。其中,简单对象访问协议(SOAP)是最得到公认的。

遗憾的是,现在还没有一个稳定执行的SOAP供我们使用,目前所能找到的最好的一个来自Apache集团,但是它只支持简单的返回类型。因此,它对于我们的项目是没有用的。但是这也不错,这意味着我们可以提出自己的HTTP上的XML的协议,并且借此来学习其中包含的概念。
概念
现在我们来建立一个简单的框架结构(framework),它将HTTP上的XML作为基本的通讯策略,从而让我们能够创建一套服务,而且使得这些服务从分布在Internet上四面八方的桌面应用程序都可以进行访问。

首先,我们需要建立普通请求和响应的语法。请求看起来是这样的:

<?xml version='1.0' encoding='utf-8' ?>
<http-request>
<requestType>
[type of request]
</requestType>
<request>
[Application specific request.This will be an XML Elment ]
</request>
</http-request>

响应看起来是这样的:

<?xml version='1.0' encoding='utf-8' ?>
<response>
<responseMessage>
[the response Message]
</responseCode>
<responseCode>
[an application specific return code.]
</responseCode>
<response>
[Application specific request.This will be an XML Element]
</response>
</http-response>

为了理解这个框架结构背后的概念,我们要编写一个应用服务例程:一个简单的数据库服务,它对任何SQL语句进行处理,并且将结果作为一个向量来返回。请求的细节,也就是请求元素所包含的XML标记非常简单,如下:

<sql-statement>
[The SQL statement to be executed]
</ sql-statement >
响应结果是这样的:
<result-set>
</result-count>
[the number of rows in the result set]
</result-count>
<row>
<col name='name'>
[the value]
</col>
?
</row>
?
<result-set>

HTTPService是一个servlet,它响应一个POST请求,恢复XML负载,并使用它来创建一个ServiceRequest例示。然后,根据请求的类型,将请求交给HttpServiceHandler 抽象类的一个特定子类。Handler类执行请求,在ServiceResponse的一个例示中存储结果,然后将这个结果发送回客户端应用程序。

通过按照惯例为服务处理器类命名,我们可以利用Java的映象功能来创建处理器类的一个例示,这个处理器类仅仅是建立在ServiceRequest 对象的服务类型属性的基础上。这就取消了HttpService和所有服务处理器类之间的依赖性,从而意味着当我们增加一个新服务时,不再需要改变 HttpService类。

在我们这个例子中,服务的类型是DBService,因此我们将创建HttpServiceHandler的一个子类,叫做DBServiceHandler。在HttpService中,我们使用以下代码:

String className = PACKAGE_NAME + "." + request.getRequestType() + "Handler";
HttpServiceHandler handler = Class.fromName(className).newInstance();
handler.handleRequest(request);

一个HttpServiceHandler子类需要执行一个方法processRequest(),它需要取一个ServiceRequest对象,然后返回一个ServiceResponse对象。这个方法是在handleRequest 方法的过程中由子类调用的:

Public void handleRequest(ServiceRequest request)
{
Serviceresponse response = processRequest(request);
SendResponse(response);
}

这是使用Template(模板)方法模式的一个典型例子,在这个过程中,抽象超类调用在一个子类中执行的方法。

ServiceRequest类将服务特定数据存储为一个XML 文档。这个类由访问者来设置和获取请求类型,它还有方法来处理请求的细节。getRequest()方法返回包含在请求标记中的XML节点,setRequest()方法则用一个新生成的请求来覆盖原来的请求。这个类还使用两个factory方法,创建新的元素和文本节点,允许开发人员生成新的请求。ServiceResponse类以一种非常简单的方法来处理请求的细节。

虽然这两个类允许我们对所有类型的请求和响应进行处理,但是开发人员也必须要了解每个请求的特殊语法。开发人员不能对请求的格式是否正确进行任何确认。

为了简化这个过程,我们将创建ServiceRequest和 ServiceResponse的子类,它们分别叫做DBServiceRequest和DBServiceResponse,它们都有处理服务特定细节的方法。例如,DBServiceRequest有设置和获取SQL 语句的方法,同时 DBServiceResponse有设置和获取结果记数值和结果设置矢量的方法。

服务是用HttpServiceClient类来访问的。在客户端应用程序中,有以下的代码:

HttpServiceClient client = new HttpServiceClient(serviceURL);
DBServiceRequest request = new DBServiceRequest();
request.setSqlStatement(statement);
DBServiceResponse response = new DBServi