< 上一课 | 下一课 >

获取 Provider

本课程阐述了利用 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 类的实例。

还需要为 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);
}
现在,获取 Provider 和处理异常的代码已经编写完成,可以开始使用 Rational ClearQuest 用户数据库中的可用资源了。

课程检查点

利用 Rational CM API 编写程序的第一步是创建 Callback 对象和获取 Provider 对象,通过 Provider 可以使用特定于产品的资源。
在本课程中,您学习到了以下内容:
  • Callback 对象
  • Provider 对象
  • CM API 中的异常处理
  • 如何利用 CM API 创建 Callback 和 Provider 对象的实例
  • 如何利用 CM API 来进行异常处理
< 上一课 | 下一课 >

反馈