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