Viết các mã tuyệt vời với các API P8 FileNet của IBM, Phần 2: Thám thính công việc của chính bạn

Các kỹ thuật để xem bạn đang đưa lên mạng những gì

Bài viết này nói về ba kỹ thuật khác nhau để xem số lượng dữ liệu mà bạn đang đưa lên mạng và giữ chúng trong các đối tượng của bạn trong bộ nhớ khi sử dụng các giao diện lập trình ứng dụng (API - Application Programming Interface) của P8 FileNet. Hai con số này là vô cùng quan trọng khi nó tác động đến việc điều chỉnh hiệu năng trong mã của bạn. Bài viết này bàn về việc truy tìm trên mạng, ghi nhật ký các API Java nội dung P8 và lớp ObjectDumper tuỳ chỉnh để kết xuất các nội dung của cá thể đối tượng. Nếu bạn hiểu mô tả của lớp ObjectDumper, thì bạn sẽ hiểu rõ hơn về các API nội dung P8 các và mô hình đối tượng của chúng.

Bill Carpenter, Kiến trúc sư phần mềm ECM, IBM

Bill CarpenterBill Carpenter là một kiến trúc sư ECM cho IBM tại Seattle, vùng Washington. Bill đã có kinh nghiệm về nghiệp vụ Quản lý nội dung doanh nghiệp (ECM-Enterprise Content Management) từ năm 1998 như là một nhà phát triển, nhà quản lý phát triển và kiến trúc sư. Ông là đồng tác giả của những cuốn sách IBM FileNet Content Manager Implementation Best Practices and Recommendations (Các khuyến nghị và Cách làm tốt nhất về Triển khai thực hiện quản lý nội dung FileNet của IBM) và Developing Applications with IBM FileNet P8 APIs (Các ứng dụng phát triển với các API FileNet P8 của IBM). Ông có kinh nghiệm từ trước về xây dựng hệ thống phần mềm lớn tại các công ty thuộc nhóm Fortune 50 và cũng từng là CTO của một công ty khởi nghiệp trên Internet. Ông đã có một danh sách gửi thư thường xuyên và là người đóng góp bản vá lỗi cho một số dự án nguồn mở. Bill có bằng Toán học và Khoa học Máy tính của trường đại học bách khoa Rensselaer tại Troy, New York.



30 11 2011

Giới thiệu

Nếu bạn đang sử dụng các API của P8, thì nhiều khả năng là bạn tham gia vào việc viết các ứng dụng mức doanh nghiệp. Nếu bạn tham gia vào việc viết các ứng dụng mức doanh nghiệp, thì sớm hay muộn, hiệu năng cũng sẽ trở thành mục cao nhất trong danh sách ưu tiên của bạn. Ngay cả khi hiệu năng đã rất tuyệt trong môi trường phát triển cô lập của bạn, thì đôi khi vẫn rất khó để dự đoán những gì sẽ xảy ra khi ứng dụng của bạn được triển khai dưới áp lực tải thực tế của nhiều người sử dụng. Các bài viết tới sẽ bàn về một số kỹ thuật mã hóa cụ thể để có hiệu năng tốt nhất. Bài viết này đặt nền tảng cho việc đó bằng cách bàn về một số công cụ và kỹ thuật để biết bạn đang đưa lên mạng những dữ liệu gì. Nó có vẻ như tạm thời chệch hướng khỏi những điều căn bản thiết thực khi mã hóa, nhưng bạn sẽ có các thông tin có giá trị khi bạn đọc các bài viết tới của loạt bài viết này và khi bạn phát triển các ứng dụng của riêng mình.

Đối với rất nhiều ứng dụng, có hai nhân tố then chốt điều khiển hiệu năng:

  • Trước hết, hiệu suất tổng thể thường bị chi phối bởi số lượng các cuộc gọi mạng mà các ứng dụng khách của bạn gọi vào các máy chủ P8. Tùy thuộc vào bạn hỏi ai, các cuộc gọi mạng có thể là: cuộc gọi khứ hồi (R/Ts), gọi thủ tục từ xa (RPCs), truy cập máy chủ và nhiều tên tương tự khác. Tất cả những tên gọi khác nhau ấy đều cùng chỉ một việc: mạng đường ống dính líu đến việc truyền yêu cầu từ máy khách đến máy chủ và việc máy chủ tính toán và truyền về đáp ứng. Trong môi trường phát triển, cuộc gọi RPC có thể chỉ cần vài chục hoặc vài trăm mili giây. Thực vậy, hiếm khi một cuộc gọi RPC của P8 cần đến một phần đáng kể của một giây trong một môi trường cô lập. Trong môi trường chịu tải đầy đủ, khoảng thời gian này tăng nhanh vì phải ganh đua với nhau trên mạng, tại máy chủ và trong một số trường hợp, tại máy khách. Khi mọi thứ bị khuyếch đại, việc loại bỏ hoặc giảm quá tải đến hết mức có thể là đáng công làm. Đối với hai công việc riêng biệt cần phải được thực hiện trên máy chủ P8, thì luôn rẻ hơn (ngoại trừ các trường hợp khó khăn thực sự) nếu bạn có thể thực hiện chúng bằng một RPC duy nhất thay vì hai RPC.
  • Thứ hai, hiệu năng bị ảnh hưởng bởi số lượng dữ liệu – tức là số lượng byte – thực sự được truyền đi trên mạng trong một cuộc gọi RPC. Đây không chỉ là vấn đề của bộ định tuyến mạng có thể xử lý các bit nhanh như thế nào. Các dữ liệu được truyền đi càng nhiều, thì các API trình khách và các đối tác máy chủ của chúng càng cần phải làm việc nhiều hơn để lắp ráp và diễn giải các yêu cầu. Trong môi trường P8, hầu như kích thước của các yêu cầu luôn luôn chỉ là một phần nhỏ của kích thước của các đáp ứng, vì vậy việc cố gắng để điều chỉnh kích thước các yêu cầu hiếm khi đáng công. Việc điều chỉnh kích thước của các đáp ứng RPC thường có thể có tác động đáng kể về hiệu năng, và nó luôn đáng được ít nhất là nghiên cứu.

Bài viết này chỉ bàn về một số điều, chúng sẽ giúp bạn có nhận thức về những gì mà ứng dụng của bạn đang làm ở cả hai lĩnh vực đó.

  • Công cụ dò tìm trên mạng giúp bạn tra tìm dữ liệu ở mức thấp đang truyền trên mạng. Mặc dù nghe có vẻ phức tạp, nhưng các công cụ hiện đại trong lĩnh vực này khá dễ vận hành, và chúng có thể làm đơn giản việc khoan sâu vào các vùng cụ thể.
  • Bằng cách ghi nhật ký API Java nội dung của P8, bạn có thể lệnh cho API cho bạn biết những gì nó đang thực hiện tại các điểm khác nhau. Đây là một kỹ thuật tốt để đánh giá cả về số lượng và kích thước của các RPC.
  • Bạn có thể sử dụng cá thể ObjectDumper trong mã phát triển của riêng bạn để chụp nhanh những gì có ở bên trong một đối tượng API của P8. Đây là việc hữu ích để đánh giá số lượng dữ liệu mà một đối tượng đang chuyên chở ở sau hậu trường và cũng để thấy sự khác biệt của các hoạt động khác nhau với dữ liệu đó.

Sử dụng công cụ dò vết trên mạng mạng

Trình thám thính (sniffer) là gì?

Dò vết trên mạng có hàm ý chung là việc bắt giữ các dữ liệu thô truyền trên mạng. Các công cụ này đôi khi được gọi là bộ phân tích mạng, bộ thám thính mạng (được đặt theo tên của ông Sniffer - nhà phân tích mạng), hoặc là bộ thám thính gói. Các bộ thám thính ban đầu là các bộ phận chuyên dùng của thiết bị độc lập có thể được gắn kết vật lý tới một phân đoạn mạng. Các thiết bị như vậy vẫn còn tồn tại, nhưng hiện nay có các phần mềm tương đương với chúng để sử dụng hàng ngày. Mặc dù công dụng chính ban đầu của các bộ thám thính là để chẩn đoán các vấn đề mạng ở mức độ khá thấp, đôi khi chúng cũng có thể được sử dụng rất hiệu quả để có được bức tranh về truyền thông giữa các thành phần của một ứng dụng phân tán. Dò vết trên mạng nghe có vẻ là một chủ đề phức tạp và khó nắm bắt cho bất kỳ ai, trừ các chuyên gia, nhưng nó rất dễ tiếp cận đối với các nhà phát triển trung bình. Các công cụ có sẵn ngày nay thường có giao diện người dùng tốt và tài liệu hướng dẫn khá dễ hiểu.

Bên cạnh việc bắt và phân tích luồng lưu thông mạng đang hoạt động, các trình thám thính cũng có thể được sử dụng để ghi lưu các luồng lưu thông thành một tệp tin hoặc để đọc tệp tin các luồng lưu thông đã được ghi lưu. Tệp tin như vậy thường được gọi là các tệp tin nắm bắt. Có các định dạng tệp tin bắt luồng lưu thông theo chuẩn thực tế của ngành công nghiệp, do vậy nhiều cơ hội (nhưng không dám chắc) rằng một tệp tin bắt luồng lưu thông do một ứng dụng thám thính tạo ra có thể đọc được bằng một trình thám thính khác.

Nếu bạn chưa có công cụ dò vết trên mạng, thì bằng việc tìm kiếm Web theo bất kỳ một thuật ngữ nào đề cập ở trên sẽ nhận được một số lựa chọn thay thế phổ biến, bao gồm các công cụ mã nguồn mở hoặc miễn phí. Nhằm mục đích minh họa, bài viết này sử dụng Wireshark, một trình thám thính mã nguồn mở mạnh mẽ và phổ biến có sẵn tại trang Wireshark.org. Wireshark phiên bản 1.0.5 được sử dụng cho bài viết này, nhưng nếu bạn sử dụng phiên bản cũ hơn hoặc một công cụ hoàn toàn khác, thì các khái niệm được thảo luận vẫn có thể áp dụng được và bạn không gặp rắc rối nào. Mục tiêu của bài viết không phải là giúp bạn trở thành một chuyên gia về bất kỳ công cụ thám thính cụ thể nào. Thay vào đó, bài viết này cung cấp cho bạn một cái nhìn lướt qua các khả năng để bạn có thể tự mình tiếp tục khám phá.

Bắt giữ kết quả dò vết trên mạng

