001 /* 002 * file CreateRecordCommand.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.CreateRecordCommand 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 package com.ibm.rational.stp.client.samples; 016 017 import java.util.ArrayList; 018 import java.util.Arrays; 019 import java.util.ListIterator; 020 import java.util.Map; 021 import java.util.TreeMap; 022 023 import javax.wvcm.PropertyNameList; 024 import javax.wvcm.WvcmException; 025 import javax.wvcm.PropertyNameList.PropertyName; 026 import javax.wvcm.PropertyRequestItem.PropertyRequest; 027 028 import com.ibm.rational.wvcm.stp.StpLocation; 029 import com.ibm.rational.wvcm.stp.StpProperty; 030 import com.ibm.rational.wvcm.stp.StpResource; 031 import com.ibm.rational.wvcm.stp.StpLocation.Namespace; 032 import com.ibm.rational.wvcm.stp.StpProvider.Domain; 033 import com.ibm.rational.wvcm.stp.cq.CqFieldValue; 034 import com.ibm.rational.wvcm.stp.cq.CqProvider; 035 import com.ibm.rational.wvcm.stp.cq.CqRecord; 036 import com.ibm.rational.wvcm.stp.cq.CqRecord.FieldName; 037 038 /** 039 * A sample CM API application that demonstrates the use of the API to create 040 * or update a ClearQuest record in a command line context. 041 * <p> 042 * The first argument is either the type of the record to be created or the name 043 * of a record to be modified. Standard CM API selector syntax must be used. 044 * The target user database must be specified in the repo field, but the domain 045 * and the namespace fields may be omitted as they are understood to be <b>cq</b> 046 * and <b>record</b>, respectively. 047 * 048 * The command-line options supported are 049 * <dl> 050 * <dt>-user <i>user-name</i> 051 * <dd>User logon name. Defaults to "admin"</dd> 052 * <dt>-pass <i>password</i> 053 * <dd>Password for the given user logon name. Defaults to ""</dd> 054 * <dt>-action <i>action name</i> 055 * <dd>The action to be used to update the record. Defaults to the default 056 * Modify-type action defined for the record type.</dd> 057 * <dt>[-set] <i>field-name</i> <i>field-value</i> 058 * <dd>Specify a new value for a field</dd> 059 * <dt>-setlist <i>field-name</i> <i>field-value</i>+ 060 * <dd>Specify a list of new values for a field. The value list is terminated 061 * by the end of the command line or the next argument beginning with a hyphen</dd> 062 * <dt>-show 063 * <dd>Causes the current value of all fields to be displayed after the 064 * operation completes. (If a record is identified by the first parameter, no 065 * fields are specified, and no action is given, no operation is actually 066 * performed: just the fields of the specified record are displayed.)</dd> 067 * </dl> 068 */ 069 public class CreateRecordCommand 070 { 071 /** Generally useful field meta-properties. * */ 072 private static final PropertyNameList FIELD_METAPROPERTIES = 073 new PropertyNameList(CqFieldValue.VALUE, 074 CqFieldValue.NAME, 075 CqFieldValue.TYPE, 076 CqFieldValue.REQUIREDNESS); 077 078 /** Property request for retrieving the user-friendly name of a resource */ 079 public static final PropertyRequest RECORD_REQUEST = 080 new PropertyRequest(StpResource.USER_FRIENDLY_LOCATION, 081 CqRecord.ALL_FIELD_VALUES.nest(StpProperty.VALUE 082 .nest(FIELD_METAPROPERTIES)), 083 CqRecord.STATE_NAME, 084 CqRecord.DEFAULT_ACTION, 085 CqRecord.LEGAL_ACTIONS, 086 CqRecord.RECORD_CLASS); 087 088 /** The CqProvider instance used by the class */ 089 private static CqProvider g_provider; 090 091 /** 092 * The database in which the record will be created determined from the 093 * command line inputs 094 */ 095 private static String g_repo = ""; 096 097 /** 098 * Creates an StpLocation given a partial specification of its selector 099 * 100 * @param name A String containing at least the name field of the selector 101 * @param namespace The namespace required by context 102 * @return An StpLocation for the given location 103 * @throws WvcmException if the specified location is malformed. 104 */ 105 static StpLocation normalize(Namespace namespace, 106 String name) throws WvcmException 107 { 108 StpLocation loc = g_provider.stpLocation(name); 109 110 if (loc.getDomain() == Domain.NONE) 111 loc = loc.recomposeWithDomain(Domain.CLEAR_QUEST); 112 113 if (loc.getNamespace() == Namespace.NONE) { 114 loc = loc.recomposeWithNamespace(namespace); 115 } 116 117 if (g_repo.equals("")) 118 g_repo = loc.getRepo(); 119 else if (loc.getRepo().equals("")) 120 loc = loc.recomposeWithRepo(g_repo); 121 122 return loc; 123 } 124 125 /** 126 * Creates a new record using field values specified on the command line 127 * 128 * @param args An array of String objects, each containing a command option 129 * or command option argument as described in the class overview 130 * comments. 131 * @throws Exception If things go wrong. 132 */ 133 public static void main(String[] args) throws Exception 134 { 135 run(args, RECORD_REQUEST); 136 System.exit(0); 137 } 138 139 /** 140 * Creates a new record using field values specified on the command line 141 * 142 * @param args An array of String objects, each containing a command option 143 * or command option argument as described in the class overview 144 * comments. 145 * @param wanted Target properties to be included in the returned proxy. 146 * Must include USER_FRIENDLY_LOCATION 147 * @return A proxy for the created/modified record if the operation was 148 * successful; otherwise <b>null</b> 149 * @throws Exception If things go wrong. 150 */ 151 public static CqRecord run(String[] args, 152 PropertyRequest wanted) throws Exception 153 { 154 // Establish a session to ClearQuest 155 g_provider = Utilities.getStaticProvider(); 156 157 boolean doShow = false; 158 StpLocation actionLoc = null; 159 ListIterator<String> argl = Arrays.asList(args).listIterator(0); 160 StpLocation target = normalize(Namespace.RECORD, argl.next()); 161 boolean doCreate = target.getNameSegmentCount() == 1; 162 CqRecord record = 163 g_provider.cqRecord(doCreate? (StpLocation) target.child("new") 164 : target); 165 166 // Read and process the remaining command line arguments 167 while (argl.hasNext()) { 168 String arg = argl.next(); 169 170 if (arg.equals("-url")) 171 g_provider.setServerUrl(argl.next()); 172 else if (arg.equals("-user")) 173 Utilities.g_user = argl.next(); 174 else if (arg.equals("-password")) 175 Utilities.g_pass = argl.next(); 176 else if (arg.equals("-action")) 177 actionLoc = normalize(Namespace.ACTION, argl.next()); 178 else if (arg.equals("-show")) 179 doShow = true; 180 else if (arg.equals("-setlist")) { 181 arg = argl.next(); 182 183 ArrayList<String> values = new ArrayList<String>(); 184 185 while (argl.hasNext()) { 186 String value = argl.next(); 187 188 if (value.startsWith("-")) { 189 argl.previous(); 190 break; 191 } else 192 values.add(value); 193 } 194 195 record.setProperty(new FieldName<Object>(arg), values); 196 } else { 197 if (arg.equals("-set")) 198 arg = argl.next(); 199 200 record.setProperty(new FieldName<String>(arg), argl.next()); 201 } 202 } 203 204 // Print the fields to be set 205 PropertyName<?>[] updated = 206 record.updatedPropertyNameList().getPropertyNames(); 207 208 for (PropertyName<?> name : updated) 209 System.out.println(name.getName() + " => " 210 + record.getProperty(name)); 211 212 // Read, create, or update record in database 213 try { 214 PropertyRequest rl = wanted; 215 216 if (doCreate) { 217 record = record.doCreateRecord(rl); 218 System.out.println("Created " 219 + record.getUserFriendlyLocation()); 220 } else if (actionLoc != null) { 221 // If record type was omitted from action location, insert it 222 // now. 223 // e.g.: input 'assign' became 'cq.action:assign@7.0.1/SAMPL'; 224 // now make it 'cq.action:Defect/assign@7.0.1/SAMPL' 225 if (actionLoc.getNameSegmentCount() == 1) 226 actionLoc = 227 (StpLocation) actionLoc.parent().child(target.parent() 228 .lastSegment()).child(actionLoc.lastSegment()); 229 230 record = 231 (CqRecord) record.setAction(g_provider.cqAction(actionLoc)) 232 .doWriteProperties(rl); 233 System.out.println(actionLoc.lastSegment() + "(ed)" + target); 234 } else { 235 // If no action was specified, use doReadProperties. Modified 236 // fields will be written if needed. 237 record = (CqRecord) record.doReadProperties(wanted); 238 System.out.println((updated.length > 0? "Updated" : "Found") 239 + record.getUserFriendlyLocation()); 240 } 241 } catch (WvcmException ex) { 242 System.out.println("Operation could not be completed: " 243 + ex.getLocalizedMessage()); 244 245 // This will identify specific fields that were in error 246 for (Throwable nested : ex.getNestedExceptions()) 247 System.out.println(nested.toString()); 248 249 return null; 250 } 251 252 if (doShow) { 253 // Collect, sort, and print the field values 254 Map<String, Object> report = new TreeMap<String, Object>(); 255 String pad = ""; 256 257 for (CqFieldValue field : record.getAllFieldValues()) { 258 report.put(field.getName(), field.getValue()); 259 260 while (field.getName().length() > pad.length()) 261 pad += " "; 262 } 263 264 for (String name : report.keySet()) { 265 System.out.println(pad.substring(name.length()) + name + " : " 266 + showValue(pad + " ", report.get(name))); 267 } 268 } 269 270 return record; 271 } 272 273 private static Object showValue(String pad, 274 Object value) 275 { 276 if (value != null) { 277 StringBuffer sb = new StringBuffer(); 278 String prefix = "{"; 279 280 if (value instanceof Iterable) { 281 for (Object item : (Iterable) value) { 282 sb.append(prefix + item); 283 prefix = "\n" + pad + " "; 284 } 285 286 sb.append("}"); 287 288 return sb.toString(); 289 } else if (value.getClass().isArray()) { 290 prefix = "["; 291 292 for (Object item : (Object[]) value) { 293 sb.append(prefix + item); 294 prefix = "\n" + pad + " "; 295 } 296 297 sb.append("]"); 298 299 return sb.toString(); 300 } 301 } 302 303 return value; 304 } 305 }