Tích hợp FileNet với IBM Content Manager, Phần 2: Triển khai thực hiện các thực thể nghiệp vụ Java và lớp cổng (gateway) cho dữ liệu trong kho lưu trữ lưu trữ của IBM Content Manager

Một ví dụ sử dụng kịch bản "Công ty ABC"

Đây là bài thứ hai trong loạt bốn bài viết cho bạn thấy cách tích hợp FileNet® Business Process Management (FileNet BPM- Quản lý quy trình nghiệp vụ FileNet) với IBM® DB2 ® Content Manager (IBM ECM-Nhà quản lý nội dung DB2 của IBM) phiên bản 8. Trong bài này, hãy tìm hiểu về các trình diễn công việc trong FileNet P8, triển khai thực hiện các thực thể nghiệp vụ Java™ và tạo ra một lớp cổng (gateway) Java. Ngoài ra, hãy xem cách sử dụng tuần tự hóa Java để tránh các hạn chế cụ thể.

S. H. Liu, Kỹ sư tư vấn phần mềm, IBM Japan

S. H. LiuS.H. Liu là một kỹ sư tư vấn phần mềm tại Phòng thí nghiệm phát triển phần mềm tại Trung Quốc của IBM (CSDL).



Dao-Quynh Dang, Kỹ sư phần mềm, IBM

Dang Quynh là một kỹ sư phần mềm cao cấp về IBM Enterprise Content Management.



Peng Shi, Kỹ sư phần mềm, Phát triển IBM Content Manager, IBM Japan

Peng ShiPeng Shi là một kỹ sư phần mềm tại Phòng thí nghiệm phát triển phần mềm tại Trung Quốc của IBM (CSDL). Anh làm về phát triển IBM Content Manager. Anh là một nhà phát triển có chứng chỉ WebSphere Studio V5.0. Anh cũng là kỹ sư phần mềm về phát triển FileNet



11 07 2011

Mở đầu

Phần 1 của loạt bài này đã giới thiệu cho bạn về các kiến trúc của FileNet P8 BPM và IBM Content Manager. Bài viết đã sử dụng một hệ thống Yêu cầu bồi thường bảo hiểm tự động của công ty ABC hư cấu để cho bạn thấy cách tích hợp hai sản phẩm mạnh mẽ này.

Phần 2 giới thiệu các trình diễn công việc của FileNet P8 và cho bạn thấy cách triển khai thực hiện các thực thể nghiệp vụ Java. Bạn sẽ tìm hiểu cách tạo ra một lớp cổng Java và xem cách sử dụng tuần tự hóa Java để tránh các hạn chế cụ thể.


Các trình diễn công việc

Để sử dụng một kho lưu trữ nội dung của bên thứ ba, FileNet Business Process Management (BPM) cần có khả năng giao tiếp với kho lưu trữ IBM Content Manager. Các trình diễn công việc FileNet có thể cho phép BPM tương tác với các ứng dụng hoặc các mã của bên thứ ba.

Trong FileNet P8, các trình diễn công việc cho phép một ứng dụng thực hiện một hoạt động hoặc một tập các hoạt động liên quan đến một dòng công việc. Thông thường, các trình diễn công việc được thiết kế không có giao diện người dùng và được sử dụng để thực hiện các hoạt động dòng công việc tự động, chẳng hạn như các hoạt động được liên kết với một bước dòng công việc cụ thể trong một định nghĩa dòng công việc. Các hoạt động trình diễn công việc được liên kết với các bước dòng công việc bao gồm đăng nhập và thiết lập một phiên làm việc của máy quy trình, thăm dò một hàng đợi người dùng hoặc hàng đợi hệ thống (cố gắng để tìm các hoạt động liên quan đến một bước dòng công việc cụ thể), khóa các đối tượng được lấy ra, xử lý công việc (cập nhật dữ liệu và v.v..) và quay ngược để thăm dò hàng đợi. Đại khái là, các trình diễn công việc cần thiết để tương tác với các ứng dụng của bên thứ ba.

Có hai kiểu trình diễn công việc, dựa vào trình tích hợp thành phần và dựa trên trình tiện ích nền sau (daemon). Trình diễn công việc dựa vào nhà tích hợp thành phần được dùng trong ví dụ này.

Các trình diễn công việc dựa vào nhà tích hợp thành phần là một thành phần được triển khai thực hiện như một lớp Java hoặc sự kiện Java Messaging Service (JMS-Dịch vụ gửi thông báo Java). Một thành phần Java có thể được sử dụng để thực hiện tất cả các hoạt động liên quan đến một dòng công việc. Hình 1 minh họa kiến trúc của nhà tích hợp thành phần:

