< 上一個課程 | 下一個課程 >

取得提供者

此指導教學課程說明使用 Rational® CM API 程式設計模型處理 Rational ClearQuest® 資源的首要步驟。
在產品儲存庫(例如 Rational ClearQuest 使用者資料庫)中擷取、修改、建立或刪除資源之前,您必須先為該產品儲存庫類型建立一個 Provider 物件的實例。

Provider 物件實作 StpProvider 介面,並藉此連接其他 CM API 介面至 CM API 程式庫提供的實作。

CM API 程式庫內有兩個 Provider 實作類別;一個實作 CcProvider 介面,一個實作 CqProvider 介面。CqProvider 實作類別是以文字 CqProvider.CQ_ONLY_PROVIDER_CLASS 命名。將此名稱傳遞至 ProviderFactory.createProvider 方法,可取得 Rational CM API CqProvider 類別的實例。

也需要提供 Callback 物件給 ProviderFactory.createProvider,實例化的 Provider 可以從該物件取得 Authentication 物件。在資料庫中執行作業(例如,變更記錄的狀態或修改欄位值)之前,Authentication 物件提供所需的認證給提供者,來鑑別使用者是否為 Rational ClearQuest 使用者。使用您提供的 Callback 物件,可讓應用程式對於取得使用者認證(使用者名稱及密碼)具有完整控制權,使用者認證可明確設定至應用程式中,或在啟動時或第一次需要它時向使用者索取。

由於 Rational CM API 程式設計的所有使用案例都需要 Provider 物件,所以,此指導教學中的所有範例將使用 Utilities 類別中定義供所有應用程式使用的 getProvider 方法。Callback 物件也定義在 Utilities 類別中,當使用者第一次嘗試存取資料庫,然後繼續重複使用那些可接受的認證時,它會要求使用者名稱及密碼。

/**
 * 會快取從使用者取得使用者名稱及密碼的簡易 Authentication 物件,
 * 供小組 API 使用。
 */
static class UnPw implements Authentication {
    /**
     * 建構 Authentication 物件
     * 
     * @param unpw 包含使用者名稱及密碼的 String[]。
     */
    UnPw(String[] unpw) {  m_data = unpw; }

    public String loginName() { return m_data[0]; }
    public String password() { return m_data.length > 1 ? m_data[1] : ""; };

    /** 快取的認證 */
    private String[] m_data;
}


/**
 * 為 ClearQuest 建構 CM API Provider 的實例。
 * 
 * @return 實例化 CqProvider 物件
 * @throws Exception
 *             如果提供者無法實例化
 */
static StpProvider getProvider() throws Exception {
    try {
        Callback callback = new StpCallback() {
            private UnPw m_unpw;

            public Authentication getAuthentication(String r, int c)
            {   return null; /* Will not be called */   }

            public Authentication getAuthenticationEx(Domain domain,
                                                      String realm,
                                                      int retryCount,
                                                      StpProvider provider,
                                                      WvcmException failure) 
            throws WvcmException
            {
                // 嘗試重複使用每一個新儲存庫的前次認證
                if (m_unpw != null && retryCount == 0)
                    return m_unpw;

                String title = "Enter " + domain 
                        + " Username '+' Password for "
                        + realm + " [" + retryCount + "]";
                
                if (failure != null)
                    title = "Login failed: " + failure + "\n" + title;
                
                String unpw = JOptionPane.showInputDialog(title, "admin+");

                if (unpw == null || unpw.length() == 0)
                    throw new IllegalAccessError("User canceled request");
                
                if (unpw.equals("anonymous"))
                    return null;
                
                if (unpw.startsWith("@")) {
                    File file = new File(unpw.substring(1));

                    try {
                        FileReader reader = new FileReader(file);
                        char[] buf = new char[100];
                        int count = reader.read(buf);

                        unpw = new String(buf, 0, count);
                        reader.close();
                    } catch (Throwable t) {
                        Utilities.exception(null,
                                            "Reading password file " + unpw,
                                            t);
                    }
                }

                return m_unpw = new UnPw(unpw.split("\\+", -2));
            }
        };

        // 實例化 Provider
        return (StpProvider) ProviderFactory
            .createProvider(StpProvider.PROVIDER_CLASS, callback);
    } catch (InvocationTargetException ite) {
        WvcmException e = (WvcmException) ite.getTargetException();

        System.out.println("*** " + e);
        
        for (Throwable nested: e.getNestedExceptions())
            System.out.println("***  " + nested);

        throw e;
    }
}

在這個範例中,我們使用延伸的 StpProvider.StpCallback 介面實例,因為在要求鑑別時,會提供更多資訊給它。

由於 Rational CM API 會擲出 StpException 來報告所有錯誤,所以,我們在 Utilities 類別中包含一個方法,它將這種異常的資訊格式化成為文字訊息,並顯示在 Swing 對話框中。

/**
 * 從 Throwable 摘錄訊息內容,並傳回成為
 * 階層式 Strings 陣列,擷取 Throwable
 * 訊息元件的巢狀。此結構在 SWING
 * showMessageDialog 呼叫中合理格式化。
 * 
 * @param ex 要摘錄其訊息內容的 Throwable 物件。
 * @return 如果給定的 Throwable 有巢狀元件、包含
 *         Throwable 訊息的陣列及巢狀訊息的陣列。
 */
private static Object messages(Throwable ex) {
    String msg = ex.getLocalizedMessage();

    if (msg == null || msg.length() == 0)
        msg = ex.toString();

    if (ex instanceof StpException) {
        Throwable[] nested = ((StpException) ex).getNestedExceptions();

        if (nested != null && nested.length > 0) {
            Object[] msgs = new Object[nested.length];

            for (int i = 0; i < msgs.length; ++i)
                msgs[i] = messages(nested[i]);

            return new Object[] { msg, msgs };
        }
    } else if (ex.getCause() != null) {
        return new Object[] {msg, new Object[]{messages(ex.getCause())}};
    }

    return msg;
}

/**
 * 顯示 Swing 對話框,它包含與給定的
 * Throwable 相關聯的訊息。
 * 
 * @param frame 訊息對話框的母框。
 * @param title 出現在對話視窗中的標題。
 * @param ex 要顯示其訊息的 throwable 物件。
 */
static void exception(Component frame, String title, Throwable ex) {
    JOptionPane.showMessageDialog(frame,
                                    messages(ex),
                                    title,
                                    JOptionPane.ERROR_MESSAGE);
}
現在您有取得提供者及處理異常的程式碼,可開始使用 Rational ClearQuest 使用者資料庫可用的資源。

課程檢查點

使用 Rational CM API 撰寫程式的首要步驟,是建立 Callback 物件及取得 Provider 物件,您可以從該物件使用產品特定資源。
在這一課,您學到下列各項:
  • 關於 Callback 物件
  • 關於 Provider 物件
  • 關於 CM API 的異常狀況處理
  • 如何使用 CM API 實例化 Callback 及 Provider 物件
  • 如何使用 CM API 進行異常狀況處理
< 上一個課程 | 下一個課程 >

意見反應