首页 > 开发 > 综合 > 正文

SOAP净化有线协议(二):Apache SOAP介绍(2)

2024-07-21 02:21:50
字体:
来源:转载
供稿:网友
三、带有javabean的helloworld实例
如前所述,apache soap提供了许多预先构造的串行化和反串行化方法,其中包括为利用java vector、enumeration、数组、javabean作为参数和返回值而提供的串行化器和反串行化器。在这一部分,我将修改helloworld服务,通过一个javabean传入接收hello信息的用户名。

3.1、helloworld服务
改写后的helloworld服务完整代码如下:


package hello;
public class helloserver
{
public string sayhelloto(string name)
{
system.out.println("sayhelloto(string name)");
return "hello " + name + ", how are you doing?";
}
public string sayhelloto(name thename)
{
system.out.println("sayhelloto(name thename)");
return "hello " + thename.getname() + ", how are you doing?";
}
}
服务的代码仍旧很简单,仍旧类似于不用javabean时的helloworld服务。不过,这意味着最复杂的工作都转移到了客户端。事实上,这个版本的服务与以前版本的唯一差别在于,现在出现了一个重载的sayhelloto()方法。上面的代码中重载后的方法用粗体字显示。

重载的方法需要一个对name javabean的引用。name javabean的定义如下:


package hello;
public class name
{
private string name;
public string getname()
{
return name;
}
public void setname(string name)
{
this.name = name;
}
}
3.2、部署服务
部署一个使用了javabean的服务时,需要为apache soap服务器提供一些额外的信息。因此,现在部署服务的过程稍微复杂一点。

■ 使用管理工具部署服务

要使用管理工具部署这个新版的helloworld服务,首先按照前面所介绍的步骤进行,但这一次不要点击deploy按钮。现在,在number of mappings输入框输入1,它表示我们将给出一个映射(即name javabean)的信息。紧接mappings之下有一个表格,我们要用到这个表格的第一行。保留encoding style的值为soap,把namespace uri设置成对象的id:在本例中,它是urn:hello。接下来,把local part和java type输入框设置成name javabean的完整名字,即hello.name。最后,把java to xml serializer和xml to java deserializer输入框设置成org.apache.soap.encoding.soapenc.beanserializer,这是一个实现了serializer和deserializer接口的类,用来串行化和反串行化name javabean。如果你用到了更多的javabean(比如还有一个address bean),则应该在这个表格中输入其他bean的信息,同时还应该更新number of mappings输入框的值,使之反映出表格中实际被使用的行数。

■ 从命令行部署服务

要从命令行进行部署,我们只需修改作为命令行参数传入的xml部署描述器文件。修改后的xml文件如下所示:


<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" id="urn:hello">
<isd:provider type="java" scope="application" methods="sayhelloto">
<isd:java class="hello.helloserver" static="false"/>
</isd:provider>
<isd:mappings>
<isd:map encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="urn:hello" qname="x:hello.name"
javatype="hello.name"
java2xmlclassname="org.apache.soap.encoding.soapenc.beanserializer"
xml2javaclassname="org.apache.soap.encoding.soapenc.beanserializer"/>
</isd:mappings>
</isd:service>
正如在前一个例子中,这些xml代码所包含的信息和通过web界面的管理工具所提供的信息一样。

3.3、helloworld客户程序
和第一个例子一样,客户程序更复杂,也更令人感兴趣。这里我不再仔细分析整个客户程序,而是介绍两个客户程序版本的不同之处。由于调用方法的一个参数(在本例中,它是唯一的参数)是一个javabean,所以必须手工设置一个类型映射注册项。这个任务通过如下步骤完成:先创建org.apache.soap.encoding.soapmappingregistry类的一个实例,然后调用它的maptypes()方法。正如maptypes()方法名字所预示的,它用来注册一个以前未知的类型,比如定制的javabean。maptypes()方法的参数包括要使用的编码方式、限定的javabean名字、类型的完整类名、串行化器和反串行化器。在本例中,执行串行化任务的是标准的bean串行化器。限定的javabean名字包含一个元素的名字,包括它所属的名称空间。在本例中,name javabean的限定名字由名称空间uri(urn:hello)和本地名字(hello.name)结合构成。请看下面的代码片断:


// 创建类型映射注册器
soapmappingregistry smr = new soapmappingregistry();
beanserializer beanser = new beanserializer();
// 映射类型
smr.maptypes(constants.ns_uri_soap_enc,
new qname("urn:hello", "hello.name"),hello.name.class, beanser, beanser);
接下来,客户程序必须告诉call对象使用新的注册器而不是默认的注册器。为此,我们要调用call对象的setsoapmappingregistry()方法,如下所示:

call.setsoapmappingregistry(smr);
手工设置好类型映射注册器之后,接下来还必须为call对象设置参数。这一步骤可以按前面介绍的方法完成,不同之处在于,现在我们不再用字符串类型的名字作为参数,而是用javabean作为参数,如下所示:


// 设置调用参数
vector params = new vector();
name thename = new name();
thename.setname(name);
params.addelement(new parameter("name", hello.name.class, thename, null));
call.setparams(params);
客户程序剩下的部分和原来的版本一样。listing 3显示了完整的客户程序代码:


