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); }