Hình 1. Kiến trúc Nhà tích hợp thành phần FileNet P8
Kiến trúc Nhà tích hợp thành phần FileNet P8

Thành phần Java hoặc JMS có thể được nhà quản lý thành phần (component manager) quản lý. Nhà quản lý thành phần cấu hình và giao tiếp với các bộ điều hợp dịch vụ. Trong thời gian chạy, nhà quản lý thành phần thăm dò các hàng đợi thành phần về các mục công việc đòi hỏi JMS hoặc xử lý bởi các thành phần Java và kết nối một mục công việc đang đòi hỏi một thành phần tới bộ điều hợp dịch vụ thích hợp. Bộ điều hợp Java xử lý các cuộc gọi quy trình cho các đối tượng Java, được biểu diễn đến máy quy trình như là các hoạt động trên các hàng đợi (các mục công việc) ở nơi mỗi hoạt động được thực hiện bằng một phương thức của lớp Java. Bộ điều hợp Java thực hiện ghép nối với thành phần Java, sau đó tự động chờ đáp ứng từ thành phần này, cập nhật mục công việc và gửi mục công việc này đến bước dòng công việc tiếp theo. Bộ điều hợp JMS đặt các thông báo vào Hàng đợi JMS (Queue JMS) và gửi đi mục công việc liên quan. Bộ điều hợp JMS xử lý quy trình gửi lên các sự kiện tới một hàng đợi thông báo (dưới dạng một sự kiện XML dựa trên phần tử bước cho hoạt động cụ thể).


Triển khai thực hiện các thực thể nghiệp vụ Java cho Công ty ABC

Công ty ABC muốn lưu trữ tất cả dữ liệu nội dung trong IBM Content Manager hiện có. Và nó cũng muốn sử dụng FileNet BPM để xây dựng một ứng dụng tập trung quy trình. Vì vậy bạn cần triển khai thực hiện các trình diễn công việc trong BPM để thao tác nội dung trong IBM Content Manager hiện có.

Bạn có thể gói logic ứng dụng bên thứ ba và trưng ra giao diện đó khi triển khai thực hiện thành phần Java. Với trường hợp của Công ty ABC, trước tiên bạn triển khai thực hiện các thực thể nghiệp vụ Java có thể truy cập dữ liệu trong IBM Content Manager. Hình 2 cho thấy kiến trúc của các thực thể nghiệp vụ Java :

Hình 2. Kiến trúc các thực thể nghiệp vụ Java
Kiến trúc các thực thể nghiệp vụ Java

Như kiến trúc này cho thấy, bạn có thể tạo các đối tượng chuyển giao dữ liệu (DTO) và đối tượng truy cập dữ liệu (DAO) phù hợp với mỗi lớp trong biểu đồ lớp (Phần 1; Hình 4). Khi lấy lớp "AutoClaim" làm ví dụ, chúng ta đã tạo ra một lớp DTO AutoClaim.java và một lớp DAO AutoClaimDAO.java.

Mã Java sau đây là AutoClaim.java. Nó bao gồm các định nghĩa đặc tính và các phương thức getter và setter có liên quan. Nó biểu diễn giá trị dữ liệu cho một mục trong kho lưu trữ IBM Content Manager.

Liệt kê 1. DTO mẫu - AutoClaim.java
public class AutoClaim
{
	public AutoClaim () 
	{
	}
				
	private String claimID;
	public String getClaimID()
	{
		return this.claimID;
	}

	public void setClaimID(String claimID)
	{
		this.claimID = claimID;
	}
	
	private Integer claimStatus;
	public Integer getClaimStatus()
	{
		return this.claimStatus;
	}

	public void setClaimStatus(Integer claimStatus)
	{
		this.claimStatus = claimStatus;
	}
	
	private String openingDate;
	public String getOpeningDate()
	{
		return this.openingDate;
	}

	public void setOpeningDate(String openingDate)
	{
		this.openingDate = openingDate;
	}
	
	private Integer claimAmount;
	public Integer getClaimAmount() {
		return claimAmount;
	}

	public void setClaimAmount(Integer claimAmount) {
		this.claimAmount = claimAmount;
	}
	
	private String eventID;

	public String getEventID() {
		return this.eventID;
	}

	public void setEventID(String eventID) {
		this.eventID = eventID;
	}
	
	private String pidString;
	public String getPidString() {
		return pidString;
	}
	public void setPidString(String pidString) {
		this.pidString = pidString;
	}
}