Mục đích chính của trình thám thính (sniffer) phần mềm là lắng nghe luồng lưu thông qua chồng giao thức nối mạng trên một máy tính mà không can thiệp vào luồng lưu thông đó. Điều này không hoàn toàn giống như lắng nghe chính trên mạng, nhưng cũng gần như thế nếu nói về những gì bạn sẽ làm. Bởi vì trình thám thính cũng là một chương trình chạy trên máy tính, điều đầu tiên bạn phải nghĩ đến là bạn sẽ chạy nó trên máy tính nào. Đôi khi điều này bị chi phối về mặt chức năng bởi những điều bạn đang cố gắng để kiểm tra. Trình thám thính chỉ nhìn thấy những thứ theo quan điểm của máy tính đang chạy nó, và nó thường không thể nhìn thấy lưu thông mạng giữa hai hoặc nhiều máy tính khác. Thực vậy, trong nhiều trường hợp phức tạp hơn, đôi khi cần phải chạy trình thám thính trên nhiều máy tính cùng một lúc để các hoạt động có thể tương quan với nhau. Tuy nhiên, điều đó không cần thiết cho kịch bản này, bởi vì bạn chỉ phải theo dõi các cuộc gọi điểm tới điểm. Sự lựa chọn của bạn rút lại chỉ còn là máy tính nơi mà API máy khách của bạn đang chạy hoặc là máy tính nơi mà máy chủ P8 đang chạy. Giả sử bạn có quyền truy cập như nhau vào cả hai máy tính, thì việc chạy nó trên máy tính khách của bạn có ý nghĩa hơn. Điều đó cho phép bạn tránh các hỗn loạn nhỏ của các yêu cầu từ các máy khách khác nếu máy chủ P8 của bạn được chia sẻ. Nếu máy chủ P8 của bạn không được chia sẻ hoặc có rất ít hoạt động trên đó, thì sự lựa chọn chỉ là vấn đề sao cho tiện cho bạn mà thôi. Đối với các loại tác vụ thường nhật trong kịch bản của bài viết này, thì trình thám thính phần mềm nói chung đặt thêm một tải công việc tối thiểu lên máy chạy nó. Bài viết này giả sử bạn đang chạy trình thám thính trên máy khách, nhưng nếu không phải như vậy thì cũng khá dễ thấy bạn phải làm khác đi những gì.

Giả sử bạn có một ứng dụng đang chạy và muốn quan sát và điều chỉnh các nhân tố liên quan đến hiệu năng được mô tả ở trên. Nếu bạn chưa có ứng dụng của mình và chỉ muốn có một ứng dụng đơn giản để chạy để xem các kỹ thuật trong bài viết này làm việc như thế nào, thì bạn có thể tải về ứng dụng mẫu HelloDocument tại bài viết đầu tiên của loạt bài viết này. Có một số lưu ý mà bạn nên xem xét khi chạy một trình thám thính cùng với ứng dụng của bạn:

  • Hãy sử dụng truyền tải WSI thay vì truyền tải EJB. Truyền tải WSI sử dụng các XML được truyền bằng cách sử dụng HTTP. Mặt khác, truyền tải EJB sử dụng các định dạng và giao thức quyết định bởi máy chủ ứng dụng đang được sử dụng. Nói chung, truyền tải EJB sử dụng các định dạng nhị phân nén hoặc ngắn gọn. Các định dạng này rất tuyệt vời cho truyền thông khách/chủ, nhưng chúng khó xử lý khi giám sát luồng lưu thông mạng một cách trực quan.
  • Đừng sử dụng kiểu bảo vệ TLS/SSL trên các kết nối của bạn. Chính vì mục đích của nó, bảo vệ TLS/SSL làm cho không thể nhìn vào bên trong luồng lưu thông đón bắt được. Ngay cả nếu một bên thứ ba nghe toàn bộ cuộc đối thoại thiết lập kết nối TLS/SSL, thì nó sẽ không thể giải mã được luồng lưu thông được bảo vệ tiếp nối sau. Trình thám thính không có bất kỳ sức mạnh huyền diệu nào để nhìn xuyên qua tấm màn che TLS/SSL. Chạy không có bảo vệ TLS/SSL sẽ để lộ luồng lưu thông và các mật khẩu chưa mã hóa cho những kẻ nghe lén tiềm năng, vì vậy bạn sẽ muốn sử dụng thông tin và các tài khoản không nhạy cảm trong khi bạn chạy các thử nghiệm về trình thám thính của mình. Bởi vì nhiều khả năng là bạn làm điều này trong một môi trường phát triển hoặc thử nghiệm, nên rất dễ dàng để thu xếp.

Nếu như còn chưa đủ rõ ràng, những điểm nêu trên kết hợp cùng nhau có nghĩa là URI kết nối của Máy nội dung của bạn phải là một URI HTTP (không phải là HTTPS và không phải là URI không HTTP).

Cách làm chung là bật trình thám thính lên, chạy ứng dụng, tắt trình thám thính và phân tích luồng lưu thông mạng. Hình 1 cho thấy bắt đầu phiên đón bắt. Mỗi dòng trong cửa sổ trên cùng là một gói tin IP đơn lẻ đã đón bắt được. Trong thuật ngữ của trình thám thính, các gói còn được gọi là khung tin. Chỉ là nói nhẹ đi khi nói rằng đây là một màn hình phức tạp và bận rộn với người còn bỡ ngỡ. Đâu là dữ liệu đáng chú ý của RPC P8 mà bạn đang tìm? Trên thực tế, chúng không có trong hình 1. Hình 1 cho thấy chưa đầy một giây đầu tiên của hoạt động mạng, thậm chí chưa đủ thời gian để đổi sang cửa sổ khác và nhìn thấy ứng dụng của bạn đã bắt đầu.

Hình 1. Thông tin dò vết trên mạng dạng thô, chưa được lọc
Hình 1. Thông tin dò vết trên mạng dạng thô, chưa được lọc

Tất nhiên, bạn có thể cuộn xuống danh sách gói tin để tìm gói tin mà bạn cần tìm. Tuy nhiên, có phải là tốt hơn không nếu có cách nào để bỏ đi những thông tin mà bạn không quan tâm? Tất nhiên là có. Có hai cách tiếp cận để làm việc đó: bạn có thể ra lệnh cho công cụ thám thính ẩn đi, không hiển thị các khung tin không liên quan, hoặc bạn có thể ra lệnh cho công cụ thám thính bỏ qua các khung tin không liên quan trong khi nó đón bắt luồng lưu thông. Với cách tiếp cận đầu tiên, bạn có thể thay đổi ý định của bạn sau này và hiển thị lại các khung tin ẩn. Với cách thứ hai, các khung tin biến mất mãi mãi. Ưu điểm của phương pháp thứ hai là dung lượng các tệp tin chụp được nhỏ đi tương ứng. Điều này tạo ra sự khác biệt nếu bạn chạy với thời gian dài hơn. Vì bạn chủ yếu dò vết trên mạng trong các phiên làm việc ngắn và có thể chạy lại toàn bộ phiên làm việc nếu bạn bỏ sót một cái gì đó, nên sự lựa chọn gần như là vấn đề sao cho thuận tiện cho riêng bạn mà thôi. Nếu bạn bắt giữ dấu vết trên mạng để gửi cho người khác, thì người đó có thể có những ưu tiên về cách lọc (hoặc không lọc) thông tin trong khi bắt giữ luồng lưu thông mạng.

Mỗi trình thám thính có giao diện người dùng của riêng mình và các phương thức để thiết lập điều kiện lọc. Đối với một số trình thám thính, có sự khác biệt về cách thực hiện việc lọc trong hai phương pháp tiếp cận đó. Hầu hết các trình thám thính làm cho việc lọc trở nên khá dễ dàng, mặc dù có một số thuật ngữ phải làm quen trước khi bạn biết chính xác công cụ đó là gì. Dưới đây là một số tiêu chí cần thiết cho việc lọc khung tin. Các con số này đơn giản là tinh chỉnh các dữ liệu được hiển thị trong hình 1.

  • Chỉ lấy TCP. Đối với truyền tải WSI, tất cả luồng lưu thông của P8 truyền qua các kết nối TCP. Do đó việc lọc tất cả luồng lưu thông UDP là cần thiết. Có một trường hợp mà việc giữ lại luồng lưu thông UDP có thể hữu ích. Nếu bạn đang xử lý sự cố DNS (bài viết này giả định không phải là như thế), thì bạn thấy hầu hết luồng lưu thông DNS truyền qua UDP. Mạng cục bộ của bạn cũng thường có những thứ lạc lõng, không phải là TCP hay UDP. Trong thực tế, trừ khi bạn đang ở trên một mạng rất cô lập, có thể bạn sẽ ngạc nhiên khi thấy khá nhều những thứ lạc lõng đó. Ta có thể giải trí và học được nhiều khi xem các gói đó và tìm hiểu xem chúng là gì, nhưng chúng không liên quan gì tới kịch bản này. Hình 2 cho thấy cùng kết quả dò vết trên mạng như trong hình 1 nhưng chỉ hiển thị các khung tin TCP.

    Hình 2. Dò theo mạng được lọc theo TCP
    Hình 2. Dò theo mạng được lọc theo TCP
  • Chỉ lấy máy chủ đích. Ngay cả bằng cách hạn chế dò vết trên mạng, chỉ lấy các khung tin TCP, bạn vẫn thấy nhiều khung tin không liên quan. Thực vậy, trong hình 2, không có khung tin nào liên quan, và bạn vẫn chỉ thấy chưa đầy hai giây đầu tiên của luồng lưu thông trên màn hình đầu tiên. Cuộc hội thoại màn hình bạn muốn bắt giữ là cuộc hội thoại điểm đến điểm. Do bạn đang chạy trình thám thính trên máy khách, hãy lọc để hiển thị chỉ các khung tin từ máy chủ. Bạn phải cẩn thận khi thiết lập bộ lọc này là bao gồm các gói tin cả đến và đi từ máy chủ đích. Nếu không, bạn sẽ chỉ thấy một nửa cuộc hội thoại. Đối với kịch bản ví dụ này, máy chủ đích có địa chỉ IP là 9.39.109.62. Hình 3 cho thấy kết quả dò vết trên mạng được tiếp tục lọc theo địa chỉ IP đích. Tùy thuộc vào công cụ thám thính và các thiết lập của mình, bạn có thể có thể làm điều tương tự bằng cách sử dụng tên máy chủ thay vì địa chỉ IP.

    Hình 3. Theo dõi mạng được lọc theo máy chủ đích
    Hình 3. Theo dõi mạng được lọc theo máy chủ đích
  • Chỉ lấy cổng đích. Bạn có thể tiếp tục lọc kết quả dò vết trên mạng theo cổng đích cụ thể mà bạn đang sử dụng. Một lần nữa, bạn muốn lấy các gói tin đến hoặc đi từ cổng đích đó. Trong kịch bản ví dụ này, số cổng là 9080, đó là mặc định của WebSphere cho truyền tải WSI của P8. Hình 4 cho thấy kết quả dò vết trên mạng được tiếp tục lọc theo cổng đích.

    Hình 4. Dò vết trên mạng theo cổng đích
    Hình 4. Dò vết trên mạng theo cổng đích

    Lưu ý thêm, bạn có thể tự hỏi tại sao cột Info dường như đang gọi cổng đích glrpc. Theo mặc định, Wireshark cố gắng dán nhãn mọi thứ để thuận tiện cho người sử dụng. Trong sổ đăng ký số hiệu cổng chính thức (xem Tài nguyên) mà Tổ chức cấp phát số hiệu Internet IANA (Internet Assigned Numbers Authority) duy trì, số cổng TCP 9080 được gọi là Groove GLRPC, vì vậy đó là lý do mà Wireshark dán nhãn nó như thế. Đó chỉ là một nhãn hiệu, và bạn không phải lo lắng về nó như bạn sẽ thấy trong phần tiếp theo. Tại cửa sổ ở giữa của hình 4, bạn có thể thấy rằng glrpc thực sự là cổng 9080.

  • Wireshark có thể thực hiện nhiều kiểu giải mã đặc thù riêng cho mỗi giao thức nếu nó biết đang xử lý giao thức nào. Bạn nhấn Analyze > Decode as để diễn giải luồng lưu thông của cổng 9080 thành HTTP. Xin xem hình 5..

    Hình 5. Giải mã Wireshark
    Hình 5. Giải mã Wireshark

    Hình 6 cho thấy kết quả dò vết trên mạng khi áp dụng giải mã HTTP. Một số gói tin tiếp tục được đánh dấu là TCP mặc dù chúng liên quan đến cổng 9080. Đó là vì chúng không có bất kỳ đặc thù riêng thực sự nào về HTTP. Các gói tin này là các gói tin mạng báo đã nhận biết và kế toán giao thức TCP khác. Về mặt lý thuyết, bạn có thể đánh dấu nhầm một số gói tin với cổng nguồn 9080 thành HTTP, trong khi chỉ có cổng đích 9080 là liên quan. Trên thực tế, điều này là hiếm.

    Hình 6. Dò vết trên mạng được giải mã thành HTTP
    Hình 6. Dò vết trên mạng được giải mã thành HTTP

