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 * © 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 }