Mã Java sau đây là AutoClaimDAO.java là một đối tượng truy cập dữ liệu. Nó chứa tất cả các phương thức gọi OOAPI của IBM Content Manager để xử lý dữ liệu trong IBM Content Manager.

Liệt kê 2. DAO mẫu - AutoClaimDAO.java
public class AutoClaimDAO
{

	 private String itemType;

	 public AutoClaimDAO()
	 {
	 	 itemType = "AutoClaim";
	 }
    
	public String create(AutoClaim autoClaim) throws DKException, Exception 
	{
		String pidString = null;
		DKDatastoreICM dsICM = SConnectDisconnectICM.connect();
		DKDDO ddo = dsICM.createDDO("AutoClaim", DKConstant.DK_CM_ITEM);
		setAttrToDDO(ddo, autoClaim);
		ddo.add();
		pidString = ddo.getPidObject().pidString();
		dsICM.disconnect();
		return pidString;
	}
	
	public AutoClaim retrieveAutoClaimByClaimID(String claimID)
	throws DKUsageError, DKException, Exception
	{
		AutoClaim autoClaim = null;
		String queryString = "/AutoClaim[@Claim=\""+claimID+"\"]";
		DKDatastoreICM dsICM = SConnectDisconnectICM.connect();
		dkIterator dkIter = 
		(dkIterator) SConnectDisconnectICM.search(dsICM, queryString, 0, true);
		if(dkIter.more())
		{
			autoClaim = new AutoClaim();
			DKDDO ddo = (DKDDO)dkIter.next();
			ddo.retrieve(DKConstant.DK_CM_CONTENT_ITEMTREE + 
				DKConstant.DK_CM_CONTENT_NO);
			setValueToAutoClaim(ddo, autoClaim);
			autoClaim.setPidString( ddo.getPidObject().pidString() );
		}
		dsICM.disconnect();
		return autoClaim;
	}

	public void update(String pidString, AutoClaim autoClaim)
	throws DKUsageError, DKException, Exception
	{
		DKDatastoreICM dsICM = SConnectDisconnectICM.connect();
		DKDDO ddo= dsICM.createDDO(pidString);
		ddo.retrieve(DKConstant.DK_CM_CONTENT_ITEMTREE + 
			DKConstant.DK_CM_CONTENT_NO);

		DKDatastoreExtICM ext = new DKDatastoreExtICM(dsICM);
		if(!ext.isCheckedOut(ddo))
		{
			ext.checkOut(ddo);
		}
		setAttrToDDO(ddo, autoClaim);
		dsICM.updateObject(ddo, DKConstant.DK_CM_CHECKIN);
		dsICM.disconnect();
	}
	
	public void delete(String pidString) throws DKUsageError, DKException, Exception
	{
		DKDatastoreICM dsICM = SConnectDisconnectICM.connect();
		DKDDO ddo = dsICM.createDDO(pidString);
		ddo.del();
		dsICM.disconnect();
	}
	
	public void setAttrToDDO(DKDDO ddo, AutoClaim autoClaim) throws DKUsageError
	{
		if(autoClaim.getClaimID()!=null)
		ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"ClaimID"), 
			autoClaim.getClaimID());
		if(autoClaim.getClaimStatus()!=null)
		ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"ClaimStatus"),
			autoClaim.getClaimStatus());
		if(autoClaim.getOpeningDate()!=null)
		ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"OpeningDate"),
			autoClaim.getOpeningDate());
		if(autoClaim.getClaimAmount()!=null)
		ddo.setData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,"ClaimAmount"),
			autoClaim.getClaimAmount());
		if( autoClaim.getEventID() != null )
		ddo.setData( ddo.dataId( DKConstant.DK_CM_NAMESPACE_ATTR, "EventID" ),
		autoClaim.getEventID() );
	}
	
	public void setValueToAutoClaim(DKDDO ddo, AutoClaim autoClaim)
	throws DKUsageError
	{
		Object attr = null;
		attr = ddo.getData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,
			"ClaimID"));
		if(attr!=null) 
		{
			autoClaim.setClaimID((String)attr);
			attr = null;
		}
		attr = ddo.getData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,
			"ClaimStatus"));
		if(attr!=null)
		{
			autoClaim.setClaimStatus((Integer)attr);
			attr = null;
		}
		attr = ddo.getData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,
			"OpeningDate"));
		if(attr!=null)
		{
			autoClaim.setOpeningDate((String)attr);
			attr = null;
		}
		attr = ddo.getData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,
			"ClaimAmount"));
		if(attr!=null)
		{
			autoClaim.setClaimAmount((Integer)attr);
			attr = null;
		}
		attr = ddo.getData(ddo.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,
			"EventID"));
		if(attr!=null)
		{
			autoClaim.setEventID( (String) attr );
			attr = null;
		}
	}
	
	...
}

