001/*
002 * file Utilities.java
003 *
004 * Licensed Materials - Property of IBM
005 * Restricted Materials of IBM - you are allowed to copy, modify and 
006 * redistribute this file as part of any program that interfaces with 
007 * IBM Rational CM API.
008 *
009 * com.ibm.rational.stp.client.samples.Utilities
010 *
011 * (C) Copyright IBM Corporation 2004, 2008.  All Rights Reserved.
012 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
013 * disclosure restricted by GSA ADP  Schedule Contract with IBM Corp.
014 */
015
016package com.ibm.rational.stp.client.samples;
017
018import java.awt.Component;
019import java.io.File;
020import java.io.FileReader;
021import java.lang.reflect.InvocationTargetException;
022import java.util.List;
023
024import javax.swing.JOptionPane;
025import javax.wvcm.Feedback;
026import javax.wvcm.ProviderFactory;
027import javax.wvcm.Resource;
028import javax.wvcm.ResourceList;
029import javax.wvcm.WvcmException;
030import javax.wvcm.PropertyRequestItem.PropertyRequest;
031import javax.wvcm.ProviderFactory.Callback;
032import javax.wvcm.ProviderFactory.Callback.Authentication;
033
034import com.ibm.rational.wvcm.stp.StpException;
035import com.ibm.rational.wvcm.stp.StpProvider;
036import com.ibm.rational.wvcm.stp.StpResource;
037import com.ibm.rational.wvcm.stp.StpProvider.Domain;
038import com.ibm.rational.wvcm.stp.StpProvider.StpCallback;
039import com.ibm.rational.wvcm.stp.cq.CqDbSet;
040import com.ibm.rational.wvcm.stp.cq.CqProvider;
041import com.ibm.rational.wvcm.stp.cq.CqUser;
042import com.ibm.rational.wvcm.stp.cq.CqUserDb;
043
044/**
045 * A collection of static utility methods supporting the CM API examples
046 */
047public class Utilities {
048
049    /**
050     * A simple Authentication object in which the username and password
051     * obtained from the user is cached for use by the CM API.
052     */
053    static class UnPw implements Authentication {
054        /**
055         * Constructs an Authentication object
056         * 
057         * @param unpw A String[] containing the username and password.
058         */
059        UnPw(String[] unpw) {  m_data = unpw; }
060
061        public String loginName() { return m_data[0]; }
062        public String password() { return m_data.length > 1 ? m_data[1] : ""; };
063
064        /** The cached credentials */
065        private String[] m_data;
066    }
067
068    /** Password used by static callback */
069    public static String g_pass = "";
070    /** User name used by static callback */
071    public static String g_user = "admin";
072
073    private static Callback g_callback = new Callback()
074    {
075        public Authentication getAuthentication(String realm, int retryCount)
076        {
077            if (retryCount > 0)
078                throw new IllegalAccessError("Wrong username/password for " + realm);
079            return new Authentication()
080            {
081                public String loginName()
082                {
083                    return g_user;
084                }
085
086                public String password()
087                {
088                    return g_pass;
089                }
090            };
091        }
092    };
093
094    public static CqProvider getStaticProvider()
095    throws Exception
096    {
097        return (CqProvider) ProviderFactory
098            .createProvider(CqProvider.CQ_ONLY_PROVIDER_CLASS, g_callback);        
099    }
100    
101    /**
102     * Constructs an instance of the CM API provider with or without an
103     * authenticator.
104     * 
105     * @return The instantiated Provider object
106     * @throws Exception
107     *             If the Provider could not be instantiated
108     */
109    static StpProvider getProvider() throws Exception {
110        try {
111            Callback callback = new StpCallback() {
112                private UnPw m_unpw;
113    
114                public Authentication getAuthentication(String r, int c)
115                {   return null; /* Will not be called */   }
116    
117                public Authentication getAuthenticationEx(Domain domain,
118                                                          String realm,
119                                                          int retryCount,
120                                                          StpProvider provider,
121                                                          WvcmException failure) 
122                throws WvcmException
123                {
124                    // Try to reuse last credentials on each new repository
125                    if (m_unpw != null && retryCount == 0)
126                        return m_unpw;
127    
128                    String title = "Enter " + domain 
129                            + " Username '+' Password for "
130                            + realm + " [" + retryCount + "]";
131                    
132                    if (failure != null)
133                        title = "Login failed: " + failure + "\n" + title;
134                    
135                    String unpw = JOptionPane.showInputDialog(title, "admin+");
136    
137                    if (unpw == null || unpw.length() == 0)
138                        throw new IllegalAccessError("User canceled request");
139                    
140                    if (unpw.equals("anonymous"))
141                        return null;
142                    
143                    if (unpw.startsWith("@")) {
144                        File file = new File(unpw.substring(1));
145    
146                        try {
147                            FileReader reader = new FileReader(file);
148                            char[] buf = new char[100];
149                            int count = reader.read(buf);
150    
151                            unpw = new String(buf, 0, count);
152                            reader.close();
153                        } catch (Throwable t) {
154                            Utilities.exception(null,
155                                                "Reading password file " + unpw,
156                                                t);
157                        }
158                    }
159    
160                    return m_unpw = new UnPw(unpw.split("\\+", -2));
161                }
162            };
163    
164            // Instantiate a Provider
165            return (StpProvider) ProviderFactory
166                .createProvider(StpProvider.PROVIDER_CLASS, callback);
167        } catch (InvocationTargetException ite) {
168            WvcmException e = (WvcmException) ite.getTargetException();
169    
170            System.out.println("*** " + e);
171            
172            for (Throwable nested: e.getNestedExceptions())
173                System.out.println("***  " + nested);
174    
175            throw e;
176        }
177    }
178
179    static ResourceList<CqUserDb> getUserDbList(CqProvider provider, 
180                                                PropertyRequest feedback)
181    throws WvcmException
182    {
183        PropertyRequest wantedProps =
184            new PropertyRequest(CqDbSet.CURRENT_USER
185                .nest(CqUser.SUBSCRIBED_DATABASES.nest(feedback)));
186        ResourceList<CqUserDb> result = provider.resourceList();
187
188        for (CqDbSet set : provider.doGetDbSetList(wantedProps)) {
189            if (set.getResourceError() == null)
190                result.addAll(set.getCurrentUser().getSubscribedDatabases());
191        }
192
193        return result;
194    }
195    
196    /**
197     * Extracts the message content from a Throwable and returns it as a
198     * hierarchical array of Strings capturing the nesting of the Throwable's
199     * message components. This structure formats reasonably in a SWING
200     * showMessageDialog invocation.
201     * 
202     * @param ex The Throwable object whose message content is to be extracted.
203     * @return If the given Throwable has nested components, an array consisting
204     *         of the Throwable's message and an array of the nested messages.
205     */
206    private static Object messages(Throwable ex) {
207        String msg = ex.getLocalizedMessage();
208    
209        if (msg == null || msg.length() == 0)
210            msg = ex.toString();
211    
212        if (ex instanceof StpException) {
213            Throwable[] nested = ((StpException) ex).getNestedExceptions();
214    
215            if (nested != null && nested.length > 0) {
216                Object[] msgs = new Object[nested.length];
217    
218                for (int i = 0; i < msgs.length; ++i)
219                    msgs[i] = messages(nested[i]);
220    
221                return new Object[] { msg, msgs };
222            }
223        } else if (ex.getCause() != null) {
224            return new Object[] {msg, new Object[]{messages(ex.getCause())}};
225        }
226    
227        return msg;
228    }
229    
230    /**
231     * Displays a Swing dialog containing the messages associated with a given
232     * Throwable.
233     * 
234     * @param frame The parent frame for the message dialog.
235     * @param title The title to appear in the dialog window.
236     * @param ex The throwable whose messages are to be displayed.
237     */
238    static void exception(Component frame, String title, Throwable ex) {
239        JOptionPane.showMessageDialog(frame,
240                                      messages(ex),
241                                      title,
242                                      JOptionPane.ERROR_MESSAGE);
243    }
244
245    /**
246     * Returns a String suitable for displaying the type of resource as
247     * determined by its proxy class.
248     * 
249     * @param resource
250     *            A Resource proxy from which the type of resource will be
251     *            determined.
252     * @return A String containing the simple name of the most derived CM API 
253     * interface implemented by the given proxy.
254     */
255    static String resourceType(Resource resource) {
256        Class interfaces[] = resource.getClass().getInterfaces();
257        Class<?> choice = Resource.class;
258
259        for (int i = 0; i < interfaces.length; ++i) {
260            String name = interfaces[i].getName();
261            if (name.startsWith("com.ibm.rational.wvcm.")
262                || name.startsWith("javax.wvcm")) {
263
264                // Within the CM API, select the most derived interface
265                // this resource implements
266                if (choice.isAssignableFrom(interfaces[i]))
267                    choice = interfaces[i];
268            }
269        }
270
271        String name = choice.getName();
272        
273        return name.substring(1+name.lastIndexOf('.'));
274    }
275    
276    static boolean isListOfResources(Object val)
277    {
278        return val instanceof ResourceList;
279    }
280    
281    static ResourceList<? extends StpResource> toResourceList(StpProvider provider, 
282                                                              List list)
283    {
284        if (list instanceof ResourceList)
285            return (ResourceList<? extends StpResource>)list;
286        
287        return provider.resourceList();
288    }
289}