Bây giờ bạn có các dấu vết được lọc chỉ còn các gói tin đáng chú ý, bạn cần xem gói nào? Hình 6 làm nổi bật khung tin 295 và mở rộng hai cửa sổ phía dưới để hiển thị nhiều chi tiết hơn. Có lẽ bạn đã suy luận ra từ các thông tin hiển thị ở đây rằng bạn đang tìm kiếm một đáp ứng thành công từ trình lắng nghe dịch vụ Web trên máy chủ nội dung P8. Công cụ trình thám thính hiển thị mọi thứ ở mức gói tin, vì vậy bạn có thể thấy các giá trị của tất cả các trường tiêu đề của IP và TCP cũng như chính nội dung HTTP được chuyển tải. Điều này tiện dụng cho một số việc, nhưng cuộc thoại HTTP có thể lan ra gồm nhiều gói tin TCP. Dĩ nhiên là có ít nhất một gói tin cho yêu cầu và một gói cho đáp ứng, nhưng rất nhiều khả năng là một trong hai gói hoặc cả hai gói sẽ còn tràn vào các gói tin tiếp theo. Vì bạn quan tâm chủ yếu đến nội dung HTTP được chuyển tải, nên sẽ là tốt hơn nếu bạn chỉ thấy nó mà không kèm theo mọi gói tin kế toán khác. Cũng sẽ tốt hơn nếu bạn có thể sẵn sàng hiển thị trực quan yêu cầu cùng với đáp ứng của nó. Wireshark có tính năng làm được điều này. Nếu bạn nhấn phím phải chuột lên gói tin HTTP và chọn Follow TCP stream, thì Wireshark bật lên cửa sổ giống như cửa sổ trong hình 7.

Hình 7. Cửa sổ Following TCP stream
Hình 7. Cửa sổ Following TCP stream

Vì hình 7 có thể khó đọc một chút, nên liệt kê 1 chứa chính các thông tin này với một số thay đổi nhỏ. Để làm cho nó vừa với chiều rộng của một trang, một số các dòng liên tục của XML và các dòng quá dài bị ngắt đoạn tại các điểm bất kỳ. Những chỗ xuống dòng nhúng trong đó được phiên dịch thành chuỗi hai ký tự ^M có thể nhìn thấy được.

Liệt kê 1. Văn bản từ hình 7
POST /wsi/FNCEWS40MTOM/ HTTP/1.1^M
User-Agent: IBM FileNet P8 CE Java API (X.Y.Z / dapNNN.MMM)^M
Date: Sat, 24 Jan 2009 00:58:55 +0000^M
Content-Type: application/soap+xml; charset=UTF-8^M
Host: MyCEServer:9080^M
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2^M
Connection: keep-alive^M
Transfer-Encoding: chunked^M
^M
29a^M
<?xml version="1.0" encoding="UTF-8"?><e:Envelope 
xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.filenet.com/ns/fnce/2006/11/ws/schema" 
xmlns:e="http://www.w3.org/2003/05/soap-envelope" 
xmlns:d="http://www.w3.org/2001/XMLSchema">
<e:Header><Security><UsernameToken>
<Username>user1234</Username><Password>pass9876</Password>
</UsernameToken></Security><Localization><Locale>en-US</Locale>
<Timezone>-08:00</Timezone></Localization></e:Header>
<e:Body><GetObjectsRequest><ObjectRequest id="1" cacheAllowed="1">
<SourceSpecification i:type="ObjectReference" classId="Domain">
</SourceSpecification></ObjectRequest></GetObjectsRequest>
</e:Body></e:Envelope>^M
2^M
^M
0^M
^M
HTTP/1.1 200 OK^M
Server: Systinet Server for Java/6.5.4 (Java/1.5.0; Windows Server 2003/5.2 
    build 3790 Service Pack 2; build SSJ-6.5.4-20060829-1824)^M
Content-Type: multipart/related; 
    boundary=----------MULTIPART_BOUNDARY_11f0627864bcdb----------; 
    type="application/xop+xml"; start-info="application/soap+xml"; 
    start="<32bb849648573a7a-1c0d723b50a78063-2392a2760337fe0f-f87db666f64901d6>"^M
Content-Language: en-US^M
Transfer-Encoding: chunked^M
Date: Sat, 24 Jan 2009 01:01:06 GMT^M
^M
d14^M
^M
------------MULTIPART_BOUNDARY_11f0627864bcdb----------^M
Content-Transfer-Encoding: binary^M
Content-Type: application/xop+xml; type="application/soap+xml"; charset=UTF-8^M
X-WASP-Message-ID: 101-bcH4nI/ftBJRilNZkDiIkg==^M
Content-ID: <32bb849648573a7a-1c0d723b50a78063-2392a2760337fe0f-f87db666f64901d6>^M
^M
<?xml version="1.0" encoding="UTF-8"?>
<e:Envelope xmlns:e="http://www.w3.org/2003/05/soap-envelope" 
xmlns:d="http://www.w3.org/2001/XMLSchema" 
xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:wn0="http://www.filenet.com/ns/fnce/2006/11/ws/schema">
<e:Body><GetObjectsResponse 
xmlns:n0="http://www.filenet.com/ns/fnce/2006/11/ws/MTOM/schema" 
xmlns="http://www.filenet.com/ns/fnce/2006/11/ws/schema">
<ObjectResponse id="1" i:type="SingleObjectResponse">
<Object i:type="ObjectValue" classId="Domain" 
objectId="{064735FF-3160-45D3-8258-ABA3AAE8F204}" updateSequenceNumber="5" 
accessAllowed="459267"><Property i:type="UnevaluatedCollection" 
propertyId="RenditionEngineConnections" collectionType="Enum"/>
<Property i:type="UnevaluatedCollection" propertyId="ContentCacheAreas" 
collectionType="Enum"/><Property i:type="SingletonId" 
propertyId="Id"><Value>{064735FF-3160-45D3-8258-ABA3AAE8F204}</Value>
</Property><Property i:type="UnevaluatedCollection" propertyId="FixedContentDevices" 
collectionType="Enum"/><Property i:type="SingletonBinary" 
propertyId="PublicKey">
<Value>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCshauwe2PIcNhCLGXhvL40DDFnNxcu1Ua
jWNe/KQ4+tQ5solDS+JGPcv1m9sOIaQpxGh2uq/luGvuZh9EiHdfJMlfVAxkjdc7ZHI/oCtaook4CCYMx7Xx
ELBu4kJAkWEr324PXfHRgqlHmv7dXkO2NGvTfa+bDypnzPDEKfEDKjwIDAQAB</Value>
</Property><Property i:type="UnevaluatedCollection" 
propertyId="PEConnectionPoints" collectionType="Enum"/>
<Property i:type="UnevaluatedCollection" propertyId="AddOns" 
collectionType="Enum"/><Property i:type="UnevaluatedCollection" 
propertyId="ObjectStores" collectionType="Enum"/><Property 
i:type="SingletonString" propertyId="EncryptionAlgorithm">
<Value>RSA/NONE/PKCS1PADDING</Value></Property><Property 
i:type="UnevaluatedCollection" propertyId="Sites" 
collectionType="Enum"/><Property i:type="UnevaluatedCollection" 
propertyId="SubsystemConfigurations" settable="1" 
collectionType="List"/><Property i:type="SingletonString" 
propertyId="Name" settable="1"><Value>WAS_SQL</Value>
</Property><Property i:type="UnevaluatedCollection" 
propertyId="DirectoryConfigurations" settable="1" collectionType="List"/>
<Property i:type="SingletonObject" propertyId="DefaultSite" 
settable="1"><Value i:type="ObjectReference" classId="Site" 
objectId="{07539A4C-DD66-492B-8A0B-BC2363C9BDB9}"/></Property>
<Property i:type="SingletonObject" propertyId="VerityDomainConfiguration">
<Value i:type="Unevaluated"/></Property><Property i:type="UnevaluatedCollection" 
propertyId="IsolatedRegions" collectionType="Enum"/><Property 
i:type="UnevaluatedCollection" propertyId="PermissionDescriptions" 
collectionType="List"/><Property i:type="UnevaluatedCollection" 
propertyId="MarkingSets" collectionType="Enum"/><Property 
i:type="SingletonObject" propertyId="ClassDescription"><Value 
i:type="ObjectReference" classId="ClassDescription" 
objectId="{FCEEED8B-AE17-441C-AD04-56E560255331}"/></Property><Property 
i:type="UnevaluatedCollection" propertyId="Permissions" settable="1" 
collectionType="List"/></Object></ObjectResponse>
</GetObjectsResponse></e:Body></e:Envelope>^M
3d^M
------------MULTIPART_BOUNDARY_11f0627864bcdb------------^M
^M
0^M

