為遺留使用者儲存庫開發聯合儲存庫的定製介面卡

CloudSpace發表於2009-05-06

引言

WebSphere Application Server (WAS) V6.1 引入了聯合使用者儲存庫,它提供了將多個獨立的使用者儲存庫對映到一個虛擬儲存庫的功能,使得在 WebSphere Application Server 中可以更加容易地使用多個儲存庫。然而,有些遺留系統正使用它們自定義的資料庫結構,將使用者名稱、密碼、組和一些相關資訊儲存在關係型資料庫中,這些資訊無法被 WebSphere Application Server 直接使用。對於這種情況,要使遺留的使用者儲存庫和聯合儲存庫整合在一起,需要為遺留的使用者儲存庫開發一個聯合儲存庫的定製介面卡,使 WebSphere Application Server 能管理儲存在遺留關聯式資料庫中的使用者和組。本文將詳細介紹如何開發聯合儲存庫的定製介面卡,並以基於關係型資料庫的遺留使用者儲存庫為例分析實現細節,最後針對示例給出定製介面卡的部署和測試方案。

聯合儲存庫

聯合儲存庫為使用者和組管理提供了讀寫能力,可以通過應用程式程式設計介面(API)、管理控制檯、wsadmin 命令等方式對使用者和組進行管理。

聯合儲存庫支援多種型別的儲存庫,包括基於檔案的儲存庫、輕量級目錄訪問協議 (LDAP) 儲存庫、資料庫儲存庫和定製儲存庫。基於檔案的儲存庫和 LDAP 儲存庫受管理控制檯支援,對於資料庫和定製儲存庫,可以使用 wsadmin 命令列介面或配置應用程式程式設計介面 (API)。

聯合儲存庫通過介面卡操作各個儲存庫。它直接提供 File、LDAP 和 Database 介面卡,分別為聯合儲存庫訪問基於檔案的儲存庫、LDAP 儲存庫和資料庫儲存庫提供適配功能,這些覆蓋了大部分使用者的需求。聯合儲存庫還提供了擴充套件功能,使用者可以通過實現定製介面卡,使聯合儲存庫支援更多的儲存庫。所有的聯合儲存庫的介面卡需要實現 com.ibm.wsspi.wim.Repository 軟體程式設計介面 (SPI)。

示例場景介紹

本文的示例場景假設使用者的遺留系統正在使用關係型資料庫來維護使用者儲存庫。在使用者儲存庫中,使用者、密碼、組和一些相關資訊在執行時有自定義的物件型別,而在持久化時也有自定義的資料表結構。然而聯合儲存庫自帶的資料庫介面卡必須使用規定的資料庫模式來儲存資料,對於遺留資料庫的儲存模式無法直接存取,因此遺留的使用者儲存庫無法直接和聯合儲存庫整合在一起。

本文以此場景為例,介紹聯合儲存庫定製介面卡的開發,並針對基於關係型資料庫的使用者儲存庫給出一種定製介面卡的實現方式。

在本文介紹的場景中,遺留系統通過 User 和 Group 類來管理使用者和組。其定義如圖 1 所示。其中 User 類表示一個使用者, Group 類表示一個組, User 和 Group 之間存在多對多關係。


圖 1. 遺留系統中的使用者和組的物件模型
遺留系統中的使用者和組的物件模型

遺留系統通過關係型資料庫來持久化使用者和組的資訊,包括 3 個資料表:USER、GROUP 和 USER_IN_GROUP。其定義如圖 2 所示。其中 USER 表儲存一個使用者,GROUP 表儲存一個組,USER_IN_GROUP 表儲存使用者和組之間的關聯關係,表示使用者在組中,分別通過外來鍵關聯到 USER 和 GROUP 表中。


圖 2. 遺留系統中的使用者和組的資料庫模型
遺留系統中的使用者和組的資料庫模型

一般情況下,關係型資料庫持久化通過資料訪問物件 (DAO) 來實現。遺留系統定義了 UserDao 和 GroupDao 來負責物件的持久化,如圖 3 所示。UserDao 包括使用者的查詢、建立、更新和刪除等操作的介面定義,GroupDao 包括使用者組的查詢、建立、更新和刪除等操作,以及增加、刪除組成員等操作的介面定義。在遺留系統中,資料訪問物件層可以有各種實現方式,例如:JDBC、Hibernate 等等。