Đối với các lớp khác trong biểu đồ lớp, có các lớp Java tương tự như AutoPolicy.javaAutoPolicyDAO.java. Bạn có thể tải về dự án Eclipse có chứa tất cả mã nguồn của lớp Java này.


Triển khai thực hiện lớp cổng cho các thực thể nghiệp vụ Java

Bây giờ bạn đã có một tập hợp các thực thể nghiệp vụ Java và cũng có rất nhiều phương thức hoạt động trong các thực thể này. Nhưng trong phần FileNet Component Integrator (Nhà tích hợp thành phần FileNet), một hàng đợi thành phần chỉ có thể trưng ra một phương thức của lớp Java. Và bạn nên tuân theo các quy tắc sau đây:

  • Lớp phải có một hàm tạo không có bất kỳ tham số nào.
  • Các phương thức phải được công khai.
  • Các phương thức có thể chỉ chứa các kiểu tham số hoạt động thành phần cho phép. Hãy tham chiếu các kiểu tham số hoạt động thành phần.

Nếu bạn muốn tương tác với nhiều lớp Java từ một ứng dụng của bên thứ ba và muốn đăng ký chỉ có một hàng đợi thành phần cho ứng dụng này, thì bạn nên bọc tất cả các phương thức của các lớp Java trong một lớp cổng (gateway). Vì thế với hệ thống Yêu cầu bồi thường bảo hiểm tự động của Công ty ABC, bạn nên bọc tất cả các phương thức hoạt động DAO trong một lớp cổng -- ICMOperation.java -- thể hiện trong mã Java sau.

Liệt kê 3. Lớp của trình bao bọc mẫu - ICMOperation.java
public class ICMOperation
{
    public void createAutoClaim(String claimID,Integer claimStatus,
    String openingDate,Integer claimAmount, String eventID) throws Exception
		{
			AutoClaim autoClaim = new AutoClaim();
		 	autoClaim.setClaimID(claimID);
			autoClaim.setClaimStatus(claimStatus);
			autoClaim.setOpeningDate(openingDate);
			autoClaim.setClaimAmount(claimAmount);
			autoClaim.setEventID( eventID );
			AutoClaimDAO dao = new AutoClaimDAO();
	    dao.create( autoClaim );
		}
    
    public Integer retrieveClaimStatusByClaimID(String claimID) throws Exception
    {
	    AutoClaim autoClaim = getAutoClaim( claimID );
	    return autoClaim.getClaimStatus();
    }
    
    public void updateClaimStatusByClaimID( String claimID, Integer claimStatus ) 
    throws Exception      
    {
      AutoClaimDAO dao = new AutoClaimDAO();
      AutoClaim autoClaim = dao.retrieveAutoClaimByClaimID( claimID );
      autoClaim.setClaimStatus( claimStatus );
      dao.update( autoClaim );
    }
    
    private AutoClaim getAutoClaim( String claimID ) throws Exception
    {
      return new AutoClaimDAO().retrieveAutoClaimByClaimID( claimID );
    }
    
    ...
	
}

Một cách giải quyết sử dụng các tham số đối tượng Java khác trong Component Integrator

FileNet P8 Component Integrator (Nhà tích hợp thành phần FileNet P8) chỉ hỗ trợ một số kiểu tham số nguyên thủy. Nhưng nói chung, một số đối tượng Java luôn được sử dụng như các tham số trong một phương thức hoạt động. Ở đây, chúng ta đã giới thiệu một giải pháp để khắc phục hạn chế này: sự tuần tự hóa Java. Hình 3 cho thấy giải pháp này.

Hình 3. Giải pháp tuần tự hóa Java
Giải pháp tuần tự hóa Java

Khi sử dụng tuần tự hóa Java, bạn có thể tuần tự hóa đối tượng Java ban đầu thành một chuỗi trong bộ nhớ. Khi bạn cần nhận đối tượng Java ban đầu, bạn có thể không tuần tự hóa chuỗi đó thành đối tượng Java ban đầu. Hãy lấy AutoClaimDAO.java làm ví dụ. Nếu bạn muốn sử dụng DTO AutoClaim.java làm tham số trong các phương thức hoạt động, bạn cần phải viết hai phương thức tiện dụng. Một phương thức là để tuần tự hóa đối tượng Java AutoClaim thành một chuỗi, trong khi phương thức kia là không tuần tự hóa chuỗi đó thành đối tượng Java AutoClaim ban đầu. Các đoạn mã Java dưới đây cho thấy hai phương thức này.