Nếu bạn chưa nhìn thấy cửa sổ này trước đây, thì bạn có thể băn khoăn về các con số đứng riêng ra, rải rác cùng với yêu cầu và đáp ứng (29a, d14 v.v..) trong Liệt kê 1. Chúng xuất hiện bởi vì bạn đang sử dụng các khúc (chunking) HTTP. Chúng là các con số thập lục phân chỉ ra có bao nhiêu byte trong khúc tiếp theo. Bạn hãy lưu ý rằng trong cả hai trường hợp, khúc cuối cùng không chứa byte nào. Trong Liệt kê 1, các con số sẽ không phải là tổng chính xác do bị ngắt bởi mã khuôn dạng. Nếu bạn muốn tìm hiểu thêm về phân khúc, bạn có thể tham khảo các đặc tả HTTP 1.1, RFC-2616 (xem phần Tài nguyên). Nếu không, bạn chỉ cần bỏ qua các con số nhỏ đó.

Điều đó không xảy ra trong kịch bản ví dụ, nhưng bạn có thể thấy nhiều cặp yêu cầu và đáp ứng trong cùng một dòng TCP. Một lần nữa, đó là vì bạn đang sử dụng kết nối HTTP liên tục. Có nhiều yêu cầu và đáp ứng theo quan điểm của HTTP, nhưng chỉ có một cuộc thoại theo quan điểm TCP. Một trường hợp đặc biệt quan trọng xảy ra khi máy chủ gặp lỗi ngoại lệ. Lỗi ngoại lệ được truyền ngược lại máy khách như là lỗi SOAP, và thông thường kết nối được đóng lại sau khi lỗi SOAP được gửi đi. Điều này không tạo ra sự khác biệt gì ở mức API, và thậm chí bạn sẽ không biết về nó nếu bạn không nhìn vào dấu vết mạng.

Bây giờ bạn có các chi tiết của cuộc thoại trước mặt, bạn có thể diễn giải chúng như thế nào? Có thể bạn đã đoán rằng XML ở đây đã được đặc tả hoàn toàn đầy đủ bởi tệp tin WSDL (Web Services Definition Language - Ngôn ngữ định nghĩa dịch vụ Web) của Dịch vụ Web của Máy nội dung (CEWS - Content Engine Web Services). Đối với những thứ thường nhật mà bạn đang tìm kiếm (có hay không các thuộc tính cụ thể, số lượng đối tượng v.v..), thì có thể bạn sẽ thấy là tương đối dễ dàng để chọn ra các thông tin ấy từ XML mà không cần hiểu đầy đủ tất cả các sắc thái của tệp tin WSDL. Mặc dù vậy, bạn có thể thấy sẽ là hữu ích nếu hiển thị XML trong các định dạng dễ đọc hơn. Nếu bạn ghi lưu nó vào một tệp tin có phần đuôi là xml, thì hầu hết các trình duyệt web sẽ làm tốt công việc hiển thị nó trong một định dạng có cấu trúc, dễ đọc. Các tương tác trong hình 7 và liệt kê 1 là một RPC kết quả từ cuộc gọi đến Factory.Domain.fetchInstance không có tên miền và bộ lọc thuộc tính. Đáp ứng là một đối tượng tên miền bao gồm một tập hợp các thuộc tính (hầu hết các thuộc tính đó là các tập hợp chưa tính toán giá trị, chúng ta sẽ thảo luận về chúng trong bài viết tới về các bộ lọc thuộc tính).

Ưu điểm và nhược điểm của dò vết trên mạng

Ưu điểm

  • Dò vết trên mạng cung cấp cho bạn một hình ảnh hoàn toàn trung thực về thông tin trên mạng. Bạn không bao giờ phải lo lắng về việc thấy các thông tin gần đúng hoặc mất mát một phần.
  • Việc dò vết trên mạng không xâm chiếm mạng. Bạn có thể chọn chạy dò mạng bất cứ lúc nào mà không cần giả mạo nhiều tới cấu hình ứng dụng của bạn (nhưng bạn nên xem phần Nhược điểm).
  • Tác động đến hiệu năng nói chung là không đáng kể. Mặc dù các trình thám thính phần mềm chạy trên một trong số các máy tính có liên quan, chúng vẫn có xu hướng tiêu thụ khá ít tài nguyên.

Nhược điểm

  • Đối với trường hợp đặc biệt dò vết trên mạng các tương tác API Java và .NET của Máy nội dung P8, bạn chỉ thực sự có cơ hội để thấy bất cứ điều gì có ý nghĩa nếu bạn sử dụng truyền tải WSI (luôn luôn là như vậy đối với API của khung công tác .NET) và tránh không dùng mật mã hóa TLS/SSL. Theo nghĩa này, bạn có thể sử dụng hoặc không sử dụng cấu hình thực tế cho ứng dụng của bạn. Tuy nhiên, nhằm mục đích xem các khía cạnh về hiệu năng mà bạn có thể tác động đến bằng các kỹ thuật mã hóa của mình, thì những điều đó không quan trọng.
  • Tương tự như vậy, do việc mật mã hóa và các yếu tố khác, bạn có thể không có khả năng có được các kết quả dò vết trên mạng có ý nghĩa với luồng lưu thông trên mạng riêng ảo (VPN). Đây có thể là một bất tiện đáng kể cho những người làm việc từ xa.
  • Như các con số ở phần trên thể hiện, sự lộn xộn của kết quả dò vết trên mạng có thể làm cho bạn chao đảo một chút cho đến khi bạn quen với nó. Nó cũng đem lại một chút cảm giác về mặt kỹ thuật. Trên thực tế, bạn sẽ làm quen với nó khá nhanh chóng, và bạn sẽ có thể định cấu hình cho công cụ dò vết trên mạng của mình để thấy những gì bạn muốn.
  • Nếu bạn chạy dò vết trên mạng cho các phiên làm việc dài, thì các tệp tin kết quả bắt giữ được trở nên khá lớn. Điều này có thể là vấn đề khi sử dụng dò vết trên mạng cho các bài toán khác. Trong bất kỳ trường hợp nào, vấn đề này có thể được giảm nhẹ đi bằng cách thiết lập các quy tắc lọc cho giai đoạn bắt giữ thông tin.

Sử dụng nhật ký dò vết API

Máy nội dung (CE) P8 sử dụng gói phần mềm mã nguồn mở phổ biến log4j, một dự án của Quỹ phần mềm Apache (Apache Software Foundation - xem phần Tài nguyên), để ghi kết quả dò vết trên mạng, cả trên máy chủ CE và trong các API Java. Phần này của bài viết thảo luận về cách định cấu hình và sử dụng gói phần mềm này.

Định cấu hình ghi nhật ký

Cũng giống như trường hợp dò vết trên mạng, bạn có thể ghi nhật ký dò vết API hoặc trên máy chủ CE hoặc từ trình khách API của mình. Tùy bạn lựa chọn, nhưng một lần nữa, hãy cân nhắc rằng có thể có một số lượng đáng kể các tư liệu không liên quan gì trong các bản ghi nhật ký của máy chủ nếu máy chủ CE được chia sẻ hoặc bận. Đối với các API của khung công tác .NET, bạn phải áp dụng việc ghi nhật ký dò vết tại phía máy chủ. Kịch bản của bài viết này giả định rằng bạn đang thực hiện ghi nhật ký API phía khách trong API Java.

FileNet Enterprise Manager (FEM) chứa giao diện người dùng để định cấu hình cho việc ghi nhật ký phía máy chủ, nhưng tệp tin cấu hình điều khiển ghi nhật ký API phía khách. Tệp tin cấu hình của log4j có thể hoặc ở định dạng XML (theo quy ước được gọi là log4j.xml) hoặc ở định dạng tệp tin thuộc tính Java (theo quy ước được gọi là log4j.properties). Chi tiết đầy đủ về các định dạng này có thể tìm thấy tại trang Web log4j (xem phần Tài nguyên). Định dạng XML linh hoạt hơn và có nhiều tính năng hơn, nhưng định dạng tệp tin thuộc tính đơn giản hơn một chút để làm việc, tùy thuộc vào sở thích của bạn. Sản phẩm FileNet Content Manager của IBM có tệp tin định cấu hình mẫu (log4j.properties.client) mà bạn có thể chỉnh sửa và đổi tên thành log4j.properties. Liệt kê 2 là nội dung của tệp tin đó, được gửi kèm theo với FileNet Content Manager phiên bản 4.5 (với một vài thay đổi không quan trọng để làm cho nó phù hợp với định dạng của bài viết này).

Liệt kê 2. Tệp mẫu log4j.properties.client
################################################################################
# Root logger
################################################################################
log4j.rootLogger=off, FileNetNullAppender
################################################################################
# Appenders
################################################################################
#=== FileNetNullAppender
log4j.appender.FileNetNullAppender=org.apache.log4j.varia.NullAppender

#=== FileNetConsoleAppender
log4j.appender.FileNetConsoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.FileNetConsoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.FileNetConsoleAppender.layout.ConversionPattern=%d %5p [%t] - %m\r\n

#=== FileNetErrorAppender
log4j.appender.FileNetErrorAppender=org.apache.log4j.FileAppender
log4j.appender.FileNetErrorAppender.File=/p8_api_error.log
log4j.appender.FileNetErrorAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.FileNetErrorAppender.layout.ConversionPattern=%d %5p [%t] - %m\r\n

#=== FileNetTraceAppender
log4j.appender.FileNetTraceAppender=org.apache.log4j.FileAppender
log4j.appender.FileNetTraceAppender.File=/p8_api_trace.log
# This is the layout that the TraceLoggingConfiguration framework on the server uses.
# To use this layout , jace.jar must be present in the classpath.
#log4j.appender.FileNetTraceAppender.layout=com.filenet.apiimpl.util.TraceLayout
# Comment out the following lines if using the FileNet TraceLayout
log4j.appender.FileNetTraceAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.FileNetTraceAppender.layout.ConversionPattern=%d %5p [%t] - %m\r\n

#=== FileNetErrorRollingAppender
log4j.appender.FileNetErrorRollingAppender=org.apache.log4j.RollingFileAppender
log4j.appender.FileNetErrorRollingAppender.File=/p8_api_error.log
log4j.appender.FileNetErrorRollingAppender.MaxFileSize=100MB
log4j.appender.FileNetErrorRollingAppender.MaxBackupIndex=1
log4j.appender.FileNetErrorRollingAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.FileNetErrorRollingAppender.layout.ConversionPattern=%d %5p [%t] - %m\r\n

