Apache Axis用戶指南(3)

本部分是apache axis用戶指南的第三部分。在Axis中使用WSDL文件。
10.使用WSDL
Web Service Description Language是由IBMMicrosoft完成的規範,並且被很多其他的組織所支持。WSDK用於結構化的描述Web ServicesWeb服務的WSDL是由程序來使用的,它包含了服務的接口,服務使用的數據類型以及服務的位置。更多的內容,可以參考W3CWSDL規範。
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文件創建StubsSkeletons和數據類型
客戶端綁定
AxisWSDL-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,那麼由於newJava中的保留字,所以不能生成這樣的成員屬性。爲了支持這種類型的映射,同時支持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)是繼承了WSDLportType的接口,可以通過這個接口訪問服務操作。例如下面的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得名字。爲了構造SDIWSDL2Java需要從portTypebinding種同時獲取信息。
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和兩個綁定bRPCbDoc,這樣的話就不能爲兩個綁定使用一個接口,此時需要使用兩個接口,一個叫pt,另一個叫bDoc,同時還要生成兩個stubs,一個叫bRPCStub(實現了pt),另一個叫bDocStub(實現了bDoc)
document/literal改變了接口的形式。
綁定
Stub類實現了SDIStub的名字是綁定的名字+Stub。它包含將方法調用轉換成SOAP調用的代碼,通過使用AxisServiceCall對象。它作爲遠程服務的代理,使調用時就像調用本地對象一樣。也就是說,不需要處理endpoingURL,名稱空間或者參數數組,這些需要通過ServiceCall對象動態調用。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 URLStub,但是當請求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 ServiceJava 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調用skeletonaddEntry的方法時,它只需要簡單的將調用分配給實際的實現的addEntry方法。
實現模板描述
WSDL還根據綁定生成了一個實現模板:
public class AddressBookSOAPBindingImpl implements AddressBook {
 
    public void addEntry(String name, Address address)
        throws java.rmi.RemoteException {
    }
}
這個類實際上只是一個實現的測試,並沒有做任何事,它預想服務的編程人員來根據這個模板完成具體的實現。
服務
這個工具同時生成了deploy.wsddundeploy.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對象可以通過GetterSetter方法存取)
無狀態以及服務範圍的信息(服務是否維持session信息)
當前處理狀態
認證信息(用戶名和密碼,可以由servlet服務器提供或者其他方式)
豐富的屬性。幾乎所有關於屬性的信息都可以通過MessageContext.getProperty()方法獲取。只需要知道屬性的名稱,通常是一個常量,定義在例如org.apache.axis.transport.http.HTTPConstants這樣的類中。例如,可以獲取Axis ServletServletContext,通過(HttpServlet)msgC.getProperty(HTTPConstants.MC_HTTP_SERVLET).getServletContext()
在服務中,當前的MessageContext總是可以通過靜態的方法MessageContext.getCurrentContext()獲取。
org.apache.axis.Message
org.apache.axis.Message對象是AxisSOAP消息的一種表示。請求和響應消息可以從MessageContext中獲取(如上所述)Message包括:
MIME(如果message本身包含MIME信息)
附件(如果message本身包含附件)
SOAPPart(快速獲取SOAPPartSOAPEnvelope),可以通過SOAPPart訪問SOAP的任意信息(<soap:Envelope>標籤內的任意信息)
org.apache.axis.SOAPEnvelope
一個MessageContext有兩個Message,每個都包含一個SOAPPartSOAPPart包含SOAPEnvelopeSOAPEnvelope包含SOAP信封的所有內容。可以從SOAPEnvelope中獲取SOAP HeaderSAOP Body
更多內容,參考具體的API
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章