Các dịch vụ Web Java: Hiểu và mô hình hóa WSDL 1.1

Tìm hiểu cách WSDL 1.1 định nghĩa các dịch vụ web và cách có thể mô hình hóa các tài liệu WSDL theo ngôn ngữ Java để xác minh và chuyển đổi

Vài năm sau khi phê duyệt Web Services Description Language 2.0 (WSDL – Ngôn ngữ mô tả các Dịch vụ Web phiên bản 2.0) như là một tiêu chuẩn của World Wide Web Consortium (W3C - Hiệp hội Mạng toàn cầu), WSDL 1.1 vẫn là dạng mẫu về mô tả dịch vụ web được sử dụng rộng rãi nhất. Dù có tính phổ biến, WSDL 1.1 vẫn có một số vấn đề, như có nhiều lược đồ đang sử dụng và nhiều thay đổi về cách các chồng/ngăn xếp (stack) các dịch vụ web xử lý các tài liệu WSDL. Trong bài này, bạn sẽ tìm hiểu cách cấu trúc các mô tả dịch vụ WSDL 1.1. Bạn cũng sẽ thấy cấu trúc cơ bản của một công cụ Java™ để xác minh các tài liệu WSDL và chuyển đổi chúng thành một dạng mẫu theo "các cách thực hành tốt nhất".

Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc.

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.



29 06 2012

Giới thiệu về loạt bài này

Các dịch vụ Web là một phần quan trọng về vai trò của công nghệ Java™ trong điện toán doanh nghiệp. Trong loạt bài này, nhà tư vấn các dịch vụ web và XML Dennis Sosnoski trình bày các khung công tác và các công nghệ chính rất quan trọng cho các nhà phát triển Java đang sử dụng các dịch vụ web. Hãy theo dõi loạt bài này để cập nhật những phát triển mới nhất trong lĩnh vực này và hiểu rõ cách bạn có thể sử dụng chúng để hỗ trợ cho các dự án lập trình của mình.

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.

Tìm hiểu về WSDL 1.1

Cách sử dụng vùng tên

Bài này sử dụng:

  • Tiền tố wsdl để mô tả vùng tên của WSDL 1.1 http://schemas.xmlsoap.org/wsdl/.
  • Tiền tố soap cho vùng tên http://schemas.xmlsoap.org/wsdl/soap/ được sử dụng cho phần mở rộng SOAP 1.1 với WSDL 1.1.
  • Tiền tố xs cho vùng tên http://www.w3.org/2001/XMLSchema được sử dụng cho các định nghĩa lược đồ XML.

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 thành phần của mô tả

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><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><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><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.

Các chi tiết thành phần

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><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 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><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><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ó.

SOAP 1.1 so với 1.2

SOAP 1.1 đã được sử dụng rộng rãi cho các dịch vụ web do đặc tả này đã được công bố năm 2000. SOAP 1.2 đã được phát triển với sự hỗ trợ rộng lớn hơn của ngành công nghiệp phần mềm thông qua W3C và được công bố như một tiêu chuẩn W3C chính thức vào năm 2007. SOAP 1.2 vừa được tạo tài liệu tốt hơn và vừa sạch hơn so với SOAP 1.1, với một số khía cạnh xấu của 1.1 được phẫu thuật cắt bỏ. Mặc dù cấu trúc này sạch hơn, nhưng với hầu hết các dịch vụ web, có rất ít sự khác biệt thực hành giữa cả hai. Có lẽ tính năng quan trọng nhất của SOAP 1.2 là nó là cách hỗ trợ chính thức duy nhất để sử dụng hỗ trợ nâng cao cho các phần đính kèm SOAP được cung cấp bởi XML-binary Optimized Packaging (XOP – Đóng gói tối ưu hóa mã nhị phân-XML) và SOAP Message Transmission Optimization Mechanism (MTOM - Cơ cấu tối ưu hóa truyền dẫn thông báo SOAP). Tôi đã sử dụng SOAP 1.1 trong loạt bài Các dịch vụ web Java cho đến nay, vì một số chồng cũ hơn không hỗ trợ SOAP 1.2, nhưng 1.2 có thể là một sự lựa chọn tốt hơn để phát triển dịch vụ web mới.

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.


Làm việc với WSDL

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.

Mô hình WSDL

Xác nhận hợp lệ so với xác minh

Tôi đang sử dụng thuật ngữ xác minh (verification) trong bài này để xem xét việc kiểm tra tính chính xác của một tài liệu WSDL, vì thuật ngữ thay thế xác nhận hợp lệ (validation) thường được sử dụng với các tài liệu XML theo nghĩa là kiểm tra các tài liệu dựa vào một định nghĩa lược đồ.

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><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 đó.

Xác minh mô hình

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.


Nhiều phần mở rộng hơ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.

Tài nguyên

Học tập

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.

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, SOA và dịch vụ Web
ArticleID=823436
ArticleTitle=Các dịch vụ Web Java: Hiểu và mô hình hóa WSDL 1.1
publish-date=06292012