#=== FileNetTraceRollingAppender
log4j.appender.FileNetTraceRollingAppender=org.apache.log4j.RollingFileAppender
log4j.appender.FileNetTraceRollingAppender.File=/p8_api_trace.log
log4j.appender.FileNetTraceRollingAppender.MaxFileSize=100MB
log4j.appender.FileNetTraceRollingAppender.MaxBackupIndex=1
# This is the layout that the TraceLoggingConfiguration framework on the server uses.
# To use this layout , jace.jar must be present in the classpath.
#log4j.appender.FileNetTraceRollingAppender.layout=com.filenet.apiimpl.util.TraceLayout
# Comment out the following lines if using the FileNet TraceLayout
log4j.appender.FileNetTraceRollingAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.FileNetTraceRollingAppender.layout.ConversionPattern=%d %5p [%t] - %m\r\n
################################################################################
# Error Loggers:
#
# Set log level to either one of off/fatal/error/warn/info.
# Child logger's value overwrites parent logger's value.
# If a logger is not specified, it inherits its value from its parent.
# By default, error logging is set to level ERROR.
################################################################################
# Don't comment out the following line since it has appenders.
log4j.logger.filenet_error = error, FileNetConsoleAppender, \
                     FileNetErrorRollingAppender, FileNetTraceRollingAppender

#=== SubSystem: api
# Uncomment to set error logging level to WARN.
#log4j.logger.filenet_error.api = warn
################################################################################
# Trace loggers:
#
# Setting log level to "off" will turn off trace logging.
# Setting log level to "debug" will turn on trace logging.
#
# Child logger's value overwrites parent logger's value.
# If a logger is not specified, it inherits its value from its parent.
# By default, trace logging is off. 
#
# The general message trace logging has a hierarchy of three detail levels represented
# with hierarchical logger names:
#    ...detail.moderate.summary  -- Enables a summary level of tracing
#    ...detail.moderate          -- Enables a moderate level of tracing
#    ...detail                   -- Enables the most detailed level of tracing
#
# Uncomment corresponding lines to turn tracing on.  To enable trace logging
# for all subsystems and all flags, set the level of the filenet_tracing
# logger to debug.
#
# Here are the trace flags used for each sub system.  For message tracing, enable the line
# corresponding to the desired detail level.
#    log4j.logger.filenet_tracing.<SubSystem>.timer                   = debug
#    log4j.logger.filenet_tracing.<SubSystem>.detail.moderate.summary = debug
#    log4j.logger.filenet_tracing.<SubSystem>.detail.moderate         = debug
#    log4j.logger.filenet_tracing.<SubSystem>.detail                  = debug
#
# For example:
#    log4j.logger.filenet_tracing.api.detail.moderate = debug
################################################################################
# Don't comment out the following line since it includes an appender.
log4j.logger.filenet_tracing = off, FileNetTraceRollingAppender

#=== SubSystem: api
# Uncomment one or more lines to enable tracing.
#log4j.logger.filenet_tracing.api                          = debug
#log4j.logger.filenet_tracing.api.timer                    = debug
# Remove the comment corresponding to the desired detail level
#log4j.logger.filenet_tracing.api.detail.moderate.summary  = debug
#log4j.logger.filenet_tracing.api.detail.moderate          = debug
#log4j.logger.filenet_tracing.api.detail                   = debug

Có lẽ bạn đoán được từ kích thước và số lượng các chi tiết rằng tệp tin cấu hình mẫu rất linh hoạt, cho phép nhiều kịch bản ghi nhật ký. Nếu bạn muốn biết thêm về log4j, thì có thể thấy sẽ biết được rất nhiều qua nghiên cứu các khía cạnh của cấu hình này trong các tài liệu của log4j. Bài viết này chỉ liên quan đến một vài dạng, tất cả chúng đều được điều khiển bởi một số dòng đã đánh dấu chú giải ở gần cuối của tệp tin và được lặp lại trong liệt kê 3. Việc ghi nhật ký dò vết API bao gồm ba mức độ chi tiết: tóm tắt, trung bình và chi tiết, mỗi mức độ được kích hoạt bằng cách bỏ dấu chú giải tại đầu dòng thích hợp trong tệp tin cấu hình. Bài viết này nêu các ví dụ ở mức tóm tắt và mức trung bình cho hai hoạt động: tìm nạp tên miền mà bạn đã thấy trong các ví dụ dò vết trên mạng (getObjectsRequest), và một cố gắng bất thành để tạo ra một thư mục trong kho đối tượng không tồn tại (executeChangesRequest). Bài viết này không nêu ví dụ ở mức dò vết chi tiết, bởi vì những điều bổ sung được ghi lại không thú vị lắm cho bài thảo luận này. Điều quan trọng là nhận thấy rằng kết quả của việc ghi nhật ký dò vết nhằm mục đích chủ yếu là giúp đỡ chẩn đoán để khắc phục nhiều loại vấn đề. Các thông điệp cụ thể và các thông tin được ghi nhật ký lại không được bảo đảm là hoàn toàn giống nhau qua những lần phát hành khác nhau và những thay đổi này thường không được đưa vào tài liệu. Các thông điệp bị thay đổi khá thường xuyên.

Liệt kê 3. Các mức chi tiết của việc dò vết API
# Remove the comment corresponding to the desired detail level
#log4j.logger.filenet_tracing.api.detail.moderate.summary  = debug
#log4j.logger.filenet_tracing.api.detail.moderate          = debug
#log4j.logger.filenet_tracing.api.detail                   = debug

Kết quả đầu ra của việc ghi nhật ký dò vết API có trong tệp tin /p8_api_trace.log. Nếu địa điểm đó không thuận tiện, bạn có thể dễ dàng tìm thấy tất cả các lần xuất hiện của tên tệp tin này trong tệp tin cấu hình log4j và thay đổi chúng khác đi. Cấu hình này sử dụng cái mà log4j gọi là cuộn vòng trình nối thêm (appender) tệp tin. Điều đó có nghĩa là các dòng ghi nhật ký tiếp tục tích lũy trong một tệp tin cho đến khi tệp tin đạt đến một kích thước nhất định. Tại thời điểm đó, tệp tin được di chuyển sang một bên, và một tệp tin mới được bắt đầu. Điều này rất tiện dụng khi việc ghi nhật ký của bạn chỉ chạy một lúc thôi. Nhưng với việc phát triển lặp lại nhiều lần, khi bạn tinh chỉnh mã của bạn để điều chỉnh hiệu năng, có thể thêm dòng mã sau vào phần định nghĩa trình nối thêm: log4j.appender.FileNetTraceRollingAppender.Append = false. Bạn phải chắc chắn cũng thực hiện các thay đổi tương tự cho các phần định nghĩa trình nối thêm khác, ở đây chỉ có tên của trình nối thêm phải được điều chỉnh. Khi bạn thêm dòng đó vào, thì log4j loại bỏ nội dung của tệp tin nhật ký và khởi động lại nó cho mỗi lần chạy mới ứng dụng của bạn. Nếu không có dòng đó, thì bạn dễ bị đánh lừa và lúng túng tại sao các dòng tại phần đầu của tệp tin ghi nhật ký dường như không thay đổi.

Thường thì log4j tìm thấy tệp tin cấu hình của nó bằng cách tìm kiếm biến môi trường classpath của Java. Ngoài ra còn có một thuộc tính Java có thể được thiết lập để trỏ đến một tệp tin cấu hình cụ thể. Cả hai cơ chế này dễ dàng sử dụng nếu bạn đang ghi nhật ký dò vết API trong một ứng dụng Java độc lập. Nếu bạn đang chạy bên trong máy chủ ứng dụng J2EE, thì bạn có thể gặp khó khăn khi làm cho log4j tìm thấy tệp tin cấu hình của bạn (do có nhiều biến trong các cơ chế classpath của các máy chủ ứng dụng). Nếu điều đó xảy ra, bạn hãy tạo ra một tệp tin JAR không có gì khác ngoài thuộc tính log4j.properties ở mức cao nhất của tệp tin JAR (không phải ở trong thư mục con). Bạn có thể đặt tên cho nó, ví dụ, là wjc.jar, nhưng tên gọi không quan trọng, miễn là nó không trùng với một số tên JAR khác. Bạn đặt tệp tin wjc.jar cùng với các tệp tin JAR mà ứng dụng của bạn sử dụng, thường ở tại địa chỉ WEB-INF/lib/. Nếu máy chủ ứng dụng tìm thấy các tệp tin JAR khác, thì nó sẽ tìm thấy tệp wjc.jar. Bạn cũng có thể sử dụng cùng kỹ thuật này với trình khách giàu (thick client), nhưng khi để thuộc tính log4j.properties ở bên trong tệp wjc.jar thì việc hiệu chỉnh sẽ trở nên khó khăn hơn.

Ví dụ về kết quả ghi nhật ký

Tệp tin cấu hình mẫu quy định khuôn dạng cho kết quả ghi nhật ký, bao gồm dấu ấn thời gian ngày/giờ, mức độ ghi nhật ký (luôn luôn là DEBUG trong kịch bản ví dụ này) và tên các chuỗi. Để đơn giản, các cột này không hiển thị trong các liệt kê kết quả ghi nhật ký. Ở chỗ mà các dòng dài bị ngắt xuống dòng một cách nhân tạo vì lý do biên tập, bạn sẽ thấy một dấu gạch chéo ngược ở cuối dòng trên và một khoảng trắng ở đầu dòng dưới. Trường hợp dòng được bỏ qua cho ngắn gọn, bạn sẽ thấy khoảng hẫng là dấu ba chấm.

  • Tóm tắt:
    Liệt kê 4. Dò vết API mức tóm tắt
    Configuration for:Config.AutoRefreshEnabled value:null applied
      . . .
    getObjects        request batch-size=1 ObjectReferences \
        [classId=Domain&objectId=null]
      . . .
    getObjects        response elapsed=5272 batch-size=1
    executeChanges    request batch-size=1 ObjectReferences [classId=ObjectStore&\
        objectId=MissingObjectStore]
    executeChanges    elapsed=943 exception: \
        com.filenet.api.exception.EngineRuntimeException: \
        Requested item not found. Object store MissingObjectStore not found.
  • Trung bình:
    Liệt kê 5. Dò vết API mức trung bình
    Configuration for:Config.AutoRefreshEnabled value:null applied
      . . .
    <getObjectsRequest cacheAllowed="true" correlationId="1">
      <objectReference type="GlobalIdentity" classIdentity="Domain" rid="2"/>
    </getObjectsRequest>
      . . .
    <getObjectsResponse elapsed="4224" correlationId="1">
      <value type="Domain" updateSequenceNumber="5" accessAllowed="459267">
        <objectReference type="GlobalIdentity" classIdentity="Domain" \
        identity="{064735FF-3160-45D3-8258-ABA3AAE8F204}" rid="3"/>
        <properties type="Properties">
          <property name="Permissions">
            <value type="UnevaluatedPropertyValue" propertyName="Permissions">
              <parent ref="3"/>
            </value>
          </property>
          <property name="FixedContentDevices">
            <value type="UnevaluatedPropertyValue" propertyName="FixedContentDevices">
              <parent ref="3"/>
            </value>
          </property>
          <property name="Id">
            <value>{064735FF-3160-45D3-8258-ABA3AAE8F204}</value>
          </property>
      . . .
          <property name="ObjectStores">
            <value type="UnevaluatedPropertyValue" propertyName="ObjectStores">
              <parent ref="3"/>
            </value>
          </property>
        </properties>
      </value>
    </getObjectsResponse>
    <executeChangesRequest refresh="false" correlationId="0" updateSequenceNumber="null">
      <pendingAction type="Create" classId="Folder" \
        objectId="{3C19B4FE-B835-44F8-AB94-AB4F40997FB1}"/>
      <objectReference type="GlobalIdentity" classIdentity="ObjectStore" \
        identity="MissingObjectStore" rid="3"/>
      <modifiedProperties type="Properties">
        <property name="Parent">
          <value type="ObjectByPath" classIdentity="Folder" path="/" rid="6">
            <objectStore ref="3"/>
          </value>
        </property>
        <property name="FolderName">
          <value>wjc</value>
        </property>
      </modifiedProperties>
    </executeChangesRequest>
    <executeChangesResponse type="EngineRuntimeException" elapsed="845" rid="1">
    com.filenet.api.exception.EngineRuntimeException: E_OBJECT_NOT_FOUND: \
        Requested item not found. Object store MissingObjectStore not found. errorStack={
    	at com.filenet.engine.gcd.GCDHelper.getObjectStoreId(GCDHelper.java:347)
      . . .
    	at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1469)
    </executeChangesResponse>