圖 3. 遺留系統中的使用者和組的資料訪問物件
遺留系統中的使用者和組的資料訪問物件

開發聯合儲存庫的定製介面卡,就是充分利用資料訪問物件提供的方法來實現介面卡中定義的方法,從而達到適配的目的。而至於資料訪問物件中定義的方法的具體實現方式對定製介面卡沒有影響。

擴充套件聯合儲存庫

所有的介面卡需要實現 com.ibm.wsspi.wim.Repository 介面。在 Repository 介面中,定義了以下 9 個抽象方法,如表 1 所示。


表 1. Repository 介面定義的抽象方法

方法 描述
create 在給定物件下建立實體
createSchema 動態建立新實體和屬性的型別結構
delete 刪除給定物件下的實體
get 獲取給定物件的資訊
getSchema 獲取實體和屬性的型別結構資訊
initialize 初始化介面卡
login 認證給定物件下的賬戶資訊
search 查詢給定物件下滿足搜尋條件的物件,並返回指定的屬性
update 更新給定物件下的實體

這 9 個方法使用 commonj.sdo.DataObject 型別作為引數,Data Object 是 Service Data Objects (SDO) 的基礎元件,用於表達結構化的資料,為結構化資料提供統一的表達形式。

Repository 介面有一個預設的實現 com.ibm.wsspi.wim.RepositoryImpl 類。類派生關係如圖 4 所示。其中,FileAdapter、LdapAdapter 和 DBAdapter 分別用於適配檔案儲存庫、LDAP 儲存庫和資料庫儲存庫。AbstractAdapterImpl 抽象類是一個輔助的資料庫實現,實現了 Repository SPI。LegacyDBAdapter 類是本文為遺留儲存庫開發的定製介面卡。


圖 4. Repository 介面及其子類的派生關係
Repository 介面及其子類的派生關係 

利用 AbstractAdapterImpl 抽象類

AbstractAdapterImpl 是 WebSphere Application Server 示例定製介面卡程式碼的一部分,可以從參考資料中下載。AbstractAdapterImpl 抽象類採用了模板方法 (Template Method) 設計模式,實現了不包含任何和特定資料庫相關的程式碼。而對於和特定資料庫相關的程式碼,AbstractAdapterImpl 抽象類以抽象方法的形式留給了子類來實現。

準備開發環境

開發定製介面卡可以使用任何 Java 整合開發環境,這裡使用 Eclipse,如下圖 5 所示。


圖 5. Eclipse 中的定製介面卡專案
Eclipse 中的定製介面卡專案