listing 3: client2.java
package hello;
import java.net.url;
import java.util.vector;
import org.apache.soap.soapexception;
import org.apache.soap.constants;
import org.apache.soap.fault;
import org.apache.soap.rpc.call;
import org.apache.soap.rpc.parameter;
import org.apache.soap.rpc.response;
import org.apache.soap.encoding.soapmappingregistry;
import org.apache.soap.encoding.soapenc.beanserializer;
import org.apache.soap.util.xml.qname;

public class client2
{
public static void main(string[] args) throws exception
{
if(args.length == 0)
{
system.err.println("usage: java hello.client [soap-router-url] ");
system.exit (1);
}
try
{
url url = null;
string name = null;
if(args.length == 2)
{
url = new url(args[0]);
name = args[1];
}
else
{
url = new url("http://localhost:8080/apache-soap/servlet/rpcrouter");
name = args[0];
}
// 构造调用对象
call call = new call();
call.settargetobjecturi("urn:hello");
call.setmethodname("sayhelloto");
call.setencodingstyleuri(constants.ns_uri_soap_enc);
// 创建类型映射注册器
soapmappingregistry smr = new soapmappingregistry();
beanserializer beanser = new beanserializer();
// 映射类型
smr.maptypes(constants.ns_uri_soap_enc,
new qname("urn:hello", "hello.name"),
hello.name.class, beanser, beanser);
call.setsoapmappingregistry(smr);
// 设置参数
vector params = new vector();
name thename = new name();
thename.setname(name);
params.addelement(new parameter("name", hello.name.class,
thename, null));
call.setparams(params);
// 发出调用
response resp = null;
try
{
resp = call.invoke(url, "");
}
catch( soapexception e )
{
system.err.println("caught soapexception (" +
e.getfaultcode() + "): " + e.getmessage());
system.exit(-1);
}

// 检查应答
if( !resp.generatedfault() )
{
parameter ret = resp.getreturnvalue();
object value = ret.getvalue();
system.out.println(value);
}
else
{
fault fault = resp.getfault();
system.err.println("generated fault: ");
system.out.println (" fault code = " + fault.getfaultcode());
system.out.println (" fault string = " + fault.getfaultstring());
}
}
catch(exception e)
{
e.printstacktrace();
}
}
}
四、编译和运行程序
现在整个程序的开发工作已经完成,该是运行它的时候了。不过,我们首先要编译服务程序和客户程序。

创建一个hello目录,把client1.java、client2.java和helloserver.java复制到这个目录。我把hello目录放到了apache soap的示例目录(即e:/soap-2_0/samples)之下。编译程序时,classpath中只需包含hello目录的父目录(即e:/soap-2_0/samples)、soap.jar和xerces.jar。我用下面的批命令编译程序:


set classpath=e:/soap-2_0/samples/;e:/soap-2_0/lib/soap.jar;e:/xerces-1_2_0/xerces.jar
javac -d .. helloserver.java client.java client2.java
注意:从hello目录执行这个批命令文件。

要使用这个服务,除了部署它之外,还需要修改web服务器的classpath,确保web服务能够找到hello.helloserver类——对于本例,这是指把e:/soap-2_0/samples加入到web服务器的classpath。对classpath进行必要的修改之后,重新启动web服务器。接下来就可以运行客户程序了。下面是我运行hello.client的批命令文件:

set classpath=e:/soap-2_0/samples/;e:/soap-2_0/lib/soap.jar;e:/xerces-1_2_0/xerces.jar
java hello.client tarak
这里的classpath和编译程序时用的classpath相同。

最后,运行hello.client2的批命令文件可以如下:

set classpath=e:/soap-2_0/samples/;e:/soap-2_0/lib/soap.jar;e:/xerces-1_2_0/xerces.jar
java hello.client2 tarak
观察web服务器的控制台窗口,看看在运行两个不同的客户程序时,helloworld服务的哪些方法正在被调用。

■ 结束语
在这篇文章中,我介绍了如何用apache soap实现来创建简单的基于soap的服务。在soap实现方面,另一个重要的竞争者是microsoft。遗憾的是,“纯”java开发者在使用microsoft实现的时候会有一段艰苦的时光,因为它的实现包含了com对象。

在下一篇文章中,我将介绍apache soap支持的另一种创建服务的方式:使用javascript之类的脚本语言,而不是java。另外,我还要介绍一个很不错的javascript引擎,即rhino。
■ 参考资源
  • w3c的soap 1.1规范:
    http://www.w3.org/tr/soap/
  • 下载apache soap:
    http://xml.apache.org/dist/soap/
  • 有关ibm soap工程的更多信息:
    http://www.alphaworks.ibm.com/tech/soap4j
  • apache soap可用功能的一个清单:
    http://xml.apache.org/soap/features.html
  • apache许可协议:
    http://www.apache.org/license.txt
  • 下载tomcat 3.1
    http://jakarta.apache.org/builds/jakarta-tomcat/release/v3.1.1/bin/
  • 下载apache xerces 1.2版本:
    http://xml.apache.org/dist/xerces-j/
  • "ms soap sdk vs ibm soap4j: comparison & review," james snell (o'reilly):
    http://windows.oreilly.com/news/soapreview_0600.html
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表