Việc ghi nhật ký dò vết API ở mức tóm tắt rất có ích nếu bạn chỉ muốn xem RPC nào đang được xử lý và chúng cần bao nhiêu thời gian. Nếu bạn muốn xem chi tiết hơn có gì trong RPC, thì mức trung bình hoặc mức chi tiết là thích hợp. RPC luôn luôn được hiển thị như là một cặp yêu cầu và đáp ứng. XML trong các yêu cầu và đáp ứng khá gần với XML đã thấy trong ví dụ dò vết trên mạng, nhưng nó không theo WSDL một cách chính xác. Một số xấp xỉ và giản lược được sử dụng. Bởi vì việc ghi nhật ký dò vết API hiển thị XML trong một định dạng rất dễ đọc, nên bạn có thể dễ dàng hiểu được ý nghĩa của hầu hết các dữ liệu đang được truyền đi. Đối với việc điều chỉnh hiệu năng, trước tiên bạn quan tâm chủ yếu đến việc giảm số lượng RPC và sau đó đến việc truyền ít nhất có thể các thuộc tính mà vẫn có thể thực hiện các nhiệm vụ cần thiết trong ứng dụng của mình. Bạn sẽ thấy rằng rất đơn giản để tìm thấy cả hai điều đó trong liệt kê 4 và 5.

Ưu điểm và nhược điểm của việc ghi nhật ký dò vết API

Ưu điểm

  • Ghi nhật ký dò vết API có thể được sử dụng với cả truyền tải WSI và EJB.
  • Ghi nhật ký dò vết API không bị ảnh hưởng bởi việc sử dụng TLS/SSL hoặc VPN.
  • Kết quả ghi nhật ký dò vết API nhỏ gọn hơn so với các tệp tin dò vết trên mạng. Ngoài ra, XML được định dạng để cho con người dễ sử dụng hơn.

Nhược điểm

  • Ghi nhật ký dò vết API gây ra các hao tổn về hiệu năng. Đối với dò vết API mức tóm tắt, thì sự suy giảm về hiệu năng là tối thiểu. Đối với dò vết mức trung bình và mức chi tiết, thì sự hao tổn về hiệu năng có thể đáng kể. Các số đo thời gian RPC có phần hơi phóng đại quá mức đối với mức trung bình và mức chi tiết.
  • Có một rắc rối nhỏ về cấu hình đối với ghi nhật ký dò vết API. Đối với một số hình thái ứng dụng đặc biệt phức tạp, để có tệp tin cấu hình được nhận biết bên trong máy chủ ứng dụng J2EE là rất phiền toái.
  • Kết quả của ghi nhật ký dò vết API chỉ là gần đúng với những gì thực sự được gửi qua mạng. Giá trị gần đúng này là đủ tốt cho nhiều mục đích.
  • Có rất ít thông tin bao gói hoặc tiêu đề trong kết quả đầu ra của ghi nhật ký dò vết trên API. Đó có thể là một thách thức nếu khoan sâu vào các thông tin, điều đó có nghĩa là nó không nhất thiết là công cụ tốt nhất để xử lý mọi sự cố.
  • Bởi vì về bản chất nó là một công cụ chẩn đoán, và bởi vì kết quả đầu ra là thường tương quan với mã nguồn hoặc các tạo phẩm khác thường không có sẵn bên ngoài môi trường hỗ trợ và phát triển, một số kết quả đầu ra của ghi nhật ký dò vết API thực sự có thể làm tăng sự nhầm lẫn. Ví dụ, khi API cố gắng khám phá những thứ về môi trường của nó, thì nó ghi lại nhiều thứ không tìm thấy. Thực vậy, những thông điệp này là khá bình thường. Tuy nhiên, có thể rất khó khăn để phân biệt những thứ như vậy với các thông điệp quan trọng hơn của việc ghi nhật ký dò vết API.

Sử dụng ObjectDumper

Khi bạn làm việc trên một ứng dụng với một trình gỡ lỗi tương tác, thì bạn có thể xem xét vô số chi tiết về tình trạng của đối tượng và hệ quả của các cuộc gọi phương thức khác nhau. Mặc dù vậy, đôi khi bạn chỉ muốn có một tiện ích có thể in ra một loạt các thông tin về một hoặc nhiều đối tượng. Lớp ObjectDumper ví dụ mẫu được cung cấp kèm theo bài viết thực hiện được điều này (xem phần Tải về). Bạn có thể sử dụng nó không phải sửa đổi gì hoặc bạn có thể sửa đổi nó cho phù hợp. ObjectDumper được viết hoàn toàn theo ngôn ngữ của các API đã trưng ra (của Content Manager 4.5.0, nhưng nó cũng làm việc rất tốt với các phiên bản trước đó). Các mã không chứa "công cụ bí mật" nào hoặc các cuộc gọi đến các phương thức không có tài liệu kèm đầy đủ. Trong một số trường hợp, các mã dựa trên kết quả đầu ra của phương thức toString của các đối tượng khác nhau, vì vậy bạn phải ghi nhớ lời cảnh báo trước là các định dạng và nội dung của các kết quả đầu ra này thay đổi giữa các phiên bản phát hành. Một phần của lý do cho việc sử dụng phương thức toString để lấy một số thông tin là ObjectDumper rất cẩn thận tránh làm bất cứ điều gì có thể thay đổi trạng thái của các đối tượng đang được kết xuất. Nếu nó không tránh được điều đó, thì giá trị của nó như là một trợ giúp chẩn đoán sẽ giảm đáng kể. Bởi vì các API Java và .NET của Máy nội dung P8 bao gồm chủ yếu các giao diện đã trưng ra và các lớp trợ giúp không trưng ra, nên bạn sẽ thấy nhiều thứ liên quan đến các lớp thi hành không trưng ra này. Các lớp này cũng có thể thay đổi, và trong mã của riêng bạn chỉ nên tiếp tục sử dụng các kiểu và các phương thức đã trưng ra, như đã được thực hiện trong ObjectDumper.

Mã nguồn

ObjectDumper có một số khả năng định cấu hình, nhưng nó cũng được cấu trúc để bạn có thể kế thừa, tạo các lớp con và ghi đè lên các phương thức để thay đổi nhiều chi tiết về cách hoạt động của nó. Có một hàm tạo (constructor) mà bạn có thể dùng để báo cho nó biết bạn muốn sử dụng OutputStream cụ thể nào, nếu không, nó mặc định ghi vào System.out. Bạn cũng có thể chuyển vào một thẻ xâu ký tự để sử dụng như là tiền tố cho mỗi dòng đầu ra. Khả năng này hữu ích giống như kỹ thuật ghi nhãn nếu bạn đang thực hiện nhiều kết xuất đối tượng. Mặc định là một xâu rỗng.

Để kết xuất đối tượng, hãy gọi một trong các phương thức kết xuất chung. Các phương thức kết xuất này kết xuất các thông tin về đối tượng và về tất cả đối tượng con lôgic của chúng. Một số đếm độ sâu được duy trì khi duyệt qua cây đối tượng. Các dòng được thụt vào hai khoảng trống cho mỗi mức sâu trên cây. Khi bạn nhìn vào mã nguồn của phương thức dump(Object, int), bạn sẽ thấy rằng hầu hết chúng là một chuỗi xếp tầng của các phép kiểm tra instanceof theo sau là các cuộc gọi đến các phương thức làm việc cụ thể để xử lý việc kết xuất các kiểu đối tượng cụ thể. Khi các phương thức làm việc đó muốn kết xuất tiếp các đối tượng con, chúng gọi lại các phương thức kết xuất chung để có thêm một phát sinh. Nếu bạn muốn tinh chỉnh việc kết xuất một trong những kiểu đối tượng cụ thể, bạn có thể ghi đè lên phương thức làm việc trong lớp con. Nếu ObjectDumper không hiểu một kiểu đối tượng cụ thể nào đó, thì phương thức chung toString được sử dụng. Phương thức làm việc dumpEngineSet được hiển thị trong liệt kê 6 để làm ví dụ.

Liệt kê 6. Phương thức dumpEngineSet
/**
 * Dumping an EngineSet is slightly tricky because we don't know anything about
 * the state of any Iterator or PageIterator being used elsewhere.  In general, 
 * we can't even say if the collection is paged or if we have all of the items
 * already.  Consequently, we just dump information about the first page in the
 * collection.  The PageMark is opaque, so we just rely on the generic toString
 * output in case it reveals anything useful. 
 */
protected void dumpEngineSet(EngineSet engineSet, int depth) throws IOException
{
    PageIterator pi = engineSet.pageIterator();
    pi.nextPage();
    PageMark pm = pi.getPageMark();
    Object[] pageItems = pi.getCurrentPage();
    String summary = "n=" + pageItems.length + " " + pm;
    os.write(summary.getBytes());
    os.write(NEWLINE_AS_BYTES);
    dump(pageItems, depth);
}

Có lẽ trường hợp phức tạp nhất trong lớp là xử lý các đối tượng Property (thuộc tính) riêng lẻ. Có nhiều trường hợp đặc biệt mà API phải giải quyết một vài rắc rối cho bạn. Trước hết, các thuộc tính có thể biểu diễn hoặc các giá trị duy nhất hoặc các tập hợp các giá trị (tùy thuộc vào định nghĩa siêu dữ liệu đối với thuộc tính đó). Theo thuật ngữ của Máy nội dung thì một thuộc tính có nhiều giá trị đôi khi được viết tắt là MVP (Multi-valued property). Thuộc tính cũng có thể là kiểu vô hướng, có nghĩa là một kiểu đơn giản như số nguyên hoặc xâu ký tự, hoặc thuộc tính có thể có giá trị là đối tượng. Theo thuật ngữ của Máy nội dung, thuộc tính có giá trị đối tượng đôi khi được viết tắt là OVP (Object-valued Property).

