Provider 对象实现了 StpProvider 接口,因此该对象将其他 CM API 接口连接到了 CM API 库提供的实现。
CM API 库中有两个 Provider 实现类;一个实现了 CcProvider 接口,另一个实现了 CqProvider 接口。CqProvider 实现类根据 CqProvider.CQ_ONLY_PROVIDER_CLASS 字面值命名。将其传递给 ProviderFactory.createProvider 方法以获取 Rational CM API CqProvider 类的实例。
还需要给 ProviderFactory.createProvider 一个 Callback 对象,通过此对象 Provider 的实例可获取 Authentication 对象。Authentication 对象给这个 Provider 提供凭证,通过这些凭证,在 Rational ClearQuest 用户对数据库执行操作(例如,更改记录状态或修改字段值)之前,对用户进行认证。利用提供的 Callback 对象,使应用程序能够完全控制用户凭证(用户名和密码)的获取。关于凭证的获取,这可以在应用程序中显式地设置或者在用户开始操作之前请求获取,或者在第一次需要时获取。
由于利用 Rational CM API 进行编程的所有用例都需要 Provider 对象,本教程的所有示例都采用了在实用程序类中定义的 getProvider 方法,以供所有应用程序使用。在用户第一次尝试访问数据库时,实用程序类中定义的 Callback 对象将请求用户名和密码,然后在其适用时继续复用这些凭证。
/**
* A simple Authentication object in which the username and password
* obtained from the user is cached for use by the Team API.
*/
static class UnPw implements Authentication {
/**
* Constructs an Authentication object
*
* @param unpw A String[] containing the username and password.
*/
UnPw(String[] unpw) { m_data = unpw; }
public String loginName() { return m_data[0]; }
public String password() { return m_data.length > 1 ? m_data[1] : ""; };
/** The cached credentials */
private String[] m_data;
}
/**
* Constructs an instance of a CM API provider for ClearQuest.
*
* @return The instantiated CqProvider object
* @throws Exception
* If the provider could not be instantiated
*/
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
{
// Try to reuse last credentials on each new repository
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));
}
};
// Instantiate a 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 来报告所有错误,所以在实用程序类中包含一个方法,该方法可以将这类异常中的信息格式化为文本消息并将其显示在 Swing 对话框中。
/**
* Extracts the message content from a Throwable and returns it as a
* hierarchical array of Strings capturing the nesting of the Throwable's
* message components. This structure formats reasonably in a SWING
* showMessageDialog invocation.
*
* @param ex The Throwable object whose message content is to be extracted.
* @return If the given Throwable has nested components, an array consisting
* of the Throwable's message and an array of the nested messages.
*/
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;
}
/**
* Displays a Swing dialog containing the messages associated with a given
* Throwable.
*
* @param frame The parent frame for the message dialog.
* @param title The title to appear in the dialog window.
* @param ex The throwable whose messages are to be displayed.
*/
static void exception(Component frame, String title, Throwable ex) {
JOptionPane.showMessageDialog(frame,
messages(ex),
title,
JOptionPane.ERROR_MESSAGE);
}