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