Các đối tượng Property của API cũng có một thành phần PropertyState. Đây chỉ là phản xạ của trạng thái của đối tượng API và nó thực sự không có gì liên quan với thuộc tính vì nó tồn tại trong kho đối tượng. Mục đích chính việc ghi sổ PropertyState là để theo dõi khi nào một giá trị Property của API không tính toán được hoặc là một tham chiếu. Tham chiếu là giá trị thuộc tính chỉ chứa các thông tin xác định một đối tượng khác, và không có gì khác ngoài thông tin nhận dạng hiện đang được biết đến (bên trong API) về đối tượng khác đó. Giá trị thuộc tính không tính toán được là giá trị về những cái mà API hiện tại hoàn toàn không biết gì, ngoài việc là một thuộc tính như vậy đang tồn tại. Thuộc tính không tính toán được là thường các MVP. Thậm chí API không biết rằng thuộc tính có giá trị không null cho cá thể cụ thể đó hay không. Các khái niệm này hơi phức tạp một chút, nhưng cả hai đều phát sinh do nguyên tắc chỉ đạo của máy chủ nội dung là để làm việc hiệu quả nhất có thể nếu không được hướng dẫn khác đi. Bạn luôn có thể phân giải hoàn toàn các tham chiếu và giá trị thuộc tính không tính toán được khi bạn cần chúng. Một phần của điều chỉnh hiệu năng là làm cho điều đó xảy ra ở thời điểm tốt nhất. Bài viết tới sẽ thảo luận vấn đề này chi tiết hơn.

Giá trị của đối tượng Property có thể trỏ đến một đối tượng API khác, và đối tượng đó đến lượt nó có thể có các thuộc tính trỏ đến các đối tượng khác nữa. Và là bình thường đối với các đối tượng được trỏ đến ấy khi được chia sẻ chung bởi các cá thể đối tượng API. Việc kết xuất chúng mỗi khi chúng xảy ra không phải là hữu ích cho lắm. ObjectDumper lưu giữ bên trong nó dấu vết các đối tượng mà nó đã nhìn thấy. Khi nó nhìn thấy các đối tượng một lần nữa, thì nó chỉ xuất ra một tham chiếu khác cho lần xuất hiện trước đó và không kết xuất một cách truy hồi các đối tượng con. Đây không phải là vấn đề thuận tiện và gọn gàng. Các tham chiếu giữa các đối tượng API đôi khi hình thành nên chu trình, do đó việc theo dõi các đối tượng đã thấy trước đó cũng ngăn cản sự truy hồi vô hạn khi đã thấy có chu trình.

Kết quả đầu ra mẫu

Việc thực hiện các sửa đổi nhỏ cho chương trình HelloDocument tại phần đầu tiên của loạt bài viết này sinh ra kết quả đầu ra sau đây. Các sửa đổi về mã được hiển thị trong liệt kê 7. Hãy thêm các dòng gần phần cuối của phương thức createAndFileDocument để kết xuất cá thể UpdatingBatch trước và sau khi gửi các cập nhật đến các máy chủ của Máy nội dung. Vì bạn đã yêu cầu làm mới các đối tượng trong cá thể UpdatingBatch nhưng không chỉ định cá thể PropertyFilter hoặc cho tài liệu hoặc cho RCR, nên việc làm mới mang trả lại nhiều thuộc tính được định nghĩa cho các đối tượng đó. Mặc dù vậy, hầu hết các thuộc tính không tính toán được hoặc là các tham chiếu.

Liệt kê 7. Mã đã sửa đổi với các cuộc gọi kết xuất
private String createAndFileDocument(Domain dom, ObjectStore os)
{
    // . . .
    UpdatingBatch ub=UpdatingBatch.createUpdatingBatchInstance(dom,RefreshMode.REFRESH);
    ub.add(doc, null);
    ub.add(rcr, null);
    ObjectDumper od = new ObjectDumper();
    try
    {
        od.dump(ub);
    }
    catch (IOException e)
    {
        e.printStackTrace();
        System.exit(0);
    }
    System.out.println("Doing updates via UpdatingBatch");
    ub.updateBatch();
    try
    {
        od.clear();
        od.dump(ub);
    }
    catch (IOException e)
    {
        e.printStackTrace();
        System.exit(0);
    }
    // . . .
}

Liệt kê 8 cho thấy kết xuất của cá thể UpdatingBatch trước khi được làm mới. Như thường lệ, các dòng dài bị ngắt nhân tạo bằng dấu gạch chéo ngược, và dấu ba chấm thay cho các phần bị bỏ qua trong kết xuất. Bạn có thể thấy rằng các đối tượng chứa tương đối ít các thuộc tính, và tất cả chúng đều bẩn. Nói cách khác, chúng có các giá trị được thiết lập bởi các ứng dụng gọi. ObjectDumper đánh dấu tên của các thuộc tính bẩn bằng dấu hoa thị. Ngoài ra còn có các hoạt động chưa thực hiện, điều này thích hợp cho các đối tượng ở bên trong cá thể UpdatingBatch.

Liệt kê 8. Cá thể UpdatingBatch trước khi làm mới
[0118cb3a] UpdatingBatch 
  [00c67a88] ArrayList 
    [0096ad7c] BatchItemHandleImpl 
      [0057ae58] DocumentImpl classId=Document&\
  objectId={7091F9E9-4982-43BA-88B0-9DFAA80C356D}&\
  objectStore=MyObjectStore
        [00775121] PendingAction[2] 
          [016f70a4] Create  Class=com.filenet.api.action.Create \
  Values={classId=Document, objectId={7091F9E9-4982-43BA-88B0-9DFAA80C356D}}
          [014c5b37] Checkin  Class=com.filenet.api.action.Checkin \
  Values={checkinminorversion=false, autoclassify=false}
        [01f8acdc] PropertiesImpl n=2, dirty=true
          [011cc367] PropertyStringImpl *DocumentTitle=My Document Title
          [0160c4b0] PropertyEngineObjectListImpl *ContentElements=((collection))
            [00114025] SubListImpl 
              [013c550f] ContentTransferImpl  \
  Class=com.filenet.apiimpl.core.ContentTransferImpl \
  AccessAllowed=null RecursionLevel=0 UpdateSequenceNumber=null \
  ObjectAddress=( com.filenet.apiimpl.core.DependentIdentity@a347a9da \
  Parent=(null) Index=null PropertyName=null IsNew=true) Connection=(null) \
  SuperClasses=[null] PendingActions=null
                [01f488f1] PropertiesImpl n=3, dirty=true
                  [014a9387] PropertyStringImpl \
  *RetrievalName=c:/temp/qbf.txt1
                  [011b86c7] PropertyContentImpl \
  *Content=com.filenet.apiimpl.property.ClientInputStream@2da5a6
                  [00d647d8] PropertyStringImpl \
  *ContentType=application/octet-stream
    [002a987d] BatchItemHandleImpl 
      [00813bc1] DynamicReferentialContainmentRelationshipImpl \
  classId=DynamicReferentialContainmentRelationship&\
  objectId={F335B56F-FAEA-42D1-9946-E08565112767}&\
  objectStore=MyObjectStore
        [007a36a2] PendingAction[1] 
          [0198c6f3] Create  Class=com.filenet.api.action.Create \
  Values={defineSecurityParentage=false, autouniquecontainmentname=true, \
  classId=DynamicReferentialContainmentRelationship, \
  objectId={F335B56F-FAEA-42D1-9946-E08565112767}}
        [012d8ecd] PropertiesImpl n=3, dirty=true
          [01fa5e5e] PropertyStringImpl *ContainmentName=I Am a Contained Document
          [00497062] PropertyEngineObjectImpl *Tail=classId=Folder&\
  path=/HelloDocument&objectStore=MyObjectStore
          [01716fa0] PropertyEngineObjectImpl *Head=classId=Document&\
  objectId={7091F9E9-4982-43BA-88B0-9DFAA80C356D}&objectStore=MyObjectStore

Liệt kê 9 cho thấy kết xuất của cá thể UpdatingBatch sau khi làm mới. Các hoạt động chưa thực hiện đã biến mất. Nhiều thuộc tính được trả lại (66 cho tài liệu và 13 cho RCR) không là bẩn, bởi vì chúng đến từ máy chủ CE. Nhiều thuộc tính không tính toán được hoặc là các tham chiếu. Liệt kê 9 cho thấy một vài cái để minh hoạ, nhưng hầu hết các dòng kết xuất được rút ngắn cho ngắn gọn và rõ ràng.