具體步驟如下:

  1. 新建 Java 專案:LegacyDBAdapter。
  2. 新增 WebSphere Application Server 執行時類庫:com.ibm.ws.runtime_6.1.0.jar。這個 jar 檔案可以在 {app_server_root}/plugins 目錄下找到。
  3. 新增 Service Data Objects 的實現類庫,這裡使用的是 Eclipse Modeling Framework (EMF) 實現。這個類庫可以在 {app_server_root}/plugins 目錄下找到,包括以下 5 個 jar 檔案 ( 對於不同版本的 WebSphere Application Server,檔名可能略有不同 ):
    • org.eclipse.emf.commonj.sdo_2.1.0.v200609210005.jar
    • org.eclipse.emf.common_2.2.1.v200609210005.jar
    • org.eclipse.emf.ecore.sdo_2.2.0.v200609210005.jar
    • org.eclipse.emf.ecore.xmi_2.2.1.v200609210005.jar
    • org.eclipse.emf.ecore_2.2.1.v200609210005.jar
  4. 新增訪問遺留系統所需要的類庫,這些類庫可能以各種形式存在。這裡直接匯入了遺留系統的訪問程式碼,包括資料模型和資料訪問物件:
    • User
    • UserDao
    • UserDaoImpl
    • Group
    • GroupDao
    • GroupDaoImpl
  5. 匯入 AbstractAdapterImpl 類,這個類可以簡化定製開發介面卡的開發。
  6. 新建 LegacyDBAdapter 類,該類從 AbstractAdapterImpl 派生,Eclipse 會自動新增抽象方法的預設實現程式碼。本文接下來幾節將詳細介紹如何實現這些抽象方法。

    實現定製介面卡的初始化功能

    在 WebSphere Application Server 啟動時會對定製介面卡進行初始化,我們可以在這個過程中從配置檔案中讀取對遺留系統訪問的相關資訊。實現初始化功能包括以下方法:

    public void initialize(DataObject reposConfig) throws WIMException

    此方法對定製介面卡進行初始化。AbstractAdapterImpl 抽象類中的 initialize 方法實現了基本的初始化操作,包括從配置檔案中讀取自定義屬性等。如果定製介面卡需要一些特定的初始化,可以重寫 initialize 方法。對於本文的例子,需要讀取資料庫訪問的一些屬性,並對資料訪問物件進行初始化。


    清單 1. 重寫 initialize 方法
    				
    public void initialize(DataObject reposConfig) throws WIMException
    {
     // 呼叫父類初始化方法
     super.initialize(reposConfig);
     // 獲取資料庫連線屬性
     ……
     // 初始化資料訪問物件
     ……
    }
    

    注:本文只列出重要的實現方法,具體的實現請下載本文提供的程式碼示例。

    public boolean isValidCustomProperty(String key, String value)

    此方法用於對自定義的屬性進行驗證。對於本文的例子,我們只驗證了屬性名是否為 driver、url、user 和 password,這些屬性用於連線遺留資料庫。

    實現使用者和組的轉換

    定製介面卡使用 DataObject 來儲存使用者和組的資訊,而遺留系統使用自定義的使用者和組的物件 User 和 Group,我們需要在兩種表達方式之間建立一種對映關係。我們定義了一些私有方法用於使用者和組的轉換,這些方法將會在後面介紹的方法中大量地被使用到。

    private DataObject convertToDataObject(User user)

    private DataObject convertToDataObject(Group group)

    private User convertToUser(DataObject entity)

    private Group convertToGroup(DataObject entity)

    前兩個方法用於將遺留系統中的使用者和組轉換成 DataObject,後兩個方法用於將 DataObject 轉換為遺留系統中的使用者和組。

    這裡以 convertToDataObject (User) 為例說明轉換過程。首先建立一個使用者 DataObject,將使用者的一些基本屬性 ( 如 principalName、uid 等 ) 存入其中,再在其下面建立一個識別符號 DataObject,將使用者的一些標識資訊 ( 如 uniqueId、uniqueName 等 ) 存入其中,最後將使用者 DataObject 返回。


    清單 2. convertToDataObject 方法實現程式碼

    				
    private DataObject convertToDataObject(User user) throws WIMException
    {
        if (user != null)
        {
            DataObject rootDO = SDOHelper.createRootDataObject();
            // 建立使用者 Data Object
            DataObject userDO = rootDO.createDataObject(
    DO_ENTITIES, WIM_NS_URI, DO_PERSON_ACCOUNT);
            userDO.setString(PROP_PRINCIPAL_NAME, user.getUid());
            userDO.setBytes(PROP_PASSWORD, user.getPassword().getBytes());
            userDO.setString("uid", user.getUid());
            ……
            // 建立使用者的識別符號 Data Object
            DataObject identifierDO = userDO.createDataObject(DO_IDENTIFIER);
            identifierDO.set(PROP_EXTERNAL_ID, user.getUniqueId());
           ……
            // 返回使用者 Data Object
            return userDO;
        }
        return null;
    }
    

    以使用者 testuser 為例,相應的 DataObject 如清單 3 所示 ( 列印成 XML 的形式便於檢視 )。


    清單 3. 使用者 testuser 的 DataObject 示例

    				
    
        
            
                
                testuser
                KioqKg==
                testuser
                aa
                bb
                testuser@ibm.com
            
        
    
    

    實現使用者和組的讀取功能

    使用者和組的讀取功能從遺留資料庫中讀取使用者和組的資訊,並轉換成 DataObject。實現使用者和組的讀取功能包括以下方法:

    public DataObject getEntity(DataObject entity, DataObject propertyCtrl, DataObject returnRoot)

    此方法用於獲取實體的資訊。首先從傳入的 entity 中獲取實體的唯一名字,然後通過唯一名字查詢到實體的詳細資訊,最後將得到的資料存入 returnRoot 中返回。此方法在通過唯一名字查詢到實體的詳細資訊時,用到了一個私有方法 getEntityByUniqueName,其定義如下:

    private DataObject getEntityByUniqueName(String uniqueName)

    此方法根據傳入的 uniqueName 的字首進行判斷,如果字首是 uid,說明實體是使用者,呼叫 UserDao 中的 getUserByUniqueName 方法從資料庫中讀取使用者的資訊;如果字首是 cn,說明實體是組,呼叫 GroupDao 中的 getGroupByUniqueName 方法從資料庫中讀取組的資訊。由於使用資料訪問物件獲取的使用者和組物件是由遺留系統提供的,所以需要通過方法 convertToDataObject 將物件中對應的欄位進行對映,轉換成 DataObject。


    清單 4. getEntityByUniqueName 方法實現程式碼

    				
    private DataObject getEntityByUniqueName(String uniqueName) throws WIMException
    {
        DataObject entity = null;
        if (uniqueName != null)
        {
            if (uniqueName.startsWith("uid"))
            {
                // entity 物件是一個使用者
                User user = userDao.getUserByUniqueName(uniqueName);
                entity = convertToDataObject(user);
            }
            else if (uniqueName.startsWith("cn"))
            {
                // entity 物件是一個組
                Group group = groupDao.getGroupByUniqueName(uniqueName);
                entity = convertToDataObject(group);
            }
        }
        if (entity == null)
        {
            throw new EntityNotFoundException(......);
        }
        return entity;
    }
    

    在以後介紹的方法中,經常需要通過實體的 uniqueName 字首來區分實體是使用者或組;而對於通過資料訪問物件獲取的使用者和組物件,也需要用 convertToDataObject 做轉換。

    public String getUniqueName(DataObject entity) throws EntityNotFoundException

    此方法用於獲取實體的唯一名字。如果傳入的 entity 是識別符號型別,直接讀取其 PROP_UNIQUE_NAME 屬性;如果 entity 是實體型別,則通過 DO_IDENTIFIER 屬性可以解析出識別符號,再遞迴呼叫 getUniqueName 就可以得到實體的唯一名字。


    清單 5. getUniqueName 方法實現程式碼

    				
    public String getUniqueName(DataObject entity) throws EntityNotFoundException
    {
        String uniqueName = null;
        if (entity != null)
        {
            // 檢查 entity 是否為識別符號物件
            if (DO_IDENTIFIER_TYPE.equals(entity.getType().getName()))
            {
            // 如果是,則直接通過 PROP_UNIQUE_NAME 屬性讀取 uniqueName
                uniqueName = entity.getString(PROP_UNIQUE_NAME);
                ......
            }
            else
            {
                // 如果不是,則通過 DO_IDENTIFIER 屬性讀取其識別符號,再遞迴呼叫 getUniqueName 方法
                DataObject identifier = entity.getDataObject(DO_IDENTIFIER);
                uniqueName = getUniqueName(identifier);
            }
        }
        return uniqueName;
    }
    

    對於上節中的使用者 DataObject 的例子,呼叫 getUniqueName 可獲得其 uniqueName 為 uid=testuser,o=legacyDBRepository,反過來,通過 uniqueName 呼叫 getEntityByUniqueName,可以得到完整的 DataObject 資訊。

    實現搜尋功能

    搜尋功能返回所有符合搜尋條件的實體。實現搜尋功能包括以下方法:

    public DataObject searchEntities(DataObject searchControl)

    此方法用於在儲存庫中搜尋實體。首先從傳入的 searchControl 中解析出搜尋的條件:需要搜尋的實體型別,和實體屬性需要符合的條件。通過對搜尋的條件表示式進行解析,將搜尋條件以名值對的形式存入一個 Properties 物件中。然後呼叫 search 私有方法進行搜尋, search 方法定義如下:

    private List search(List entityTypes, Properties properties)

    此私有方法根據不同的實體型別 entityTypes,搜尋符合 properties 條件的物件列表。


    清單 6. searchEntities 方法實現程式碼

    				
    public DataObject searchEntities(DataObject searchControl) throws WIMException
    {
        try
        {
            // 解析搜尋表示式
            String searchExpr = 
    searchControl.getString(SchemaConstants.PROP_SEARCH_EXPRESSION);
            WIMXPathInterpreter parser =
     new WIMXPathInterpreter(new StringReader(searchExpr));
            XPathNode node = parser.parse(null);
            // 獲取匹配的實體型別
            List entityTypes = parser.getEntityTypes();
            // 將搜尋條件以名值對的形式存入一個 Properties 物件中
            Properties properties = new Properties();
            if (node != null)
            ......
            // 呼叫 search 方法
            List searchResult = search(entityTypes, properties);
            // 建立包含搜尋結果的 Data Object,並返回
            ......
        }
        catch (Exception e) { ...... }
    }
    


    清單 7. search方法實現程式碼
    				
    private List search(List entityTypes, Properties properties) throws Exception
    {
        List searchResult = new Vector();
        // 遍歷每一個實體型別
        for (Iterator typeIterator = entityTypes.iterator(); typeIterator.hasNext();)
        {
            String entityType = (String) typeIterator.next();
            if (SchemaHelper.isLoginAccountType(entityType))
            {
                // 搜尋使用者
                List userList = userDao.findUsersByProperties(properties);
                // 將每一個搜尋結果轉換為Data Object
                ......
            }
            else if (SchemaHelper.isGroupType(entityType))
            {
                // 搜尋組
                List groupList = groupDao.findGroupsByProperties(properties);
                // 將每一個搜尋結果轉換為 Data Object
                ......
            }
        }
        return searchResult;
    }
    

    以下給出一個搜尋的示例。假設要搜尋 E-mail 為 testuser@ibm.com 的使用者。在整合解決方案控制檯中提交搜尋使用者請求,如圖 6 所示。


    圖 6. 提交搜尋使用者請求
    提交搜尋使用者請求

    WebSphere Application Server 會呼叫 searchEntities 方法,傳入的 searchControl 中儲存的搜尋條件表示式為:

    //entities[@xsi:type='PersonAccount' and mail='testuser@ibm.com']

    然後從中解析出一個搜尋條件:名字為 mail,值為 testuser@ibm.com,儲存在 properties 物件中,傳入 search 方法。再呼叫 UserDao.findUsersByProperties,將搜尋條件轉換為 SQL 語句:

    SELECT * FROM USER WHERE 1=1 AND MAIL LIKE ‘testuser@ibm.com’

    最後將查詢的結果轉換為 DataObject 返回,如之前的清單 3 所示。

    在整合解決方案控制檯中顯示的結果如圖 7 所示。


    圖 7. 搜尋使用者結果
    搜尋使用者結果 

    實現登入功能

    實現登入功能包括以下方法:
    public DataObject login(DataObject account, DataObject loginCtrl)

    此方法用於對使用者進行認證。首先從傳入的 account 物件中解析出用於認證的 principalName 和 password,將 principalName 作為搜尋條件,呼叫 search 方法找到相應的賬戶資訊,並且此賬戶資訊必須是唯一的。然後將傳入的密碼和賬戶資訊中的密碼進行比對,比對成功後,將唯一匹配的帳戶 DataObject 返回,登入成功。


    清單 8. login 方法實現程式碼

    				
    public DataObject login(DataObject account, DataObject loginCtrl) throws WIMException
    {
        ......
        String accountEntityType = account.getType().getName();
        // 搜尋匹配的使用者資訊
        List entityTypes = new Vector();
        ……
        // 呼叫 search 方法
        List accountList = search(entityTypes, properties);
        if (accountList.size() == 1)
        {
            // 找到唯一的匹配使用者
            accountDO = (DataObject) accountList.get(0);
        }
        else if (accountList.size() > 1)
        {
            throw new PasswordCheckFailedException(......);
        }
        // 進行密碼比對
        ......
    }
    

    實現使用者和組的管理功能

    使用者和組的管理功能包括建立、更新和刪除使用者和組等操作。實現使用者和組的管理功能包括以下方法:

    public DataObject createEntity(String entityType, DataObject entity)

    此方法用於建立實體。首先需要對傳入的 entity 做一些處理,包括:去除一些和其它實體的關聯資訊,為新實體生成唯一的 ID,對實體中的密碼資訊進行 Hash 處理等。然後通過資料訪問物件 UserDao.createUser ( 使用者 ) 或 GroupDao.createGroup ( 組 ) 向資料庫中新增實體資訊,並將新生成的實體返回。

    public boolean entityAlreadyExists(DataObject entity)

    public boolean entityAlreadyExists(String uniqueId, String uniqueName)

    public boolean entityMustExist(String uniqueId, String uniqueName)

    public boolean memberMustExist(String uniqueId, String uniqueName)

    public boolean groupMustExist(String uniqueId, String uniqueName)

    這幾個方法都用於判斷實體是否已經存在。根據 uniqueName 呼叫資料訪問物件 UserDao.getUserByUniqueName ( 使用者 ) 或 GroupDao.getGroupByUniqueName ( 組 ) 從資料庫中查詢實體,如果找到,說明實體已經存在。

    public void rename(String entityType, DataObject updatedEntity, String uniqueName, String newUniqueName)

    此方法用於更改實體的 uniqueName。首先驗證新的 uniqueName 在儲存庫中不存在,然後更新 updatedEntity 中的 uniqueName 和 externalName,最後通過資料訪問物件 UserDao.updateUserUniqueName ( 使用者 ) 或 GroupDao.updateGroupUniqueName ( 組 ) 更新實體的 uniqueName。

    public void updateEntity(DataObject updatedEntity, String uniqueName, List modItems)

    此方法用於更新實體的屬性。需要更新的屬性儲存在一個 ModificationItem 物件的列表中。首先通過 uniqueName 獲取需要更改的 entity。然後對於列表中的每一個 ModificationItem 物件,如果更改操作是替換或刪除,則從 entity 中去除要更改的屬性;如果更改操作是替換或新增,則向 entity 中新增新的屬性和值。最後根據 entity 的 uniqueName 將 entity 轉換成使用者或組物件,通過資料訪問物件 UserDao.updateUser( 使用者 ) 或 GroupDao.updateGroup( 組 ) 更新到資料庫中。


    清單 9. updateEntity 方法實現程式碼

    				
    public void updateEntity(DataObject updatedEntity, String uniqueName, List modItems)
    {
        ......
        // 遍歷每一個 ModificationItem
        for (int i = 0; i < modItems.size(); i++)
        {
            ModificationItem modItem = (ModificationItem) modItems.get(i);
            int modOp = modItem.getModificationOp();
            Attribute attr = modItem.getAttribute();
            String propName = attr.getID();
            // 如果是替換或刪除操作,則先刪除原先設定的值
            if ((modOp == DirContext.REPLACE_ATTRIBUTE) 
                || (modOp == DirContext.REMOVE_ATTRIBUTE))
            {
                entity.unset(propName);
            }
            // 如果是替換或新增操作,則設定新的值
            ......
            }
        }
        // 將實體更新資訊儲存到資料庫
        ......
    }
    

    public DataObject deleteEntities(DataObject root)

    此方法用於刪除實體。從每一個待刪除的實體中解析出 uniqueName,通過資料訪問物件 UserDao.deleteUser ( 使用者 ) 或 GroupDao.deleteGroup ( 組 ) 從資料庫中刪除。

    以下給出一個 rename 和 updateEntity 的示例。假設我們要將使用者 testuser 的使用者標識更改為 testuser2,以及姓氏更改為 xxxx。在整合解決方案控制檯中提交更改使用者請求,如圖 8 所示。


    圖 8. 提交更改使用者請求
    提交更改使用者請求

    WebSphere Application Server 首先會呼叫 rename 方法,將使用者 testuser 的 uniqueName 改為 testuser2,如清單 10 所示。


    清單 10. 呼叫 rename 後原 testuser 的 DataObject

    				
    
        
            
                
                ......
            
        
    


    清單 11. 呼叫 updateEntities 後原 testuser 的 DataObject
    				
    
        
            
                xxxx
            
       
    
    
    

    在整合解決方案控制檯中顯示的結果如圖 9 所示。


    圖 9. 更改使用者結果
    更改使用者結果

    實現組和成員的管理功能

    組和成員的管理功能包括獲取組中的成員列表,獲取成員所在組的列表,以及更新組中的成員。實現組和成員的管理功能包括以下方法:

    public void getGroupMembers(DataObject rootEntity, DataObject grpMemberCtrl)

    此方法用於獲取組中的成員列表。首先從傳入的 rootEntity 中解析出組的 uniqueName。然後呼叫資料訪問物件 UserDao.findUsersByGroupUniqueName 從資料庫中讀取其成員的資訊,再將每一個成員資訊轉換成 DataObject,存入組的 DataObject 的成員列表 (DO_MEMBERS) 部分中。


    清單 12. getGroupMembers 方法實現程式碼

    				
    public void getGroupMembers(DataObject rootEntity, DataObject grpMemberCtrl)
    {
        // 得到組的 uniqueName
        ……
        // 通過組的 uniqueName 獲取組成員列表
        List userList = userDao.findUsersByGroupUniqueName(groupUniqueName);
        // 將每一個成員轉換成 Data Object,並加入組的 DO_MEMBERS 列表中
        for (Iterator iterator = userList.iterator(); iterator.hasNext();)
        {
            User user = (User) iterator.next();
            DataObject userEntity = convertToDataObject(user);
            groupEntity.getList(DO_MEMBERS).add(userEntity);
        }
    }
    

    以組 testgroup 和其成員 testuser 為例,相應的 DataObject 如清單 13 所示。


    清單 13. 組 testgroup 和其成員 testuser 的 DataObject 示例

    				
    
        
            
                
                testgroup
                
                   
                    ......
                
                This is a test group.
            
        
    
    

    public void getGroupMembership(DataObject rootEntity, DataObject grpMembershipCtrl)

    此方法用於獲取使用者所在組的列表。首先從傳入的 rootEntity 中解析出使用者的 uniqueName,然後呼叫資料訪問物件 GroupDao.findGroupsByUserUniqueName 從資料庫中讀取包含該使用者的組的資訊,再將每一個組員資訊轉換成 DataObject,存入使用者的 DataObject 的組列表 (DO_GROUPS) 部分中。

    public void updateGroupMembers(DataObject entity, String groupUniqueName, List memberUniqueNames, int grpMbrMod)

    此方法用於更新組中的成員。如果是新增或替換,通過呼叫 addMemberToGroup 方法,從而呼叫了資料訪問物件 GroupDao.addMemberToGroup 將 memberUniqueNames 中的每個使用者加入組中;如果是刪除,則將呼叫私有方法 removeMemberFromGroup ,從而呼叫資料訪問物件 GroupDao. removeMemberFromGroup 從組中移除使用者。

    定製介面卡的部署

    部署定製介面卡可以使用 wsadmin 命令,具體步驟如下:

    1. 將 LegacyDBAdapter.jar 和遺留資料庫訪問相關的 jar 檔案複製到 {app_server_root}/lib 目錄下。

    2. 在 wimconfig.xml 檔案 ( 位於 {app_server_root}/profiles/{profile_name}/config/cells/cell_name/wim/config) 中,新增新的 config:repositories 元素,將此元素放在 config:realmConfiguration 元素之前。

    3. 啟動 wsadmin 工具:wsadmin –conntype none

    4. 禁用頁面排程功能。

    $AdminTask updateIdMgrRepository {-id LegacyDBRepository -supportPaging false}

    5. 設定 driver、url、user 和 password 定製屬性,用於和遺留儲存庫連線。

    $AdminTask setIdMgrCustomProperty {-id LegacyDBRepository -name driver -value "com.ibm.db2.jcc.DB2Driver"}

    $AdminTask setIdMgrCustomProperty {-id LegacyDBRepository -name url -value "jdbc:db2://localhost:50000/LegacyDB"}

    $AdminTask setIdMgrCustomProperty {-id LegacyDBRepository -name user -value "administrator"}

    $AdminTask setIdMgrCustomProperty {-id LegacyDBRepository -name password -value "passw0rd"}

    6. 配置 o=legacyDBRepository 基本條目。

    $AdminTask addIdMgrRepositoryBaseEntry {-id LegacyDBRepository -name o=legacyDBRepository}

    7. 將 o=legacyDBRepository 基本條目和 defaultWIMFileBasedRealm 域關聯。

    $AdminTask addIdMgrRealmBaseEntry {-name defaultWIMFileBasedRealm -baseEntry o=legacyDBRepository}

    8. 儲存更改,在 wimconfig.xml 中新新增的內容如下:

    
    
      
      
      
      
    

    9. 重新啟動 WebSphere Application Server。

    定製介面卡的測試

    要驗證定製介面卡是否正確配置和工作,可以通過在 WebSphere Application Server 的整合解決方案控制檯中進行一些簡單操作。以下列舉一些測試示例。

    1. 將預設父代基本條目設為 o=legacyDBRepository
    在整合解決方案控制檯中,進入“安全性 > 安全管理、應用程式和基礎結構”,確認“可用的域定義”選擇的是“聯合儲存庫”,單擊“配置”按鈕。在聯合儲存庫配置頁面中,單擊“受支援的實體型別”,將“預設父代基本條目”設定為 o=legacyDBRepository,如圖 10 所示。儲存更改,並重新啟動 WebSphere Application Server。


    圖 10. 設定預設父代基本條目
    設定預設父代基本條目

    2. 新增使用者和組
    在整合解決方案控制檯中,進入“使用者和組 > 管理使用者”。單擊“建立”按鈕,建立使用者 testuser,如圖 11 所示。通過資料庫工具可以檢視到已經在 USER 表中新增了 testuser 記錄,如圖 12 所示。


    圖 11. 新增使用者
    圖 11. 新增使用者

    圖 12. 新新增的使用者記錄
    圖 12. 新新增的使用者記錄

    用類似的方法可以建立組 testgroup,如圖 13 所示。通過資料庫工具可以檢視到已經在 GROUP 表中新增了 testgroup 記錄,如圖 14 所示。


    圖 13. 新增組
    圖 13. 新增組

    圖 14. 新新增的組記錄
    圖 14. 新新增的組記錄

    在新建的 testgroup 組中,“成員”標籤頁下,單擊“新增使用者”,將 testuser 加入該組中,如圖 15 所示。通過資料庫工具可以檢視到已經在 USER_IN_GROUP 表中新增了相應的關聯記錄,如圖 16 所示。


    圖 15. 新增組成員
    圖 15. 新增組成員

    圖 16. 新新增的使用者和組的關聯記錄
    新新增的使用者和組的關聯記錄

    3. 使用者登入
    在整合解決方案控制檯中,進入“使用者和組 > 管理使用者角色”,給 testuser 賦予“管理員”角色,如圖 17 所示。


    圖 17. 給使用者新增管理員角色
    圖 17. 給使用者新增管理員角色

    用 testuser 重新登入整合解決方案控制檯,此時 testuser 應該可以正常地登入整合解決方案控制檯。

    技巧和提示

    1. 可以開啟 WebSphere Application Server 的詳細日誌來幫助除錯定製介面卡。具體方法如下:在整合解決方案控制檯中,進入 Troubleshooting -> Logs and Trace,選擇相應的 profile 名字,單擊 Diagnostic Trace,再單擊 Change Log Detail Levels,在文字框中新增 com.ibm.ws.wim.*=all。儲存更改後,重新啟動 WebSphere Application Server。
    2. 在使用者登入時,WebSphere Application Server 會搜尋聯合儲存庫配置的各個儲存庫以查詢該使用者出現的所有位置,任何一個儲存庫的訪問失敗,都會導致使用者登入失敗。如果找到了該使用者的多個例項,也會導致使用者登入失敗。
    3. 如果由於定製介面卡的配置或實現上的問題,導致 WebSphere Application Server 管理使用者無法登入整合解決方案控制檯,可以暫時先將 WebSphere Application Server 的全域性安全禁用。具體方法如下:
      啟動 wasadmin 工具:
      wsadmin –conntype none
      輸入以下命令將 WebSphere Application Server 的全域性安全禁用:
      securityoff
      重新啟動 WebSphere Application Server。
    4. 聯合儲存庫的定製介面卡不能依賴於任何 WebSphere Application Server 元件。如果定製介面卡的實現需要使用資料來源來連線資料庫,那麼在 WebSphere Application Server 啟動期間,需要使用 JDBC 進行連線。在稍後資料來源可用的情況下,改為使用資料來源來連線至資料庫。
    5. 在 WIMTraceHelper 輔助類中,提供了 printDataGraph 靜態方法,可以將 DataObject 轉換為 XML 形式的字串,在除錯定製介面卡時非常實用。其宣告如下:
      public static String printDataGraph(commonj.sdo.DataObject root)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14789789/viewspace-594335/,如需轉載,請註明出處,否則將追究法律責任。

相關文章