Liệt kê 4. Mã mẫu về tuần tự hóa và không tuần tự hóa
    public AutoClaim parseStringToAutoClaim( String autoClaimString ) 
    throws IOException, ClassNotFoundException
    {
        ObjectInputStream objInput = null;
        try
        {
            objInput = new ObjectInputStream( new ByteArrayInputStream( 
            new BASE64Decoder().decodeBuffer( autoClaimString ) ) );
            return (AutoClaim) objInput.readObject();
        }
        finally
                    if( objInput != null )
            {
                objInput.close();
            }
        }
    }

    public String parseAutoClaimToString( AutoClaim autoClaim ) throws IOException
    {
        ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
        ObjectOutputStream objOutput = null;
        try
        {
            objOutput = new ObjectOutputStream( byteOutput );
            objOutput.writeObject( autoClaim );
            return new BASE64Encoder().encode( byteOutput.toByteArray() );
        }
        finally
        {
            byteOutput.close();
            if( objOutput != null )
            {
                objOutput.close();
            }
        }
    }

Sau đó bạn nên làm cho AutoClaim có thể tuần tự hóa được. Điều cần thiết duy nhất là để cho AutoClaim triển khai thực hiện giao diện java.io.Serializable. Mã sau đây cho thấy AutoClaim có thể tuần tự hóa.

Liệt kê 5. Mã mẫu triển khai thực hiện tuần tự hóa
public class AutoClaim implements Serializable{
	private static final long serialVersionUID = 4599855634813333505L;
	...
}

Bây giờ bạn có thể đăng ký phương thức sử dụng chuỗi để biểu diễn đối tượng Java. Và trong ứng dụng khách, bạn nên sử dụng phương thức không tuần tự hóa để nhận đối tượng Java ban đầu. Hãy xem đoạn mã sau. Chúng ta sử dụng một phương thức trong lớp ICMOperation đã đăng ký để lấy ra đối tượng Java AutoClaim. Sau khi có kết quả, đầu tiên sử dụng phương thức tuần tự hóa để tuần tự hóa đối tượng Java AutoClaim thành một đối tượng chuỗi, sau đó trả về chuỗi cho một máy khách. Một khi máy khách nhận được chuỗi đó, sử dụng phương thức không tuần tự hóa để không tuần tự hóa chuỗi đó thành một đối tượng AutoClaim.

Liệt kê 6. Mã mẫu xem xét xung quanh sự hạn chế tham số
    public String retrieveAutoClaimByClaimID( String claimID ) throws Exception
    {
        AutoClaim autoClaim = getAutoClaim( claimID );
        return new AutoClaimDAO().parseAutoClaimToString( autoClaim );
    }

Bây giờ chúng ta có các thực thể nghiệp vụ Java và một lớp cổng Java trưng ra tất cả các phương thức hoạt động. Bạn có thể tải về tất cả các mã nguồn trong một dự án Java của Eclipse. Lưu ý rằng mức tuân thủ của trình biên dịch nên được đặt là 1.4 khi tạo các tệp jar tương ứng do sự tương thích của nhà tích hợp thành phần.


Kết luận

Trong Phần 2 của loạt bài này, bạn đã xem xét các trình diễn công việc của FileNet và đã thấy trình diễn công việc dựa trên nhà tích hợp thành phần chi tiết. Bạn đã tạo ra các thực thể nghiệp vụ Java và đã chuẩn bị một lớp cổng Java. Chúng ta cũng đã giới thiệu một giải pháp để vượt qua sự hạn chế tham số của nhà tích hợp thành phần của FileNet.

Phần 3 mô tả cách triển khai thực hiện các trình diễn công việc cho hệ thống yêu cầu bồi thường bảo hiểm tự động của Công ty ABC.


Tải về

Mô tảTênKích thước
Java Business Entities project for eclipseBO.zip10MB

Tài nguyên

Học tập

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

  • Tải về trình diễn FileNet Content Manager để xem cách IBM FileNet P8 tích hợp nội dung, quy trình và tuân thủ theo luồng, quản lý và tối ưu hóa quy trình xử lý yêu cầu bồi thường tự động.
  • Tải về các phiên bản đánh giá sản phẩm của IBM và nhận các công cụ phát triển ứng dụng thực hành và các sản phẩm trung gian từ DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

Thảo luậ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=Information Management
ArticleID=715540
ArticleTitle=Tích hợp FileNet với IBM Content Manager, Phần 2: Triển khai thực hiện các thực thể nghiệp vụ Java và lớp cổng (gateway) cho dữ liệu trong kho lưu trữ lưu trữ của IBM Content Manager
publish-date=07112011