Liệt kê 9. Cá thể UpdatingBatch sau khi làm mới
[0118cb3a] UpdatingBatch 
  [0181b3d4] ArrayList 
    [0096ad7c] BatchItemHandleImpl 
      [0057ae58] DocumentImpl classId=Document&\
  objectId={7091F9E9-4982-43BA-88B0-9DFAA80C356D}&\
  objectStore={5463CB9C-C05C-4ED6-8DB2-EA931DC026F8}
        [0045378f] PendingAction[0] 
        [014b9a74] PropertiesImpl n=66, dirty=false
          [00893969] PropertyEngineObjectImpl  ReleasedVersion=((UNEVALUATED))  \
  Class=com.filenet.apiimpl.property.PropertyEngineObjectImpl \
  PropertyName=ReleasedVersion Value=classId=Document&\
  objectId={7091F9E9-4982-43BA-88B0-9DFAA80C356D}&\
  objectStore={5463CB9C-C05C-4ED6-8DB2-EA931DC026F8}&propertyId=ReleasedVersion \
  IsDirty=false Access=1 State=(UNEVALUATED) Connection=( \
  Class=com.filenet.apiimpl.core.ConnectionImpl \
  URI=http://MyCEServer:9080/wsi/FNCEWS40MTOM/ Parameters={})
          [014b081b] PropertyEngineObjectImpl  VersionSeries=((REFERENCE))  \
  Class=com.filenet.apiimpl.property.PropertyEngineObjectImpl \
  PropertyName=VersionSeries Value=classId=VersionSeries&\
  objectId={642B77E7-EE55-4E96-8C9E-4C005D800361}&\
  objectStore={5463CB9C-C05C-4ED6-8DB2-EA931DC026F8} \
  IsDirty=false Access=1 State=(REFERENCE) Connection=( \
  Class=com.filenet.apiimpl.core.ConnectionImpl \
  URI=http://MyCEServer:9080/wsi/FNCEWS40MTOM/ Parameters={})
          [00e8606c] PropertyEngineObjectListImpl  Permissions=((UNEVALUATED))  . . .
          [00292cb2] PropertyDateTimeImpl  DateLastModified=Sun Jan 25 21:13:56 PST 2009
          [0135605a] PropertyStringImpl  EntryTemplateLaunchedWorkflowNumber=null
          [0148e798] PropertyBooleanImpl  IsCurrentVersion=true
          [015ccfb1] PropertyEngineObjectImpl  OwnerDocument=null
          [004788d5] PropertyEngineObjectImpl  DocumentLifecyclePolicy=null
          [00688954] PropertyStringImpl  LockOwner=null
          [0110278e] PropertyEngineObjectImpl  SecurityPolicy=null
          [0194e776] PropertyStringImpl  Name=My Document Title
          [00e80740] PropertyEngineObjectImpl  StorageArea=((REFERENCE))  . . .
          [00f2225f] PropertyEngineObjectSetImpl  Annotations=((UNEVALUATED))  . . .
          [006de609] PropertyEngineObjectSetImpl  \
  ParentRelationships=((UNEVALUATED))  . . .
          [01f217ec] PropertyStringImpl  DocumentTitle=My Document Title
          [0100aff5] PropertyBinaryImpl  PublicationInfo=null
          [00200db9] PropertyEngineObjectImpl  SecurityParent=((UNEVALUATED))  . . .
          [015dc37d] PropertyEngineObjectImpl  StoragePolicy=((REFERENCE))  . . .
          [00e7e8eb] PropertyEngineObjectImpl  PublishingSubsidiaryFolder=null
          [016cbd97] PropertyStringImpl  CurrentState=null
          [001321f5] PropertyInteger32Impl  ClassificationStatus=0
          [001a6518] PropertyEngineObjectImpl  ClassDescription=((REFERENCE))  . . .
          [013e4a5a] PropertyEngineObjectSetImpl  \
  WorkflowSubscriptions=((UNEVALUATED))  . . .
          [019050a0] PropertyStringImpl  StorageLocation=null
          [019d3b3a] PropertyFloat64Impl  ContentSize=110.0
          [019b808a] PropertyDateTimeImpl  DateContentLastAccessed=null
          [00140fee] PropertyEngineObjectSetImpl  ParentDocuments=((UNEVALUATED))  . . .
          [0082254d] PropertyDateTimeImpl  DateCreated=Sun Jan 25 21:13:56 PST 2009
          [005f1ae9] PropertyInteger32Impl  ReservationType=32
          [01dfc8a0] PropertyStringImpl  EntryTemplateObjectStoreName=null
          [00ec898a] PropertyEngineObjectSetImpl  AuditedEvents=((UNEVALUATED))  . . .
          [015e0c2b] PropertyInteger32Impl  VersionStatus=1
          [0171194d] PropertyBooleanImpl  IsInExceptionState=false
          [0034151f] PropertyEngineObjectListImpl  ActiveMarkings=((UNEVALUATED))  . . .
          [00114b17] PropertyStringImpl  MimeType=application/octet-stream
          [0159054d] PropertyStringImpl  LastModifier=user1234
          [016b321b] PropertyEngineObjectListImpl  ContentElements=((UNEVALUATED))  . . .
          [0098f192] PropertyEngineObjectImpl  Reservation=((UNEVALUATED))  . . .
          [017748d3] PropertyStringImpl  Creator=user1234
          [004e2f0a] PropertyEngineObjectSetImpl  FoldersFiledIn=((UNEVALUATED))  . . .
          [00c2cf83] PropertyBooleanImpl  IsFrozenVersion=false
          [01c5af2e] PropertyStringImpl  ComponentBindingLabel=null
          [01702c48] PropertyInteger32Impl  LockTimeout=null
          [016b6c55] PropertyInteger32Impl  MajorVersionNumber=1
          [01954f89] PropertyIdImpl  Id={7091F9E9-4982-43BA-88B0-9DFAA80C356D}
          [0198e8b4] PropertyEngineObjectSetImpl  \
  DependentDocuments=((UNEVALUATED))  . . .
          [004b0bbb] PropertyStringListImpl  ContentElementsPresent=((collection))
            [00ef4504] StringListImpl 
              [002f1e75] String application/octet-stream
          [01c5ddd3] PropertyEngineObjectImpl  This=((REFERENCE))  . . .
          [01f47ae8] PropertyInteger32Impl  MinorVersionNumber=0
          [01b11b79] PropertyEngineObjectImpl  SourceDocument=null
          [0082d603] PropertyDateTimeImpl  ContentRetentionDate=null
          [01b09282] PropertyBooleanImpl  IsReserved=false
          [0162ba99] PropertyIdImpl  LockToken=null
          [00c8c7d6] PropertyIdImpl  EntryTemplateId=null
          [01b7b407] PropertyInteger32Impl  CompoundDocumentState=0
          [018c5e67] PropertyBooleanImpl  IsVersioningEnabled=true
          [0089c116] PropertyBooleanImpl  IgnoreRedirect=null
          [01e3bbd7] PropertyEngineObjectSetImpl  Versions=((UNEVALUATED))  . . .
          [00a86d12] PropertyEngineObjectImpl  SecurityFolder=null
          [0190d8e1] PropertyStringImpl  Owner=user1234@example.net
          [008eae04] PropertyEngineObjectSetImpl  Containers=((UNEVALUATED))  . . .
          [0135ae7e] PropertyEngineObjectSetImpl  \
  DestinationDocuments=((UNEVALUATED))  . . .
          [009abce9] PropertyEngineObjectImpl  CurrentVersion=((UNEVALUATED))  . . .
          [0095215b] PropertyIdImpl  IndexationId=null
          [00c10de0] PropertyEngineObjectSetImpl  \
  ChildRelationships=((UNEVALUATED))  . . .
          [0056fc16] PropertyEngineObjectSetImpl  ChildDocuments=((UNEVALUATED))  . . .
    [002a987d] BatchItemHandleImpl 
      [00813bc1] DynamicReferentialContainmentRelationshipImpl \
  classId=DynamicReferentialContainmentRelationship&\
  objectId={F335B56F-FAEA-42D1-9946-E08565112767}&\
  objectStore={5463CB9C-C05C-4ED6-8DB2-EA931DC026F8}
        [018f9b75] PendingAction[0] 
        [01c85444] PropertiesImpl n=13, dirty=false
          [01144ba2] PropertyEngineObjectImpl  VersionSeries=((REFERENCE))  . . .
          [008de972] PropertyStringImpl  Creator=user1234
          [00d964af] PropertyDateTimeImpl  DateLastModified=Sun Jan 25 21:13:56 PST 2009
          [006127da] PropertyStringImpl  ContainmentName=I Am a Contained Document
          [007f8062] PropertyEngineObjectImpl  ClassDescription=((REFERENCE))  . . .
          [01f8d077] PropertyEngineObjectImpl  Tail=((REFERENCE))  . . .
          [00ee6ad6] PropertyIdImpl  Id={F335B56F-FAEA-42D1-9946-E08565112767}
          [00a826da] PropertyEngineObjectImpl  This=((REFERENCE))  . . .
          [015fc672] PropertyEngineObjectImpl  Head=((REFERENCE))  . . .
          [0178aae1] PropertyDateTimeImpl  DateCreated=Sun Jan 25 21:13:56 PST 2009
          [011abd68] PropertyStringImpl  Name=I Am a Contained Document
          [008932e8] PropertyEngineObjectSetImpl  AuditedEvents=((UNEVALUATED))  . . .
          [00c08593] PropertyStringImpl  LastModifier=user1234

Ưu điểm và nhược điểm của cá thể ObjectDumper

Ưu điểm

  • Bạn có thể sử dụng ObjectDumper để xem trạng thái của đối tượng tại các điểm tùy ý trong ứng dụng của mình. Bạn không bị giới hạn bởi các RPC công cụ đo lường.
  • Bạn có thể tùy chỉnh nó cho đến khi bạn thấy phù hợp, cả với các thông tin kết xuất ra lẫn định dạng của các thông tin đó. Bạn có thể làm điều đó bằng cách tạo lớp con hoặc bằng cách chỉnh sửa trực tiếp mã nguồn của ObjectDumper.
  • Bởi vì bạn có mã nguồn, nên bạn có toàn quyền kiểm soát nó sẽ làm những gì. Không phải tìm hiểu công cụ mới hoặc các cơ chế định cấu hình mới, ngoại trừ những thứ mà bạn tự mình phát minh ra.

Nhược điểm

  • Bạn thường phải sửa đổi mã nguồn của ứng dụng được đo lường để sử dụng ObjectDumper. Nó không hữu ích khi đo lường các thành phần mà mã nguồn không có sẵn.
  • Khi bạn thêm các cuộc gọi kết xuất vào một thành phần, thì bạn phải xây dựng lại các thành phần đó. Vì vậy, ngay cả khi bạn có mã nguồn (thường là như vậy với những thứ mà bạn sử dụng ObjectDumper), có thể có rắc rối nhỏ trong việc xây dựng lại và triển khai lại các ứng dụng của bạn.

Kết luận

Bài viết này đã giới thiệu cho các bạn ba kỹ thuật riêng biệt để giám sát các dữ liệu truyền qua các RPC và trạng thái của các đối tượng API Java của Máy nội dung P8 nói chung. Mặc dù từng chi tiết của các kỹ thuật này không được thảo luận, bài viết đã chỉ cho các bạn một cách thỏa đáng các nét riêng của từng kỹ thuật để bạn có thể tự tiếp tục khám phá theo cách của riêng bạn. Mỗi kỹ thuật có điểm mạnh và điểm yếu của nó, bạn cần phải có một ý tưởng nào đó về việc bạn muốn sử dụng kỹ thuật nào và khi nào. Các bài viết sau sẽ còn nói đến các kỹ thuật này, đặc biệt là khi thảo luận về điều chỉnh hiệu năng.


Tải về

Mô tảTênKích thước
Java source code for this articleObjectDumper.zip6KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

  • Tham gia vào các diễn đàn khác nhau liên quan đến ECM trên trang developerWorks, bao gồm một số diễn đàn con liên quan cụ thể đến sản phẩm FileNet của IBM. Đặc biệt, hãy xem các diễn đàn về sản phẩm IBM FileNet Content Manager và về sản phẩm FileNet Business Process Manager. Các diễn đàn này chủ yếu dựa vào cộng đồng và không phải là thay thế cho cơ chế hỗ trợ sản phẩm chính thức.
  • Tham gia vào các blog của trang developerWorks và tham gia vào cộng đồng developerWorks.

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=Information Management
ArticleID=777453
ArticleTitle=Viết các mã tuyệt vời với các API P8 FileNet của IBM, Phần 2: Thám thính công việc của chính bạn
publish-date=11302011