source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 23.2 KB
Line 
1/*
2 * Copyright 2000-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs.optional.vss;
19
20import org.apache.tools.ant.types.EnumeratedAttribute;
21import java.io.File;
22import java.io.IOException;
23import java.text.DateFormat;
24import java.text.ParseException;
25import java.util.Calendar;
26import java.util.Date;
27import java.util.GregorianCalendar;
28
29import org.apache.tools.ant.BuildException;
30import org.apache.tools.ant.Project;
31import org.apache.tools.ant.Task;
32import org.apache.tools.ant.taskdefs.Execute;
33import org.apache.tools.ant.taskdefs.LogStreamHandler;
34import org.apache.tools.ant.types.Commandline;
35
36/**
37 * A base class for creating tasks for executing commands on Visual SourceSafe.
38 * <p>
39 * The class extends the 'exec' task as it operates by executing the ss.exe program
40 * supplied with SourceSafe. By default the task expects ss.exe to be in the path,
41 * you can override this be specifying the ssdir attribute.
42 * </p>
43 * <p>
44 * This class provides set and get methods for 'login' and 'vsspath' attributes. It
45 * also contains constants for the flags that can be passed to SS.
46 * </p>
47 *
48 */
49public abstract class MSVSS extends Task implements MSVSSConstants {
50
51 private String ssDir = null;
52 private String vssLogin = null;
53 private String vssPath = null;
54 private String serverPath = null;
55
56 /** Version */
57 private String version = null;
58 /** Date */
59 private String date = null;
60 /** Label */
61 private String label = null;
62 /** Auto response */
63 private String autoResponse = null;
64 /** Local path */
65 private String localPath = null;
66 /** Comment */
67 private String comment = null;
68 /** From label */
69 private String fromLabel = null;
70 /** To label */
71 private String toLabel = null;
72 /** Output file name */
73 private String outputFileName = null;
74 /** User */
75 private String user = null;
76 /** From date */
77 private String fromDate = null;
78 /** To date */
79 private String toDate = null;
80 /** History style */
81 private String style = null;
82 /** Quiet defaults to false */
83 private boolean quiet = false;
84 /** Recursive defaults to false */
85 private boolean recursive = false;
86 /** Writable defaults to false */
87 private boolean writable = false;
88 /** Fail on error defaults to true */
89 private boolean failOnError = true;
90 /** Get local copy for checkout defaults to true */
91 private boolean getLocalCopy = true;
92 /** Number of days offset for History */
93 private int numDays = Integer.MIN_VALUE;
94 /** Date format for History */
95 private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
96 /** Timestamp for retreived files */
97 private CurrentModUpdated timestamp = null;
98 /** Behaviour for writable files */
99 private WritableFiles writableFiles = null;
100
101 /**
102 * Each sub-class must implemnt this method and return the constructed
103 * command line to be executed. It is up to the sub-task to determine the
104 * required attrubutes and their order.
105 * @return The Constructed command line.
106 */
107 abstract Commandline buildCmdLine();
108
109 /**
110 * Directory where <code>ss.exe</code> resides.
111 * By default the task expects it to be in the PATH.
112 * @param dir The directory containing ss.exe.
113 */
114 public final void setSsdir(String dir) {
115 this.ssDir = Project.translatePath(dir);
116 }
117
118 /**
119 * Login to use when accessing VSS, formatted as "username,password".
120 * <p>
121 * You can omit the password if your database is not password protected.
122 * If you have a password and omit it, Ant will hang.
123 * @param vssLogin The login string to use.
124 */
125 public final void setLogin(final String vssLogin) {
126 this.vssLogin = vssLogin;
127 }
128
129 /**
130 * SourceSafe path which specifies the project/file(s) you wish to perform
131 * the action on.
132 * <p>
133 * A prefix of 'vss://' will be removed if specified.
134 * @param vssPath The VSS project path.
135 * @ant.attribute group="required"
136 */
137 public final void setVsspath(final String vssPath) {
138 String projectPath;
139 if (vssPath.startsWith("vss://")) { //$NON-NLS-1$
140 projectPath = vssPath.substring(5);
141 } else {
142 projectPath = vssPath;
143 }
144
145 if (projectPath.startsWith(PROJECT_PREFIX)) {
146 this.vssPath = projectPath;
147 } else {
148 this.vssPath = PROJECT_PREFIX + projectPath;
149 }
150 }
151
152 /**
153 * Directory where <code>srssafe.ini</code> resides.
154 * @param serverPath The path to the VSS server.
155 */
156 public final void setServerpath(final String serverPath) {
157 this.serverPath = serverPath;
158 }
159
160 /**
161 * Indicates if the build should fail if the Sourcesafe command does. Defaults to true.
162 * @param failOnError True if task should fail on any error.
163 */
164 public final void setFailOnError(final boolean failOnError) {
165 this.failOnError = failOnError;
166 }
167
168 /**
169 * Executes the task. <br>
170 * Builds a command line to execute ss.exe and then calls Exec's run method
171 * to execute the command line.
172 * @throws BuildException if the command cannot execute.
173 */
174 public void execute() throws BuildException {
175 int result = 0;
176 Commandline commandLine = buildCmdLine();
177 result = run(commandLine);
178 if (Execute.isFailure(result) && getFailOnError()) {
179 String msg = "Failed executing: " + formatCommandLine(commandLine)
180 + " With a return code of " + result;
181 throw new BuildException(msg, getLocation());
182 }
183 }
184
185 // Special setters for the sub-classes
186
187 protected void setInternalComment(final String comment) {
188 this.comment = comment;
189 }
190
191 protected void setInternalAutoResponse(final String autoResponse) {
192 this.autoResponse = autoResponse;
193 }
194
195 protected void setInternalDate(final String date) {
196 this.date = date;
197 }
198
199 protected void setInternalDateFormat(final DateFormat dateFormat) {
200 this.dateFormat = dateFormat;
201 }
202
203 protected void setInternalFailOnError(final boolean failOnError) {
204 this.failOnError = failOnError;
205 }
206
207 protected void setInternalFromDate(final String fromDate) {
208 this.fromDate = fromDate;
209 }
210
211 protected void setInternalFromLabel(final String fromLabel) {
212 this.fromLabel = fromLabel;
213 }
214
215 protected void setInternalLabel(final String label) {
216 this.label = label;
217 }
218
219 protected void setInternalLocalPath(final String localPath) {
220 this.localPath = localPath;
221 }
222
223 protected void setInternalNumDays(final int numDays) {
224 this.numDays = numDays;
225 }
226
227 protected void setInternalOutputFilename(final String outputFileName) {
228 this.outputFileName = outputFileName;
229 }
230
231 protected void setInternalQuiet(final boolean quiet) {
232 this.quiet = quiet;
233 }
234
235 protected void setInternalRecursive(final boolean recursive) {
236 this.recursive = recursive;
237 }
238
239 protected void setInternalStyle(final String style) {
240 this.style = style;
241 }
242
243 protected void setInternalToDate(final String toDate) {
244 this.toDate = toDate;
245 }
246
247 protected void setInternalToLabel(final String toLabel) {
248 this.toLabel = toLabel;
249 }
250
251 protected void setInternalUser(final String user) {
252 this.user = user;
253 }
254
255 protected void setInternalVersion(final String version) {
256 this.version = version;
257 }
258
259 protected void setInternalWritable(final boolean writable) {
260 this.writable = writable;
261 }
262
263 protected void setInternalFileTimeStamp(final CurrentModUpdated timestamp) {
264 this.timestamp = timestamp;
265 }
266
267 protected void setInternalWritableFiles(final WritableFiles writableFiles) {
268 this.writableFiles = writableFiles;
269 }
270
271 protected void setInternalGetLocalCopy(final boolean getLocalCopy) {
272 this.getLocalCopy = getLocalCopy;
273 }
274
275 /**
276 * Gets the sscommand string. "ss" or "c:\path\to\ss"
277 * @return The path to ss.exe or just ss if sscommand is not set.
278 */
279 protected String getSSCommand() {
280 if (ssDir == null) {
281 return SS_EXE;
282 }
283 return ssDir.endsWith(File.separator) ? ssDir + SS_EXE : ssDir
284 + File.separator + SS_EXE;
285 }
286
287 /**
288 * Gets the vssserverpath string.
289 * @return null if vssserverpath is not set.
290 */
291 protected String getVsspath() {
292 return vssPath;
293 }
294
295 /**
296 * Gets the quiet string. -O-
297 * @return An empty string if quiet is not set or is false.
298 */
299 protected String getQuiet() {
300 return quiet ? FLAG_QUIET : "";
301 }
302
303 /**
304 * Gets the recursive string. "-R"
305 * @return An empty string if recursive is not set or is false.
306 */
307 protected String getRecursive() {
308 return recursive ? FLAG_RECURSION : "";
309 }
310
311 /**
312 * Gets the writable string. "-W"
313 * @return An empty string if writable is not set or is false.
314 */
315 protected String getWritable() {
316 return writable ? FLAG_WRITABLE : "";
317 }
318
319 /**
320 * Gets the label string. "-Lbuild1"
321 * Max label length is 32 chars
322 * @return An empty string if label is not set.
323 */
324 protected String getLabel() {
325 String shortLabel="";
326 if (label != null && label.length() > 0) {
327 shortLabel = FLAG_LABEL + getShortLabel();
328 }
329 return shortLabel;
330 }
331 /**
332 * Return at most the 30 first chars of the label, logging a warning message about the truncation
333 * @return at most the 30 first chars of the label
334 */
335 private String getShortLabel() {
336 String shortLabel;
337 if (label != null && label.length() > 31) {
338 shortLabel = this.label.substring(0, 30);
339 log("Label is longer than 31 characters, truncated to: " + shortLabel, Project.MSG_WARN);
340 } else {
341 shortLabel = label;
342 }
343 return shortLabel;
344 }
345 /**
346 * Gets the style string. "-Lbuild1"
347 * @return An empty string if label is not set.
348 */
349 protected String getStyle() {
350 return style != null ? style : "";
351 }
352
353 /**
354 * Gets the version string. Returns the first specified of version "-V1.0",
355 * date "-Vd01.01.01", label "-Vlbuild1".
356 * @return An empty string if a version, date and label are not set.
357 */
358 protected String getVersionDateLabel() {
359 String versionDateLabel = "";
360 if (version != null) {
361 versionDateLabel = FLAG_VERSION + version;
362 } else if (date != null) {
363 versionDateLabel = FLAG_VERSION_DATE + date;
364 } else {
365 // Use getShortLabel() so labels longer then 30 char are truncated
366 // and the user is warned
367 String shortLabel = getShortLabel();
368 if (shortLabel != null && !shortLabel.equals("")) {
369 versionDateLabel = FLAG_VERSION_LABEL + shortLabel;
370 }
371 }
372 return versionDateLabel;
373 }
374
375 /**
376 * Gets the version string.
377 * @return An empty string if a version is not set.
378 */
379 protected String getVersion() {
380 return version != null ? FLAG_VERSION + version : "";
381 }
382
383 /**
384 * Gets the localpath string. "-GLc:\source" <p>
385 * The localpath is created if it didn't exist.
386 * @return An empty string if localpath is not set.
387 */
388 protected String getLocalpath() {
389 String lclPath = ""; //set to empty str if no local path return
390 if (localPath != null) {
391 //make sure m_LocalDir exists, create it if it doesn't
392 File dir = getProject().resolveFile(localPath);
393 if (!dir.exists()) {
394 boolean done = dir.mkdirs();
395 if (!done) {
396 String msg = "Directory " + localPath + " creation was not "
397 + "successful for an unknown reason";
398 throw new BuildException(msg, getLocation());
399 }
400 getProject().log("Created dir: " + dir.getAbsolutePath());
401 }
402 lclPath = FLAG_OVERRIDE_WORKING_DIR + localPath;
403 }
404 return lclPath;
405 }
406
407 /**
408 * Gets the comment string. "-Ccomment text"
409 * @return A comment of "-" if comment is not set.
410 */
411 protected String getComment() {
412 return comment != null ? FLAG_COMMENT + comment : FLAG_COMMENT + "-";
413 }
414
415 /**
416 * Gets the auto response string. This can be Y "-I-Y" or N "-I-N".
417 * @return The default value "-I-" if autoresponse is not set.
418 */
419 protected String getAutoresponse() {
420 if (autoResponse == null) {
421 return FLAG_AUTORESPONSE_DEF;
422 } else if (autoResponse.equalsIgnoreCase("Y")) {
423 return FLAG_AUTORESPONSE_YES;
424 } else if (autoResponse.equalsIgnoreCase("N")) {
425 return FLAG_AUTORESPONSE_NO;
426 } else {
427 return FLAG_AUTORESPONSE_DEF;
428 }
429 }
430
431 /**
432 * Gets the login string. This can be user and password, "-Yuser,password"
433 * or just user "-Yuser".
434 * @return An empty string if login is not set.
435 */
436 protected String getLogin() {
437 return vssLogin != null ? FLAG_LOGIN + vssLogin : "";
438 }
439
440 /**
441 * Gets the output file string. "-Ooutput.file"
442 * @return An empty string if user is not set.
443 */
444 protected String getOutput() {
445 return outputFileName != null ? FLAG_OUTPUT + outputFileName : "";
446 }
447
448 /**
449 * Gets the user string. "-Uusername"
450 * @return An empty string if user is not set.
451 */
452 protected String getUser() {
453 return user != null ? FLAG_USER + user : "";
454 }
455
456 /**
457 * Gets the version string. This can be to-from "-VLbuild2~Lbuild1", from
458 * "~Lbuild1" or to "-VLbuild2".
459 * @return An empty string if neither tolabel or fromlabel are set.
460 */
461 protected String getVersionLabel() {
462 if (fromLabel == null && toLabel == null) {
463 return "";
464 }
465 if (fromLabel != null && toLabel != null) {
466 if (fromLabel.length() > 31) {
467 fromLabel = fromLabel.substring(0, 30);
468 log("FromLabel is longer than 31 characters, truncated to: "
469 + fromLabel, Project.MSG_WARN);
470 }
471 if (toLabel.length() > 31) {
472 toLabel = toLabel.substring(0, 30);
473 log("ToLabel is longer than 31 characters, truncated to: "
474 + toLabel, Project.MSG_WARN);
475 }
476 return FLAG_VERSION_LABEL + toLabel + VALUE_FROMLABEL + fromLabel;
477 } else if (fromLabel != null) {
478 if (fromLabel.length() > 31) {
479 fromLabel = fromLabel.substring(0, 30);
480 log("FromLabel is longer than 31 characters, truncated to: "
481 + fromLabel, Project.MSG_WARN);
482 }
483 return FLAG_VERSION + VALUE_FROMLABEL + fromLabel;
484 } else {
485 if (toLabel.length() > 31) {
486 toLabel = toLabel.substring(0, 30);
487 log("ToLabel is longer than 31 characters, truncated to: "
488 + toLabel, Project.MSG_WARN);
489 }
490 return FLAG_VERSION_LABEL + toLabel;
491 }
492 }
493
494 /**
495 * Gets the Version date string.
496 * @return An empty string if neither Todate or from date are set.
497 * @throws BuildException
498 */
499 protected String getVersionDate() throws BuildException {
500 if (fromDate == null && toDate == null
501 && numDays == Integer.MIN_VALUE) {
502 return "";
503 }
504 if (fromDate != null && toDate != null) {
505 return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE + fromDate;
506 } else if (toDate != null && numDays != Integer.MIN_VALUE) {
507 try {
508 return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE
509 + calcDate(toDate, numDays);
510 } catch (ParseException ex) {
511 String msg = "Error parsing date: " + toDate;
512 throw new BuildException(msg, getLocation());
513 }
514 } else if (fromDate != null && numDays != Integer.MIN_VALUE) {
515 try {
516 return FLAG_VERSION_DATE + calcDate(fromDate, numDays)
517 + VALUE_FROMDATE + fromDate;
518 } catch (ParseException ex) {
519 String msg = "Error parsing date: " + fromDate;
520 throw new BuildException(msg, getLocation());
521 }
522 } else {
523 return fromDate != null ? FLAG_VERSION + VALUE_FROMDATE
524 + fromDate : FLAG_VERSION_DATE + toDate;
525 }
526 }
527
528 /**
529 * Builds and returns the -G- flag if required.
530 * @return An empty string if get local copy is true.
531 */
532 protected String getGetLocalCopy() {
533 return (!getLocalCopy) ? FLAG_NO_GET : "";
534 }
535
536 /**
537 * Gets the value of the fail on error flag.
538 * @return True if the FailOnError flag has been set or if 'writablefiles=skip'.
539 */
540 private boolean getFailOnError() {
541 return getWritableFiles().equals(WRITABLE_SKIP) ? false : failOnError;
542 }
543
544
545 /**
546 * Gets the value set for the FileTimeStamp.
547 * if it equals "current" then we return -GTC
548 * if it equals "modified" then we return -GTM
549 * if it equals "updated" then we return -GTU
550 * otherwise we return -GTC
551 *
552 * @return The default file time flag, if not set.
553 */
554 public String getFileTimeStamp() {
555 if (timestamp == null) {
556 return "";
557 } else if (timestamp.getValue().equals(TIME_MODIFIED)) {
558 return FLAG_FILETIME_MODIFIED;
559 } else if (timestamp.getValue().equals(TIME_UPDATED)) {
560 return FLAG_FILETIME_UPDATED;
561 } else {
562 return FLAG_FILETIME_DEF;
563 }
564 }
565
566
567 /**
568 * Gets the value to determine the behaviour when encountering writable files.
569 * @return An empty String, if not set.
570 */
571 public String getWritableFiles() {
572 if (writableFiles == null) {
573 return "";
574 } else if (writableFiles.getValue().equals(WRITABLE_REPLACE)) {
575 return FLAG_REPLACE_WRITABLE;
576 } else if (writableFiles.getValue().equals(WRITABLE_SKIP)) {
577 // ss.exe exits with '100', when files have been skipped
578 // so we have to ignore the failure
579 failOnError = false;
580 return FLAG_SKIP_WRITABLE;
581 } else {
582 return "";
583 }
584 }
585
586 /**
587 * Sets up the required environment and executes the command line.
588 *
589 * @param cmd The command line to execute.
590 * @return The return code from the exec'd process.
591 */
592 private int run(Commandline cmd) {
593 try {
594 Execute exe = new Execute(new LogStreamHandler(this,
595 Project.MSG_INFO,
596 Project.MSG_WARN));
597
598 // If location of ss.ini is specified we need to set the
599 // environment-variable SSDIR to this value
600 if (serverPath != null) {
601 String[] env = exe.getEnvironment();
602 if (env == null) {
603 env = new String[0];
604 }
605 String[] newEnv = new String[env.length + 1];
606 for (int i = 0; i < env.length; i++) {
607 newEnv[i] = env[i];
608 }
609 newEnv[env.length] = "SSDIR=" + serverPath;
610
611 exe.setEnvironment(newEnv);
612 }
613
614 exe.setAntRun(getProject());
615 exe.setWorkingDirectory(getProject().getBaseDir());
616 exe.setCommandline(cmd.getCommandline());
617 // Use the OS launcher so we get environment variables
618 exe.setVMLauncher(false);
619 return exe.execute();
620 } catch (IOException e) {
621 throw new BuildException(e, getLocation());
622 }
623 }
624
625 /**
626 * Calculates the start date for version comparison.
627 * <p>
628 * Calculates the date numDay days earlier than startdate.
629 * @param startDate The start date.
630 * @param daysToAdd The number of days to add.
631 * @return The calculated date.
632 * @throws ParseException
633 */
634 private String calcDate(String startDate, int daysToAdd) throws ParseException {
635 Date currentDate = new Date();
636 Calendar calendar = new GregorianCalendar();
637 currentDate = dateFormat.parse(startDate);
638 calendar.setTime(currentDate);
639 calendar.add(Calendar.DATE, daysToAdd);
640 return dateFormat.format(calendar.getTime());
641 }
642
643 /**
644 * Changes the password to '***' so it isn't displayed on screen if the build fails
645 *
646 * @param cmd The command line to clean
647 * @return The command line as a string with out the password
648 */
649 private String formatCommandLine(Commandline cmd) {
650 StringBuffer sBuff = new StringBuffer(cmd.toString());
651 int indexUser = sBuff.substring(0).indexOf(FLAG_LOGIN);
652 if (indexUser > 0) {
653 int indexPass = sBuff.substring(0).indexOf(",", indexUser);
654 int indexAfterPass = sBuff.substring(0).indexOf(" ", indexPass);
655
656 for (int i = indexPass + 1; i < indexAfterPass; i++) {
657 sBuff.setCharAt(i, '*');
658 }
659 }
660 return sBuff.toString();
661 }
662
663 /**
664 * Extention of EnumeratedAttribute to hold the values for file time stamp.
665 */
666 public static class CurrentModUpdated extends EnumeratedAttribute {
667 /**
668 * Gets the list of allowable values.
669 * @return The values.
670 */
671 public String[] getValues() {
672 return new String[] {TIME_CURRENT, TIME_MODIFIED, TIME_UPDATED};
673 }
674 }
675
676 /**
677 * Extention of EnumeratedAttribute to hold the values for writable filess.
678 */
679 public static class WritableFiles extends EnumeratedAttribute {
680 /**
681 * Gets the list of allowable values.
682 * @return The values.
683 */
684 public String[] getValues() {
685 return new String[] {WRITABLE_REPLACE, WRITABLE_SKIP, WRITABLE_FAIL};
686 }
687 }
688}
Note: See TracBrowser for help on using the repository browser.