Các dịch vụ Web dùng cho các ứng dụng doanh nghiệp phụ thuộc rất nhiều vào việc sử dụng các định nghĩa dịch vụ. Các định nghĩa dịch vụ quy định một hợp đồng cơ bản giữa các nhà cung cấp dịch vụ và bất kỳ người dùng có thể nào, mô tả chi tiết các kiểu chức năng do dịch vụ cung cấp và các thông báo được trao đổi như là một phần của từng chức năng. Các nhà cung cấp dịch vụ và người dùng tự do triển khai thực hiện các thiết bị đầu cuối trao đổi của mình theo bất kỳ cách nào họ muốn, miễn là các thông báo thực tế mà họ gửi đi phù hợp với định nghĩa dịch vụ. Việc sử dụng các định nghĩa dịch vụ đang quy định các trao đổi thông báo XML là những gì đặt ra cho các dịch vụ web ngoài các công nghệ trước đây dùng cho việc lập trình phân tán.
Người ta đã đề xuất nhiều kỹ thuật khác nhau để định nghĩa các dịch vụ web, nhưng cách tiếp cận được sử dụng rộng rãi nhất là WSDL 1.1. WSDL 1.1 có một số nhược điểm, bao gồm một cấu trúc quá phức tạp làm cho nó hơi khó đọc một chút với những người chưa thạo. Nó cũng bị thiếu một định nghĩa chính thức có căn cứ, dẫn đến các thông tin chi tiết "bổ sung" liên tục là đã vá một số lỗ hổng trong tài liệu đặc tả ban đầu. Đáp lại, các chồng các dịch vụ web có xu hướng xử lý các tài liệu WSDL 1.1 càng linh hoạt càng tốt. Tính linh hoạt này có thể luôn làm tăng thêm sự nhầm lẫn trong việc hiểu WSDL 1.1, vì các nhà phát triển có thể thấy một loạt các cấu trúc WSDL không kèm theo hướng dẫn về cách tiếp cận nào là tốt nhất.
Trong bài này, bạn sẽ học cách tìm hiểu các tài liệu WSDL 1.1 và bạn sẽ thấy phần đầu tiên của một mô hình Java để xác minh các tài liệu WSDL và chuyển đổi chúng sang một dạng chuẩn.
WSDL 1.1, được công bố vào đầu năm 2001, về mặt kỹ thuật đã được thay thế bởi khuyến cáo WSDL 2.0 của W3C được công bố vào năm 2007. WSDL 2.0 cung cấp một cấu trúc sạch hơn WSDL 1.1 và linh hoạt hơn. Nhưng WSDL 2.0 có một vấn đề con gà và quả trứng — WSDL 2.0 không được sử dụng rộng rãi vì nó không được hỗ trợ rộng rãi và vì nó không được sử dụng rộng rãi nên để hỗ trợ nó có một chút áp lực lên những người triển khai thực hiện các chồng các dịch vụ web. WSDL 1.1, mặc dù không hoàn thiện, nhưng cũng đủ tốt cho hầu hết các mục đích.
Đặc tả WSDL 1.1 đã không chính xác về có bao nhiêu tính năng đã được sử dụng. Vì trọng tâm của WSDL là làm việc với các định nghĩa dịch vụ SOAP, nó cũng có hỗ trợ cho một số tính năng SOAP (chẳng hạn như mã hóa rpc) mà sau đó người ta đã thấy là có thể gây rắc rối. Tổ chức về Tính tương thích các Dịch vụ Web (WS-I - Web Services Interoperability Organization) đã giải quyết những vấn đề này trong Lược đồ cơ bản (BP - Basic Profile), trong đó xác định các cách thực hành tốt nhất cho các dịch vụ web khi sử dụng SOAP và WSDL. BP 1.0 đã được phê duyệt vào năm 2004 và được cập nhật là BP 1.1 vào năm 2006. Trong bài này, tôi sẽ trình bày WSDL 1.1 dựa trên các hướng dẫn WS-I BP, bỏ qua các tính năng thực sự bị phản đối như mã hóa rpc cho SOAP.
Các định nghĩa lược đồ XML được cho là để định nghĩa cấu trúc của các tài liệu XML. WSDL 1.1 gồm có một mô tả lược đồ theo đặc tả ban đầu, nhưng lược đồ đó không phù hợp với các mô tả văn bản theo một số khía cạnh. Điều này đã được sửa chữa trong một phiên bản sửa đổi lược đồ sau đó một chút, nhưng tài liệu WSDL 1.1 đã không được cập nhật để phản ánh sự thay đổi này. Sau đó, nhóm WS-I BP đã quyết định thực hiện các thay đổi nhiều hơn với lược đồ WSDL, vì vậy nhóm đó đã tạo ra những gì có vẻ là phiên bản các cách thực hành tốt nhất của lược đồ khó xử này. Các tài liệu được viết cho một phiên bản lược đồ nói chung không tương thích với các phiên bản khác (mặc dù sử dụng cùng một vùng tên), nhưng may mắn thay, về cơ bản, hầu hết các công cụ các dịch vụ web bỏ qua lược đồ và chấp nhận bất cứ điều gì trông hợp lý. (Xem Tài nguyên để biết các liên kết đến nhiều lược đồ của WSDL).
Ngay cả phiên bản WS-I BP của lược đồ WSDL 1.1 không giúp gì nhiều trong việc đảm bảo rằng các tài liệu WSDL 1.1 tuân theo đặc tả này. Lược đồ không phản ánh tất cả các ràng buộc trong WS-I BP, đặc biệt là đối với thứ tự của các thành phần. Ngoài điều này, XML Schema (Lược đồ XML) không có khả năng xử lý nhiều kiểu ràng buộc đã nói rõ trên các tài liệu (ví dụ như các thuộc tính thay thế hoặc các phần tử mở rộng cần thiết từ các lược đồ riêng). Vì vậy, việc kiểm tra xem một tài liệu WSDL 1.1 có tuân theo đặc tả WSDL 1.1 (như được WS-I BP sửa đổi) không liên quan đến nhiều hơn chỉ là bật xác nhận hợp lệ lược đồ XML. Tôi sẽ quay lại chủ đề này sau trong bài này. Trước tiên, tôi sẽ xem xét cấu trúc của các mô tả dịch vụ WSDL 1.1.
Các tài liệu WSDL
1.1 sử dụng một phần tử gốc cố định, có tên phù hợp là <wsdl:definitions>. Trong phần tử gốc này, một phần tử con "thụ
động" (chỉ cần tham khảo các tài liệu WSDL 1.1 riêng) và năm phần tử con "chủ động"
(những phần tử này thực sự đóng góp vào mô tả dịch vụ) được định nghĩa trong vùng
tên WSDL 1.1:
<wsdl:import>tham khảo một tài liệu WSDL 1.1 riêng, với các mô tả được kết hợp vào trong tài liệu này.<wsdl:types>định nghĩa các kiểu hoặc các phần tử XML được sử dụng cho các trao đổi thông báo.<wsdl:message>định nghĩa một thông báo thực tế, về các kiểu hoặc các phần tử XML.<wsdl:portType>định nghĩa một thiết lập các hoạt động trừu tượng được một dịch vụ thực hiện.<wsdl:binding>định nghĩa việc thực hiện thực tế của một phần tử<wsdl:portType>, sử dụng các giao thức và các định dạng đặc biệt.<wsdl:service>định nghĩa một dịch vụ là tất cả, thường gồm có một hoặc nhiều phần tử<wsdl:port>với thông tin truy cập cho các phần tử<wsdl:binding>.
Ngoài ra có một phần tử <wsdl:document> có thể
được sử dụng cho các mục đích thông tin, như là phần tử con đầu tiên của <wsdl:definitions> và cũng như là phần tử con đầu
tiên của bất kỳ các phần tử ở trên.
Một mô tả dịch vụ đầy đủ thường cần ít nhất một trong mỗi phần tử này ngoại trừ phần tử <wsdl:import>, nhưng chúng không cần tất cả có mặt
trong cùng một tài liệu. Bạn có thể sử dụng phần tử <wsdl:import> để lắp ráp một mô tả WSDL đầy đủ từ nhiều tài liệu,
cho bạn sự linh hoạt để phân chia các mô tả cho phù hợp với tổ chức của bạn. Ví dụ,
ba phần tử mô tả đầu tiên (<wsdl:types>, <wsdl:message> và <wsdl:portType>) cùng nhau cung cấp một mô tả giao diện dịch vụ
đầy đủ (có lẽ do một nhóm kiến trúc sư định nghĩa), do đó, để giữ chúng tách rời
khỏi các phần tử <wsdl:binding> và <wsdl:service> theo hướng thực hiện có thể là hợp lý.
Tất cả các chồng các dịch vụ web chính hỗ trợ chia tách các mô tả thành nhiều tài
liệu WSDL.
Liệt kê 1 và Liệt kê 2 cho thấy một mẫu của một mô tả dịch vụ WSDL được chia tách thành hai tài liệu WSDL, với các thành phần mô tả-giao diện trong một tệp BookServerInterface.wsdl và các thành phần thực hiện trong tệp BookServerImpl.wsdl. Liệt kê 1 hiển thị tệp BookServerInterface.wsdl:
Liệt kê 1. Tệp BookServerInterface.wsdl
<wsdl:definitions ... xmlns:tns="http://sosnoski.com/ws/library/BookServerInterface"
targetNamespace="http://sosnoski.com/ws/library/BookServerInterface">
<wsdl:document>Book service interface definition.</wsdl:document>
<wsdl:types>
<xs:schema ...
targetNamespace="http://sosnoski.com/ws/library/BookServerInterface">
<xs:import namespace="http://sosnoski.com/ws/library/types"
schemaLocation="book-types.xsd"/>
...
</xs:schema>
</wsdl:types>
<wsdl:message name="getBookMessage">
<wsdl:part name="part" element="tns:getBook"/>
</wsdl:message>
<wsdl:message name="getBookResponseMessage">
<wsdl:part name="part" element="tns:getBookResponse"/>
</wsdl:message>
...
<wsdl:message name="addBookMessage">
<wsdl:part name="part" element="tns:addBook"/>
</wsdl:message>
<wsdl:message name="addBookResponseMessage">
<wsdl:part name="part" element="tns:addBookResponse"/>
</wsdl:message>
<wsdl:message name="addDuplicateFault">
<wsdl:part name="fault" element="tns:addDuplicate"/>
</wsdl:message>
<wsdl:portType name="BookServerPortType">
<wsdl:documentation>
Book service implementation. This creates an initial library of books when the
class is loaded, then supports method calls to access the library information
(including adding new books).
</wsdl:documentation>
<wsdl:operation name="getBook">
<wsdl:documentation>
Get the book with a particular ISBN.
</wsdl:documentation>
<wsdl:input message="tns:getBookMessage"/>
<wsdl:output message="tns:getBookResponseMessage"/>
</wsdl:operation>
...
<wsdl:operation name="addBook">
<wsdl:documentation>Add a new book.</wsdl:documentation>
<wsdl:input message="tns:addBookMessage"/>
<wsdl:output message="tns:addBookResponseMessage"/>
<wsdl:fault message="tns:addDuplicateFault" name="addDuplicateFault"/>
</wsdl:operation>
</wsdl:portType>
</wsdl:definitions>
|
Liệt
kê 2 hiển thị tệp BookServerImpl.wsdl. Phần tử <wsdl:import> gần phần đầu của nó nhập khẩu mô tả giao diện từ tệp
BookServerInterface.wsdl.
Liệt kê 2. Tệp BookServerImpl.wsdl
<wsdl:definitions ... xmlns:ins="http://sosnoski.com/ws/library/BookServerInterface"
xmlns:tns="http://sosnoski.com/ws/library/BookServer"
targetNamespace="http://sosnoski.com/ws/library/BookServer">
<wsdl:document>
Definition of actual book service implementation.
</wsdl:document>
<wsdl:import namespace="http://sosnoski.com/ws/library/BookServerInterface"
location="BookServerInterface.wsdl"/>
<wsdl:binding name="BookServerBinding" type="ins:BookServerPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="getBook">
<soap:operation soapAction="urn:getBook"/>
<wsdl:input>
<soap:body/>
</wsdl:input>
<wsdl:output>
<soap:body/>
</wsdl:output>
</wsdl:operation>
...
<wsdl:operation name="addBook">
<soap:operation soapAction="urn:addBook"/>
<wsdl:input>
<soap:body/>
</wsdl:input>
<wsdl:output>
<soap:body/>
</wsdl:output>
<wsdl:fault name="addDuplicateFault">
<soap:fault name="addDuplicateFault"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="BookServer">
<wsdl:port name="BookServerPort" binding="tns:BookServerBinding">
<soap:address location="http://localhost:8080/cxf/BookServer"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
Bên
cạnh các định nghĩa phần tử (và thuộc tính) trong vùng tên WSDL 1.1, WSDL 1.1 cũng
định nghĩa các phần tử mở rộng. Các phần tử mở rộng được dự định cắm vào các vị trí
đặc biệt trong các mô tả dịch vụ WSDL 1.1 để cung cấp thông tin bổ sung cần thiết
cho một kiểu dịch vụ cụ thể. Chỉ những phần tử mở rộng của WSDL 1.1 vẫn đang được sử
dụng rộng rãi là những phần tử dùng cho các liên kết của SOAP 1.1 (đã thấy trong Liệt kê 2, trong các phần tử <wsdl:binding> và <wsdl:service>), đã được định nghĩa bởi đặc tả WSDL 1.1 ban đầu và
dùng cho các liên kết của SOAP 1.2, được định nghĩa bởi một đặc tả riêng vào năm
2006.
Phần tử <wsdl:types> gói tất cả các định nghĩa XML được sử dụng
cho các thông báo, dưới dạng của một hoặc nhiều phần tử <xs:schema>. (WSDL cho phép các lựa chọn thay thế với XML Schema
dùng cho các định nghĩa này, nhưng hầu hết các chồng chỉ hỗ trợ XML Schema). Các
phần tử <xs:schema> có thể sử dụng <xs:import> và/hoặc <xs:include> để kết hợp các lược đồ khác bên ngoài vào WSDL, nếu
muốn (cũng như để tham khảo các lược đồ riêng được chứa trong cùng một WSDL).
Vì một phần tử <wsdl:types> đơn lẻ có
thể chứa bất kỳ số lượng các định nghĩa lược đồ nào, nên không bao giờ có lý do để
sử dụng nhiều hơn một phần tử <wsdl:types> trong
một tài liệu WSDL. Trong Liệt kê 1, phần tử <wsdl:types> là ở gần đầu của tệp
BookServerInterface.wsdl.
Khác với <wsdl:import> và <wsdl:types>, tất cả các thành phần mức cao nhất khác của một tài
liệu WSDL được đặt tên từng cái một bằng cách sử dụng một thuộc tính name cần thiết. Nếu bạn sử dụng một thuộc tính targetNamespace trên phần tử gốc tài liệu <wsdl:definitions> mà bạn thường nên làm, như là một
cách thực hành tốt nhất, các tên cho các thành phần này được định nghĩa trong vùng
tên đích đó. Điều này có nghĩa là khi bạn định nghĩa tên này, bạn chỉ đưa ra một
phần tên đơn giản hoặc "cục bộ", nhưng các tài liệu tham khảo đến thành phần đó phải
nói rõ tên với một tiền tố vùng tên hoặc vùng tên mặc định. Hình 1 cho thấy các liên
kết quan trọng nhất giữa các thành phần WSDL, với các đường liền mô tả các tài liệu
tham khảo tên được nói rõ và các đường chấm chấm mô tả các tên được dùng để định
dạng mà không đủ điều kiện vùng tên:
Hình 1. Các liên kết giữa các thành phần WSDL
Các thông báo, do các phần tử <wsdl:message> mô tả,
ở tâm điểm của các mô tả dịch vụ WSDL. Các phần tử <wsdl:message> là các mô tả của dữ liệu XML được trao đổi giữa một
khách hàng và một nhà cung cấp dịch vụ. Mỗi phần tử <wsdl:message> không chứa hoặc có chứa nhiều (thường là một) phần
tử con <wsdl:part>. Mỗi phần tử part yêu cầu thuộc tính name riêng của mình
(duy nhất trong <wsdl:message>) và hoặc một thuộc
tính element hoặc một thuộc tính type tham khảo định nghĩa lược đồ của dữ liệu XML. Một số phần tử <wsdl:message> được hiển thị trong Liệt kê 1, theo sau phần tử <wsdl:types> trong tệp BookServerInterface.wsdl.
Các phần tử
<wsdl:portType> định nghĩa giao diện trừu tượng
cho một dịch vụ, về các thông báo được gửi đến và đi khỏi dịch vụ đó. Các phần tử
<wsdl:portType> có chứa bất kỳ số lượng các
phần tử con <wsdl:operation> nào. Mỗi phần tử con
<wsdl:operation> cần thuộc tính name riêng của mình (theo yêu cầu của WS-I BP là duy nhất
trong <wsdl:portType>) và chứa một hoặc nhiều phần
tử con hơn để mô tả các thông báo được sử dụng bởi hoạt động (operation). Các phần
tử con thường có ba loại, mô tả các kiểu sử dụng khác nhau:
<wsdl:input>: Dữ liệu được gửi từ khách hàng đến nhà cung cấp dịch vụ làm đầu vào cho hoạt động đó.<wsdl:output>: Dữ liệu được nhà cung cấp dịch vụ trả về cho khách hàng làm kết quả của hoạt động đó.<wsdl:fault>: Dữ liệu được nhà cung cấp dịch vụ trả về cho khách hàng khi có lỗi trong lúc xử lý.
WSDL 1.1 định nghĩa một số mẫu các tương tác giữa khách hàng và nhà cung cấp
dịch vụ, được mô tả theo các thứ tự khác nhau của các phần tử con <wsdl:input> và <wsdl:output>, nhưng không phải tất cả các mẫu được định nghĩa đủ
rõ để được triển khai thực hiện. WS-I BP hạn chế các mẫu còn đúng hai mẫu: các hoạt
động hỏi-trả lời, với một phần tử con <wsdl:input>
tiếp theo là một phần tử con <wsdl:output> và các
hoạt động một chiều, với một phần tử con <wsdl:input> duy nhất. Trong trường hợp về các hoạt động hỏi-trả
lời (kiểu phổ biến nhất, hơn hẳn), có thể tiếp sau các phần tử <wsdl:input> và <wsdl:output> là
bất kỳ số lượng các phần tử <wsdl:fault>
nào.
Mỗi phần tử <wsdl:input>, <wsdl:output> hay <wsdl:fault> tham khảo một mô tả thông báo thông qua các thuộc
tính message cần thiết. Tài liệu tham khảo này đã rõ-vùng
tên, do đó, nó thường cần có một tiền tố. Bạn có thể xem các ví dụ về điều này trong
Liệt kê 1, ví dụ với phần tử <wsdl:input message="tns:getBookMessage"/> được sử dụng trong mô
tả hoạt động getBook. (Tiền tố tns được định nghĩa trên phần tử gốc <wsdl:definitions> với URI vùng tên giống như các thuộc tính targetNamespace).
Bạn có thể nghĩ về các phần tử
<wsdl:portType> như là sự tương đương logic của
một giao diện Java trong hầu hết các chi tiết, với các phần tử <wsdl:operation> tương đương với các phương thức, các phần tử
<wsdl:input> là các tham số phương thức, các
phần tử <wsdl:output> là phương thức trả về và các
phần tử <wsdl:fault> là các trường hợp ngoại lệ
được kiểm tra. Việc tạo mã Java từ WSDL sử dụng các sự tương ứng này, giống như khi
sử dụng hầu hết các công cụ tạo WSDL từ mã Java hiện có.
Các phần tử <wsdl:binding> mô tả một cá
thể của giao diện trừu tượng được định nghĩa bởi một phần tử <wsdl:portType>, đã thấy trong Liệt kê 2 ở
phần đầu của tệp BookServerImpl.wsdl. Thuộc tính type
cung cấp tên rõ của kiểu cổng (portType) do phần tử liên kết (binding) triển khai
thực hiện.
Các phần tử con của <wsdl:binding>
cung cấp các chi tiết về cách triển khai thực hiện kiểu cổng (portType). Các phần tử
con của vùng tên WSDL tương ứng với các phần tử con của <wsdl:portType> và phải sử dụng các giá trị name như nhau —
không phải các tài liệu tham khảo đã rõ-vùng tên, cũng như với tài liệu
tham khảo <wsdl:portType>. Hình1 cho thấy kết nối này ở mức <wsdl:operation> theo các đường chấm chấm. Kết nối tương tự theo
tên áp dụng cho các phần tử con <wsdl:input>/<wsdl:output>/<wsdl:fault> của các phần tử <wsdl:operation>. Mặc cho việc sử dụng lại các tên phần tử giống
nhau này, nội dung của các phần tử này rất khác nhau khi chúng là phần tử con của
một phần tử <wsdl:binding> chứ không phải của một
phần tử <wsdl:portType>.
Phần tử <wsdl:binding> là nơi các phần mở rộng được WSDL định
nghĩa, bắt đầu hoạt động. Phần tử con <soap:binding> được sử dụng trong khi định nghĩa một dịch vụ SOAP
(kiểu dịch vụ duy nhất được WS-I BP cho phép, mặc dù WSDL 1.1 cũng cho phép các liên
kết HTTP). Phần tử <soap:binding> này sử dụng thuộc
tính transport cần thiết để định nghĩa kiểu vận chuyển
được liên kết đó sử dụng. (HTTP, như được hiển thị bằng giá trị http://schemas.xmlsoap.org/soap/http trong Liệt kê
2, là sự lựa chọn duy nhất được WS-I BP cho phép). Thuộc tính tùy chọn style cho phép bạn chọn giữa các kiểu dáng rpc và tài liệu
để mô tả dữ liệu XML (với mặc định phổ biến nhất là document tương ứng với các thông báo khi sử dụng các định nghĩa lược đồ
phần tử, chứ không phải là các định nghĩa kiểu).
Bên trong mỗi phần tử con
<wsdl:operation> của phần tử <wsdl:binding>, có thể sử dụng một phần tử <soap:operation> để chỉ rõ một giá trị SOAPAction để nhận biết các yêu cầu đang gọi hoạt động đó
(và ngoài ra có thể ghi đè lên sự lựa chọn kiểu dáng rpc hay tài liệu do phần tử
<soap:binding> xác định, mặc dù WS-I BP cấm
cách sử dụng này). Mỗi phần tử con <wsdl:input>/<wsdl:output>/<wsdl:fault> có chứa phần tử mở rộng khác, mà trong
trường hợp của Liệt kê 2 phần tử mở rộng đó luôn là <soap:body> (cho biết rằng dữ liệu thông báo được gửi
trong phần thân thông báo SOAP — cũng có thể gửi dữ liệu và thậm chí cả các
lỗi trong các phần tiêu đề của SOAP, mặc dù tôi coi việc này là một cách thực hành
xấu) với một <wsdl:input> hoặc <wsdl:output> hoặc <soap:fault>
tương đương được sử dụng với một <wsdl:fault>.
Thành phần cuối cùng của một mô tả dịch vụ WSDL là
phần tử <wsdl:service>, có chứa một nhóm các phần
tử <wsdl:port>. Mỗi phần tử <wsdl:port> kết hợp một địa chỉ truy cập với một phần tử <wsdl:binding>. Địa chỉ truy cập này do phần tử mở
rộng <soap:address> lồng nhau cung cấp.
Với tất cả các biến thể của các lược đồ và các quy tắc dùng cho các tài liệu WSDL 1.1, không có gì đáng ngạc nhiên là nhiều tài liệu không phù hợp với dạng mẫu các cách thực hành tốt nhất do WS-I BP định nghĩa. Sự hỗ trợ bởi tất cả các chồng các dịch vụ web cho nhiều biến thể từ dạng mẫu các cách thực hành tốt nhất đã giúp duy trì việc sử dụng các cấu trúc đã lỗi thời hoặc không đúng, dẫn đến sự lây lan của các cách thực hành xấu trên toàn ngành công nghiệp phần mềm. Và chắc chắn tôi không được miễn dịch khỏi sự lây lan này — trong khi xem xét các tài liệu WSDL mà tôi đã cung cấp cho mã mẫu trong loạt bài này, tôi đã ngạc nhiên khi thấy rằng điều đó không hoàn toàn chính xác.
Vì vậy, khi tôi bắt đầu viết bài này, tôi đã nghĩ rằng sẽ là tốt để đưa vào một công cụ mà mọi người có thể sử dụng để xác minh các tài liệu WSDL dựa vào các quy tắc các cách thực hành tốt nhất. Nó có vẻ là một bước nhỏ từ đó đến việc chuyển đổi các tài liệu WSDL thành dạng mẫu các cách thực hành tốt nhất, miễn là WSDL ban đầu hoàn toàn không có lỗi. Hóa ra đây là công việc vượt xa hơn tôi đã dự kiến ban đầu và các chi tiết đầy đủ của mô hình đó sẽ được phát triển trong hai bài tiếp theo của loạt bài này.
Nhiều mô
hình khác nhau đã được xây dựng để làm việc với các tài liệu WSDL trong ngôn ngữ
Java, bao gồm Web Services Description Language for Java Toolkit (WSDL4J - Ngôn ngữ
Mô tả các Dịch vụ Web cho bộ công cụ Java) được sử dụng rộng rãi, là công cụ tham
khảo JSR 110 (xem Tài nguyên). Không một mô hình nào trong
số các mô hình đó thực sự có vẻ phù hợp với điều tôi muốn thực hiện, vì hai mục tiêu
đầu tiên là, đọc các tài liệu WSDL theo bất kỳ dạng mẫu hợp lý nửa vời nào và báo
cáo cả các lỗi lẫn các biến thể của các cách thực hành tốt nhất và thứ hai là, viết
các tài liệu WSDL không có lỗi được định dạng lại thành dạng mẫu các cách thực hành
tốt nhất. Ví dụ, WSDL4J sẽ không duy trì thứ tự của các phần tử từ đầu vào để tôi có
thể báo cáo các vấn đề sắp xếp thứ tự và hơn nữa không xử lý các định nghĩa lược đồ
vì thế không thể sử dụng chúng trực tiếp để kiểm tra các tài liệu tham khảo từ các
phần tử <wsdl:part>. Vì vậy, tôi có thể lựa chọn
giữa thiết lập các mục tiêu của mình thực tế hơn hoặc viết mô hình riêng của mình.
Dĩ nhiên là tôi chọn viết mô hình riêng của mình.
Trước đây, tôi đã triển khai thực hiện một mô hình WSDL một phần để sử dụng với liên kết dữ liệu
JiBX, như là một phần của dự án JiBX/WS. Mô hình đó được thiết kế chỉ cho đầu ra và
nó liên quan đến một số lượng tương đối nhỏ các lớp, mà trong một số trường hợp các
lớp đó kết hợp dữ liệu từ các phần tử lồng nhau của cấu trúc XML của WSDL (phần tử
<wsdl:message> được kết hợp với một phần tử con
<wsdl:part> riêng lẻ, các phần tử con <wsdl:input>, <wsdl:output> và <wsdl:fault>
trong một phần tử <wsdl:binding> được kết hợp với
một phần tử <soap:body> hoặc phần tử <soap:fault> và v.v..) Cấu trúc lớp rút gọn này đã
giúp dễ dàng xây dựng các tập con của các tài liệu WSDL do cấu trúc này hỗ trợ,
nhưng khi tôi bắt đầu xem xét một công cụ xác minh và cấu trúc lại dựa vào mô hình
đó, tôi đã nhận ra rằng việc hỗ trợ đầu vào của WSDL có cấu trúc kém thường đòi hỏi
một mô hình gần giống hơn với mô tả XML.
Việc tạo mã từ lược đồ WS-I BP cho WSDL 1.1 đã là một tùy chọn khác. Khi xem xét điều đó, tôi đã nhận ra rằng đúng là việc sử dụng các lớp được tạo ra trực tiếp sẽ là một mớ hỗn độn, vì lược đồ gồm có cả các kiểu dự phòng cũng như một số cấu trúc phức tạp được sử dụng để mô tả các mẫu trao đổi-thông báo khác nhau (một số mẫu trong đó sau này đã bị tài liệu nguyên bản WS-I BP cấm sử dụng).
Vì vậy, tôi đã chấm dứt ngay việc xây dựng các lớp bằng thủ công, mặc dù kết quả cuối cùng đã gần giống như thể tôi đã bắt đầu với mã được tạo ra từ lược đồ và chỉ cần lược bớt phần trùng lặp và phức tạp không cần thiết. Liên kết dữ liệu JiBX hỗ trợ nhiều liên kết tới các lớp giống nhau, vì vậy tôi đã có thể thiết lập liên kết đầu vào để xử lý toàn bộ các tùy chọn do bất kỳ phiên bản nào của WSDL 1.1 cho phép trong khi chỉ cấu hình liên kết đầu ra để chỉ xuất ra WSDL theo một dạng mẫu các cách thực hành tốt nhất.
Liệt kê 3 cho thấy một phần của
lớp Definitions, tương ứng với phần tử gốc <wsdl:definitions>:
Liệt kê 3. Lớp
Definitions (một phần)
public class Definitions extends ElementBase
{
/** Enumeration of child elements, in expected order. */
static enum AddState {
invalid, imports, types, message, portType, binding, service };
/** List of allowed attribute names. */
public static final StringArray s_allowedAttributes =
new StringArray(new String[] { "name", "targetNamespace" });
/** Validation context in use. */
private ValidationContext<ElementBase,Definitions> m_validationContext;
/** Current state (used for checking order in which child elements are added). */
private AddState m_state;
/** Name for this definitions. */
private String m_name;
/** Target namespace for WSDL. */
private String m_targetNamespace;
/** List of all import child elements. */
private List<Import> m_imports = new ArrayList<Import>();
/** List of all types child elements. */
private List<Types> m_types = new ArrayList<Types>();
/** List of all message child elements. */
private List<Message> m_messages = new ArrayList<Message>();
/** List of all portType child elements. */
private List<PortType> m_portTypes = new ArrayList<PortType>();
/** List of all binding child elements. */
private List<Binding> m_bindings = new ArrayList<Binding>();
/** List of all services child elements. */
private List<Service> m_services = new ArrayList<Service>();
/** Map from qualified name to message in this definition. */
private Map<QName,Message> m_nameMessageMap =
new HashMap<QName,Message>();
/** Map from qualified name to port type in this definition. */
private Map<QName,PortType> m_namePortTypeMap =
new HashMap<QName,PortType>();
/** Map from qualified name to message in this definition. */
private Map<QName,Binding> m_nameBindingMap =
new HashMap<QName,Binding>();
/** Map from qualified name to service in this definition. */
private Map<QName,Service> m_nameServiceMap =
new HashMap<QName,Service>();
...
/**
* Check state transitions between different types of child elements.
* If the elements are not in the expected order,
* this flags the first out-of-order element for reporting.
* @param state new add state
* @param comp element component
*/
private void checkAdd(AddState state, ElementBase comp) {
if (m_state != state) {
if (m_state == null || (m_state != AddState.invalid &&
state.ordinal() > m_state.ordinal())) {
// advanced on to another type of child element
m_state = state;
} else if (state.ordinal() < m_state.ordinal()) {
// report child element out of order
m_validationContext.addWarning
("Child element of wsdl:definitions out of order", comp);
m_state = AddState.invalid;
}
}
}
...
/**
* Add an unmarshalled wsdl:message child element. This also indexes the message by
* name for validation access.
*
* @param child
*/
public void addMessage(Message child) {
checkAdd(AddState.message, child);
m_messages.add(child);
addName(child.getName(), child, m_nameMessageMap);
}
...
|
Tổ
chức dữ liệu phần tử-con trong Liệt kê 3 cho thấy cách mô
hình đó hỗ trợ cả đầu vào dạng mẫu-chung lẫn đầu ra các thực hành tốt nhất. Thay vì
sử dụng một danh sách các phần tử con riêng của tất cả các kiểu, nó sử dụng các danh
sách riêng cho từng kiểu. Liên kết JiBX đầu vào xử lý các phần tử con như là một
thiết lập không có thứ tự, gọi một phương thức thiết lập cụ thể theo kiểu phần tử
mỗi khi một phần tử con không sắp xếp theo thứ tự. Phương thức thiết lập này thêm cá
thể vào một danh sách định kiểu chứ không thay thế bất kỳ giá trị có trước nào, như
bạn có thể thấy trong phương thức thiết lập addMessage()
được sử dụng cho các phần tử con <wsdl:message>.
Ngoài ra, mỗi phương thức thiết lập chạy một bước kiểm tra trạng thái để bắt giữ các
trường hợp trong đó các phần tử không theo thứ tự dự kiến.
Người ta cho phép các thuộc tính và các phần tử mở rộng (về cơ bản là bất kỳ các thuộc tính hoặc các phần tử nào không sử dụng vùng tên WSDL 1.1) trong bất kỳ các phần tử WSDL nào. Các cấu hình WS-Policy đã nhúng trong các tài liệu WSDL từ các bài trước trong loạt bài này là một ví dụ về các phần tử mở rộng như vậy, như là các tài liệu tham khảo chính sách thực tế. Cách thực hành tốt nhất với các phần tử mở rộng này là để cho chúng đi trước bất kỳ phần tử con nào trong vùng tên WSDL 1.1 và đó là cách mà chúng được xử lý trong liên kết đầu ra. Liên kết đầu vào xử lý các phần tử và các thuộc tính mở rộng bằng cách sử dụng mã của một lớp cơ sở của các lớp phần tử WSDL, không được hiển thị trong Liệt kê 3 và cho phép các phần tử theo bất kỳ thứ tự nào (tạo ra một cảnh báo nếu chúng đi sau một phần tử của vùng tên WSDL 1.1).
Mô hình này xử lý các phần tử mở rộng đã biết bằng cách sử dụng các liên kết riêng cho mỗi vùng tên mở rộng, mỗi liên kết với thiết lập các lớp riêng của nó. Tôi sẽ trình bày việc xử lý các phần tử mở rộng này chi tiết hơn trong phần đăng tiếp theo của loạt bài Các dịch vụ web Java và cũng sẽ cung cấp thêm chi tiết về mã nguồn trong phần đăng đó.
Một số bước xác minh cơ bản của dữ liệu
WSDL được thực hiện như các đối tượng không sắp xếp theo thứ tự tương ứng với các
phần tử được bổ sung vào cấu trúc cây tài liệu WSDL, như thể hiện trong mã addMessage() ở cuối Liệt kê 3. Mã
này sử dụng phương thức checkAdd() để kiểm tra việc sắp
xếp thứ tự của các phần tử con và phương thức addName()
để bảo đảm một tên hợp lệ đã được cung cấp (tài liệu phù hợp với kiểu lược đồ NCName và giá trị là duy nhất bên trong kiểu phần tử) và để
ánh xạ tên đó tới đối tượng. Nhưng đó chỉ là việc kiểm tra riêng thông tin cơ bản
nhất với phần tử đó; cần sử dụng nhiều mã xác minh hơn để kiểm tra các đặc tính khác
của mỗi phần tử và các mối tương quan giữa các phần tử.
JiBX cho phép bạn gọi
các móc nối mở rộng-người dùng như là một phần của quá trình sắp xếp theo thứ tự và
không theo thứ tự. Mô hình WSDL sử dụng một móc nối mở rộng như vậy, một phương thức
thiết lập-trước, để chạy logic xác minh. Một phương thức thiết lập-trước được gọi
sau khi hoàn thành việc sắp xếp đối tượng liên quan không theo thứ tự, do đó, để
chạy các bước kiểm tra xác minh-đối tượng thường là một cách tốt. Trong trường hợp
xác minh WSDL, cách tiếp cận đơn giản nhất là chạy tất cả xác minh đối tượng ngoài
một phương thức thiết lập-trước riêng lẻ, với phần tử gốc <wsdl:definitions>. Cách tiếp cận này tránh được bất kỳ các vấn đề
nào với các tài liệu tham khảo trước với các thành phần của tài liệu WSDL khi các
thành phần đó không theo thứ tự dự kiến.
Trong bài này, bạn đã thấy những điều cơ bản về cấu trúc và cách sử dụng WSDL, cùng với giới thiệu một mô hình dữ liệu Java cho WSDL được dự kiến để hỗ trợ cả hai việc xác minh các tài liệu WSDL và chuyển đổi chúng sang một dạng mẫu các cách thực hành tốt nhất.
Bài tiếp theo trong loạt bài này tiếp tục thêm về chủ đề này, xem xét các vấn đề thường gặp phải trong khi viết các xác nhận WS-Policy và WS-SecurityPolicy. Nó cũng sẽ trình bày mô hình WSDL và việc xử lý xác minh sâu hơn, bao gồm việc mở rộng mô hình để đưa vào các xác nhận WS-Policy/WS-SecurityPolicy được nhúng trong WSDL.
Học tập
-
WSDL 1.1: Tài liệu tham khảo chính thức cho
phiên bản WSDL cũ hơn, nhưng vẫn được sử dụng rộng rãi nhất.
- Các lược đồ WSDL 1.1: Đặc tả WSDL 1.1 bao gồm một
định nghĩa lược đồ không phù hợp với
văn bản. Sau này lược đồ đó được thay thế bằng lược đồ này, mặc dù
văn bản WSDL 1.1 chưa bao giờ được cập nhật để tham khảo lược đồ sau cùng này. Cuối
cùng, WS-I định nghĩa một phiên bản thứ
ba của lược đồ WSDL 1.1, có lẽ là tốt nhất của gói đó.
-
Mở rộng liên kết WSDL 1.1 với
SOAP 1.2: Mở rộng WSDL 1.1 để hỗ trợ SOAP 1.2.
-
Phần 0 của WSDL 2.0:
Sách vỡ lòng, Phần 1
của WSDL 2.0: Ngôn ngữ cốt lõi và Phần 2 của WSDL
2.0: Các phần phụ: Các tài liệu tham khảo chính thức cho tiêu chuẩn WSDL
2.0.
-
Lược tả
cơ bản về WS-I: WS-I đã hỗ trợ thiết lập các cách thực hành tốt nhất cho
việc sử dụng WSDL 1.1 cũng như nhiều khía cạnh khác của các dịch vụ web.
-
Việc đóng gói tối ưu hóa mã nhị phân-XML
và Cơ chế tối ưu hóa truyền dẫn thông
báo SOAP: Các tiêu chuẩn này định nghĩa cách dữ liệu nhị phân về mặt logic
có thể được nhúng trong các thông báo SOAP 1.2 trong khi đang được gửi riêng, để
tránh chi phí mã hóa. Chúng thay thế các cách tiếp cận cạnh tranh SOAP Messages with Attachments (SwA
- các thông báo SOAP với các phần đính kèm) (được triển khai thực hiện rộng
rãi trong các chồng Java, nhưng không có trong .NET) và Direct
Internet Message Encapsulation (DIME - Đóng gói thông báo Internet trực
tiếp) (được triển khai thực hiện trong .NET, nhưng không có trong hầu hết
các chồng Java) được sử dụng cho các phần đính kèm với SOAP 1.1. Nếu điều đó không
quá khó hiểu, một số chồng cũng triển khai thực hiện XOP/MTOM với SOAP 1.1 bằng cách
sử dụng đề xuất Liên kết SOAP
1.1 với MTOM 1.0.
-
Ngôn ngữ mô tả các Dịch vụ Web cho bộ công
cụ Java (WSDL4J): công cụ tham khảo của JSR 110: Các API Java cho
WSDL là một thư viện được sử dụng rộng rãi để làm việc với các tài liệu
WSDL.
- Duyệt qua hiệu sách
công nghệ với các cuốn sách về các chủ đề kỹ thuật này và khác.
-
Vùng công nghệ Java của
developerWorks: Tìm hàng trăm bài viết về mọi khía cạnh về lập trình Java.
Thảo luận
- Hãy dành tâm trí cho cộng đồng My developerWorks. Kết nối với
những người dùng developerWorks khác trong lúc khám phá các blog, các diễn đàn, các
nhóm và các wiki hướng nhà phát triển.
Dennis Sosnoski là một nhà tư vấn và nhà trợ giúp đào tạo chuyên về các dịch vụ Web và SOA dựa trên-Java. Kinh nghiệm phát triển phần mềm chuyên nghiệp của ông trải suốt hơn 30 năm qua, với một thập kỉ cuối tập trung vào các công nghệ XML và Java phía máy chủ. Dennis là nhà phát triển hàng đầu về dụng cụ liên kết dữ liệu XML JiBX mã nguồn mở, cũng là một người có duyên nợ với khung công tác của các dịch vụ Web Apache Axis2. Ông cũng là một trong những thành viên của nhóm chuyên gia đặc tả kỹ thuật của Jax-WS 2.0 và JAXB 2.0. Xem trang web của ông để có thông tin về các dịch vụ đào tạo và tư vấn của ông.