中国铁建企业门户网站wordpress 懒加载插件
 本部分是 apache axis 用户指南的第三部分。在 Axis 中使用 WSDL 文件。 
 
 10. 使用 WSDL 
 
 Web Service Description Language 是由 IBM 和 Microsoft 完成的规范,并且被很多其他的组织所支持。 WSDK 用于结构化的描述 Web Services 。 Web 服务的 WSDL 是由程序来使用的,它包含了服务的接口,服务使用的数据类型以及服务的位置。更多的内容,可以参考 W3C 的 WSDL 规范。 
 
 Axis 通过以下三宗方式来支持 WSDL 
 
 ¨          在 Axis 中发布一个服务后,用户可以通过标准的 web 浏览器来访问服务的 URL ,在后面添加一个 ?WSDL ,这样就可以获得自动生成的服务的 WSDL 文件。 
 
 ¨          提供了一个 WSDL2Java 工具,用于根据 WSDL 文件生成 Java 代理类和框架类。 
 
 ¨          提供了一个 Java2WSDL 工具,用于根据 Java 类构建 WSDL 文件。 
 
 ?WSDL :获取服务的 WSDL 文件 
 
 当通过 Axis 成功发布一个服务后,这个服务和唯一的一个 URL 相关联。对于 JWS 文件来说, URL 就是 JWS 文件本身的路径;对于非 JWS 服务来说,一般情况下 URL 的格式如下: 
 
 http://<host>[:port]/axis/services/<service-name> 
 
 如果可以通过浏览器访问服务的 URL ,那么就会看到一个提示消息,提示终端节点是一个 Axis 服务,那么你应该通过 SOAP 来访问。提示信息如下: 
 
 <service-name> 
 
 
 Hi there, this is an AXIS service! 
 
 Perhaps there will be a form for invoking the service here... 
 
 然而,如果在 URL 后面添加一个 ”?wsdl” ,那么 Axis 会自动生成一个服务的 WSDL 文件,并在浏览器中以 XML 格式显示,如下图所示: 
 
 
 WSDL2Java: 根据 WSDL 文件创建 Stubs 、 Skeletons 和数据类型 
 
 客户端绑定 
 
 Axis 的 WSDL-to-Java 工具的类为 org.apache.axis.wsdl.WSDL2Java ,基本调用方式如下: 
 
 java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL) 
 
 这会为客户端生成必要的绑定,这个过程中 Axis 遵守 JAX-RPC 规范。假设执行以下命令: 
 
 cm %AXIS_HOME%/samples/addr 
 
 java org.apache.axis.wsdl.WSDL2Java AddressBook.wsdl 
 
 这会在 %AXIS_HOME%/samples/addr/AddressFetcher2 目录生成很多类。他们之所以在这里生成,是由于这是 WSDL 中声明的 targetNamespace ,并且是映射到 Java 的包名。名称空间会在下面进行说明: 
 
 
 类型 
 
 根据 WSDL 类型生成的 Java 类根据 WSDL 的类型进行命名。一般来说 ( 不是绝对的 ), 这个 java 类会是一个 bean 。例如下面的 WSDL 类型: 
 
 <xsd:complexType name="phone"> 
 
 <xsd:all> 
 
     <xsd:element name="areaCode" type="xsd:int"/> 
 
     <xsd:element name="exchange" type="xsd:string"/> 
 
     <xsd:element name="number" type="xsd:string"/> 
 
 </xsd:all> 
 
 </xsd:complexType> 
 
 WSDL2Java 会生成如下的 Java 类: 
 
 public class Phone implements java.io.Serializable { 
 
     public Phone() {...} 
 
     public int getAreaCode() {...} 
 
     public void setAreaCode(int areaCode) {...} 
 
     public java.lang.String getExchange() {...} 
 
     public void setExchange(java.lang.String exchange) {...} 
 
     public java.lang.String getNumber() {...} 
 
     public void setNumber(java.lang.String number) {...} 
 
     public boolean equals(Object obj) {...} 
 
     public int hashCode() {...} 
 
 } 
 
 XML-->Java 的映射:元数据 
 
 上面例子中的 XML 类型名为 phone ,而生成的 Java 类名为 Phone ,第一个字母为大写,以适应 Java 编码规范。这种映射经常发生,因为对于 XML 的名称或者标志符的限制要比 Java 少得多。例如,如果 phone 的子元素的名字为 new ,那么由于 new 是 Java 中的保留字,所以不能生成这样的成员属性。为了支持这种类型的映射,同时支持 XML 属性的序列化与反序列化,通过类型元数据系统来关联 Java 数据类和描述符。 
 
 当 WSDL2Java 工具生成了一个数据 bean ,例如上面的 Phone 类,它会观察是否 schema 中包含一些没有直接映射到 Java 的成员变量的属性或者名字。如果有的话,就会生成一个静态的代码片断并为这个类提供一个类型描述符。类型描述符是一个变量描述符的集合,每一个变量描述符将 Java 的变量映射到 XML 元素或者属性。 
 
 // Type metadata 
 
 private static TypeDesc typeDesc; 
 
 static { 
 
          typeDesc = new TypeDesc(AttributeBean.class); 
 
          FieldDesc field; 
 
          // An attribute with a specified QName 
 
          field = new AttributeDesc(); 
 
          field.setFieldName("name"); 
 
          field.setXmlName(new QName("foo", "nameAttr")); 
 
          typeDesc.addFieldDesc(field); 
 
          // An attribute with a default QName 
 
          field = new AttributeDesc(); 
 
          field.setFieldName("male"); 
 
          typeDesc.addFieldDesc(field); 
 
          // An element with a specified QName 
 
          field = new ElementDesc(); 
 
          field.setFieldName("age"); 
 
          field.setXmlName(new QName("foo", "ageElement")); 
 
          typeDesc.addFieldDesc(field); 
 
 } 
 
 Holders 
 
 这种类型作为输入输出或者输出参数使用。 Java 没有输入输出 / 输出参数的概念。为了实现这种行为, JAX-RPC 指定使用 holder 类。一个 Holder 类似一个简单的 java 类,包含了它的一个类型。例如上面例子中的 Phone 类的 holder 类如下: 
 
 package samples.addr.holders; 
 
 public final class PhoneHolder implements javax.xml.rpc.holders.Holder { 
 
     public samples.addr.Phone value; 
 
     public PhoneHolder() 
 
     { 
 
     } 
 
     public PhoneHolder(samples.addr.Phone value) { 
 
         this.value = value; 
 
     } 
 
 } 
 
 holder 类只有在类型被作为输入输出或者输出参数的时候才被生成。 Holder 类实在类名后面加上后缀 Holder 来命名,并且生成到包名为 holders 下面。 
 
 PortTypes 
 
 服务定义接口 (Service Definition Interface,SDI) 是继承了 WSDL 的 portType 的接口,可以通过这个接口访问服务操作。例如下面的 WSDL : 
 
 <message name="empty"> 
 
 <message name="AddEntryRequest"> 
 
  <part name="name" type="xsd:string"/> 
 
  <part name="address" type="typens:address"/> 
 
 </message> 
 
 <portType name="AddressBook"> 
 
  <operation name="addEntry"> 
 
     <input message="tns:AddEntryRequest"/> 
 
     <output message="tns:empty"/> 
 
  </operation> 
 
 </portType> 
 
 WSDL2Java 会生成如下的接口: 
 
 public interface AddressBook extends java.rmi.Remote { 
 
     public void addEntry(String name, Address address) throws java.rmi.RemoteException; 
 
 } 
 
 SDI 接口的名字就是 portType 得名字。为了构造 SDI , WSDL2Java 需要从 portType 何 binding 种同时获取信息。 
 
 JAX-RPC 规范对这部分的说明是: 
 
 "The name of the Java interface is mapped from the name attribute of the wsdl:portType element. ... If the mapping to a service definition interface uses elements of the wsdl:binding ..., then the name of the service definition interface is mapped from the name of the wsdl:binding element." 
 
 这个规范是 JAX-RPC ,这意味着只适用于 portType 是一个 RPC 接口。如果绑定的信息不是 RPC ,那么将使用绑定的名称。 
 
 可以有一个 portType—pt 和两个绑定 bRPC 和 bDoc ,这样的话就不能为两个绑定使用一个接口,此时需要使用两个接口,一个叫 pt ,另一个叫 bDoc ,同时还要生成两个 stubs ,一个叫 bRPCStub( 实现了 pt) ,另一个叫 bDocStub( 实现了 bDoc) 。 
 
 document/literal 改变了接口的形式。 
 
 绑定 
 
 Stub 类实现了 SDI , Stub 的名字是绑定的名字 +Stub 。它包含将方法调用转换成 SOAP 调用的代码,通过使用 Axis 的 Service 和 Call 对象。它作为远程服务的代理,使调用时就像调用本地对象一样。也就是说,不需要处理 endpoing 的 URL ,名称空间或者参数数组,这些需要通过 Service 和 Call 对象动态调用。 Stub 隐藏了所有这些的具体实现。 
 
 根据下面的 WSDL 片段: 
 
 <binding name="AddressBookSOAPBinding" type="tns:AddressBook"> 
 
  ... 
 
 </binding> 
 
 WSDL2Java 会生成如下的 Stub : 
 
 public class AddressBookSOAPBindingStub extends org.apache.axis.client.Stub 
 
                                         implements AddressBook { 
 
     public AddressBookSOAPBindingStub() throws org.apache.axis.AxisFault 
 
     {...} 
 
     public AddressBookSOAPBindingStub(URL endpointURL,  
 
                                       javax.xml.rpc.Service service) 
 
         throws org.apache.axis.AxisFault 
 
     {...} 
 
     public AddressBookSOAPBindingStub(javax.xml.rpc.Service service) 
 
         throws org.apache.axis.AxisFault 
 
     {...} 
 
     public void addEntry(String name, Address address) throws RemoteException 
 
     {...} 
 
 } 
 
 Services 服务 
 
 正常来讲,客户端应用程序不会直接实例化一个 stub ,而是实例化一个 service locator ,然后调用一个方法来返回 stub 。这个 locator 是根据 WSDL 中的服务元素来指定的。 WSDL2Java 根据 service 元素生成两个独享,例如: 
 
 <service name="AddressBookService"> 
 
  <port name="AddressBook" binding="tns:AddressBookSOAPBinding"> 
 
     <soap:address location="http://localhost:8080/axis/services/AddressBook"/> 
 
  </port> 
 
 </service> 
 
 WSDL2Java 会生成如下接口和类: 
 
 public interface AddressBookService extends javax.xml.rpc.Service { 
 
     public String getAddressBookAddress(); 
 
     public AddressBook getAddressBook() throws javax.xml.rpc.ServiceException; 
 
     public AddressBook getAddressBook(URL portAddress) throws javax.xml.rpc.ServiceException; 
 
 } 
 
 实现了 AddressBookServie 的类: 
 
 public class AddressBookServiceLocator extends org.apache.axis.client.Service 
 
                                        implements AddressBookService { 
 
     ... 
 
 } 
 
 Service 接口定义了 WSDL 中定义的每个接口的 get 方法。 locator 实现了 service 接口,也就是说它实现了 get 方法。它用来获取 Stub 实例。 Service 类会默认的创建一个指向 endpoint URL 的 Stub ,但是当请求 PortType 的时候,可能需要指定一个不同的 URL 。 
 
 一个典型的 stub 类的应用如下: 
 
 public class Tester 
 
 { 
 
     public static void main(String [] args) throws Exception { 
 
         // Make a service 
 
         AddressBookService service = new AddressBookServiceLocator(); 
 
         // Now use the service to get a stub which implements the SDI. 
 
         AddressBook port = service.getAddressBook(); 
 
         // Make the actual call 
 
         Address address = new Address(...); 
 
         port.addEntry("Russell Butek", address); 
 
     } 
 
 } 
 
 服务器端的绑定 
 
 就像在客户端有一个 Web Service 的 Java stub 一样,一个 Java  框架的 skeleton 在服务器端使用。为了生成 Skeleton 类,需要使用 WSDL2Java 的 —server-side –skeletonDeploy true 选项。例如,仍旧使用 AddressBook.wsdl 文件: 
 
 java org.apache.axis.wsdl.WSDL2Java --server-side --skeletonDeploy true AddressBook.wsdl 
 
 可以看到 WSDL2Java 生成了所有在之前生成的 client 端的类,但是又声称了一些新的文件: 
 
 
 如果不指定 —skeletonDeploy true 选项,那么不会生成 skeleton ,而是生成的 deploy.wsdd 文件指示实现类已经直接部署了。在这种情况下, deploy.wsdd 文件包含了额外的元数据来描述实现类的操作和参数。通过下面的方法可以直接部署服务: 
 
 java org.apache.axis.wsdl.WSDL2Java --server-side AddressBook.wsdl 
 
 下面是在服务器端生成的文件: 
 
 
 绑定 
 
 Skeleton 描述 
 
 Skeleton 类是介于 Axis engine 和实际的服务实现之间的类。它的名字就是绑定的名字 +Skeleton 。例如对于 AddressBook 绑定, WSDL2Java 会生成如下的 Skeleton : 
 
 public class AddressBookSOAPBindingSkeleton implements AddressBook, 
 
                                                        org.apache.axis.wsdl.Skeleton { 
 
     private AddressBook impl; 
 
      public AddressBookSOAPBindingSkeleton() { 
 
         this.impl = new AddressBookSOAPBindingImpl(); 
 
     } 
 
      public AddressBookSOAPBindingSkeleton(AddressBook impl) { 
 
         this.impl = impl; 
 
     } 
 
      public void addEntry(java.lang.String name, Address address) 
 
         throws java.rmi.RemoteException 
 
     { 
 
         impl.addEntry(name, address); 
 
     } 
 
 } 
 
 实际的 Skeleton 类可能内容还有很多,这里只拷贝了基本的框架。 
 
 skeleton 类包含一个 AddressBook 服务的实现。这个实现要么通过构造器传递给 skeleton ,要么是一个生成的实现的实例。当 Axis engine 调用 skeleton 的 addEntry 的方法时,它只需要简单的将调用分配给实际的实现的 addEntry 方法。 
 
 实现模板描述 
 
 WSDL 还根据绑定生成了一个实现模板: 
 
 public class AddressBookSOAPBindingImpl implements AddressBook { 
 
 
     public void addEntry(String name, Address address) 
 
         throws java.rmi.RemoteException { 
 
     } 
 
 } 
 
 这个类实际上只是一个实现的测试,并没有做任何事,它预想服务的编程人员来根据这个模板完成具体的实现。 
 
 服务 
 
 这个工具同时生成了 deploy.wsdd 和 undeploy.wsdd 文件,可以供 AdminClient 食用。这些文件只有当填充了实现类的具体方法后,编译类文件,然后将类文件放到 Axis engine 可以访问的位置后,才可以调用 AdminClient 方法来发布服务。 
 
 11.Java2WSDL 
 
           这个工具笔者就不介绍了 
 
 笔者认为使用 WSDL2Java 工具生成的代码中冗余的代码太多,并且编码规范有一些 ApacheAxis-Specific ,所以觉得还是自己手写代码比较好一些。 
 
 一旦写完了 Java 代码,可以通过 Web 浏览器访问,获取 WSDL 文件,没有必要使用 Java2WSDL 了。 
 
 12. 公开的 Axis 接口 
 
          Axis 公开的接口相对稳定,可以使用,即使 Axis 重构的话,这部分也应该不会修改。或者做一些兼容性的修改。 
 
 可以实现的一些 Apache Axis 的接口如下: 
 
 JAX-RPC 的一些接口,这些接口是针对 JAX-RPC 规范 1.0 的,会根据新的规范进行修改。 
 
 Axis 接口:这些相对不稳定。 
 
 12. 重要的类 
 
 org.apache.axis.MessageContext 
 
 Axis 所知道的关于请求 / 响应的所有的信息都是通过 MessageContext 来获取的。 Axis 将下面的内容存储在 MessageContext 中: 
 
 AxisEngine 的引用 
 
 请求和响应的消息 (org.apache.axis.Message 对象可以通过 Getter 和 Setter 方法存取 ) 
 
 无状态以及服务范围的信息 ( 服务是否维持 session 信息 ) 
 
 当前处理状态 
 
 认证信息 ( 用户名和密码,可以由 servlet 服务器提供或者其他方式 ) 
 
 丰富的属性。几乎所有关于属性的信息都可以通过 MessageContext.getProperty() 方法获取。只需要知道属性的名称,通常是一个常量,定义在例如 org.apache.axis.transport.http.HTTPConstants 这样的类中。例如,可以获取 Axis Servlet 的 ServletContext ,通过 (HttpServlet)msgC.getProperty(HTTPConstants.MC_HTTP_SERVLET).getServletContext() 。 
 
 在服务中,当前的 MessageContext 总是可以通过静态的方法 MessageContext.getCurrentContext() 获取。 
 
 org.apache.axis.Message 
 
 org.apache.axis.Message 对象是 Axis 对 SOAP 消息的一种表示。请求和响应消息可以从 MessageContext 中获取 ( 如上所述 ) 。 Message 包括: 
 
 MIME 头 ( 如果 message 本身包含 MIME 信息 ) 
 
 附件 ( 如果 message 本身包含附件 ) 
 
 SOAPPart( 快速获取 SOAPPart 的 SOAPEnvelope) ,可以通过 SOAPPart 访问 SOAP 的任意信息 (<soap:Envelope> 标签内的任意信息 ) 
 
 org.apache.axis.SOAPEnvelope 
 
 一个 MessageContext 有两个 Message ,每个都包含一个 SOAPPart , SOAPPart 包含 SOAPEnvelope 。 SOAPEnvelope 包含 SOAP 信封的所有内容。可以从 SOAPEnvelope 中获取 SOAP Header 和 SAOP Body 。 
 
 更多内容,参考具体的 API 。 
  