Source Code Cross Referenced for TeamFoundationServer.java in  » Build » cruisecontrol » net » sourceforge » cruisecontrol » sourcecontrols » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Build » cruisecontrol » net.sourceforge.cruisecontrol.sourcecontrols 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package net.sourceforge.cruisecontrol.sourcecontrols;
002:
003:        import java.io.BufferedReader;
004:        import java.io.IOException;
005:        import java.io.InputStream;
006:        import java.io.InputStreamReader;
007:        import java.io.Reader;
008:        import java.text.DateFormat;
009:        import java.text.ParseException;
010:        import java.text.SimpleDateFormat;
011:        import java.util.ArrayList;
012:        import java.util.Date;
013:        import java.util.List;
014:        import java.util.Locale;
015:        import java.util.Map;
016:        import java.util.TimeZone;
017:        import java.util.regex.Matcher;
018:        import java.util.regex.Pattern;
019:
020:        import net.sourceforge.cruisecontrol.CruiseControlException;
021:        import net.sourceforge.cruisecontrol.Modification;
022:        import net.sourceforge.cruisecontrol.SourceControl;
023:        import net.sourceforge.cruisecontrol.util.Commandline;
024:        import net.sourceforge.cruisecontrol.util.StreamLogger;
025:        import net.sourceforge.cruisecontrol.util.ValidationHelper;
026:
027:        import org.apache.log4j.Logger;
028:
029:        /**
030:         * The class implements the SourceControl interface to allow communication with
031:         * Microsoft Visual Studio Team Foundation Server
032:         * 
033:         * @author <a href="http://www.woodwardweb.com">Martin Woodward</a>
034:         */
035:        public class TeamFoundationServer implements  SourceControl {
036:
037:            private static final Logger LOG = Logger
038:                    .getLogger(TeamFoundationServer.class);
039:
040:            /** UTC Date format - best one to pass dates across the wire. */
041:            private static final String TFS_UTC_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
042:
043:            /** Configuration parameters */
044:
045:            private String server;
046:            private String projectPath;
047:            private String username;
048:            private String password;
049:            private String tfPath = "tf";
050:            private String options;
051:
052:            private final SourceControlProperties properties = new SourceControlProperties();
053:
054:            /**
055:             * The main getModification method called by the build loop. Responsible for
056:             * querying history from TFS, parsing the results and then transforming that
057:             * into a list of CruiseControl Modification objects.
058:             * 
059:             * @see net.sourceforge.cruisecontrol.SourceControl
060:             *      getModifications(java.util.Date, java.util.Date)
061:             */
062:            public List getModifications(Date lastBuild, Date now) {
063:
064:                List modifications = new ArrayList();
065:                final Commandline command = buildHistoryCommand(lastBuild, now);
066:
067:                try {
068:                    modifications = execHistoryCommand(command, lastBuild);
069:                } catch (Exception e) {
070:                    LOG.error("Error executing tf history command " + command,
071:                            e);
072:                }
073:
074:                fillPropertiesIfNeeded(modifications);
075:                return modifications;
076:            }
077:
078:            /**
079:             * Populate the source control properties.  As well as detecting if modifications found
080:             * and if any deletion is found, also put the maximum changeset found in the modification list.
081:             * The changeset ID represents the state of the repository at the time the modifications were
082:             * detected and therefore can be used in a subsquent get, label etc to ensure consistency.
083:             * @param modifications the list of modifications reported by TFS
084:             */
085:            void fillPropertiesIfNeeded(List modifications) {
086:                if (!modifications.isEmpty()) {
087:                    properties.modificationFound();
088:                    int maxChangset = 0;
089:                    for (int i = 0; i < modifications.size(); i++) {
090:                        Modification modification = (Modification) modifications
091:                                .get(i);
092:                        maxChangset = Math.max(maxChangset, Integer
093:                                .parseInt(modification.revision));
094:                        Modification.ModifiedFile file = (Modification.ModifiedFile) modification.files
095:                                .get(0);
096:                        if (file.action.equals("delete")) {
097:                            properties.deletionFound();
098:                            break;
099:                        }
100:                    }
101:                    properties.put("tfschangeset", "" + maxChangset);
102:                }
103:            }
104:
105:            /**
106:             * Build a history command like the following:-
107:             * 
108:             * tf history -noprompt -server:http://tfsserver:8080 $/TeamProjectName/path
109:             * -version:D2006-12-01T01:01:01Z~D2006-12-13T20:00:00Z -recursive
110:             * -format:detailed -login:DOMAIN\name,password
111:             * 
112:             * For more details on history command syntax see
113:             * 
114:             * <a href="http://msdn2.microsoft.com/en-us/library/yxtbh4yh(VS.80).aspx">
115:             * http://msdn2.microsoft.com/en-us/library/yxtbh4yh(VS.80).aspx </a>
116:             */
117:            Commandline buildHistoryCommand(Date lastBuild, Date now) {
118:
119:                Commandline command = new Commandline();
120:                command.setExecutable(tfPath);
121:                command.createArgument().setValue("history");
122:                command.createArgument().setValue("-noprompt");
123:                command.createArgument().setValue("-server:" + server);
124:                command.createArgument().setValue(projectPath);
125:
126:                command.createArgument().setValue(
127:                        "-version:D" + formatUTCDate(lastBuild) + "~D"
128:                                + formatUTCDate(now));
129:
130:                command.createArgument().setValue("-recursive");
131:                command.createArgument().setValue("-format:detailed");
132:
133:                if (username != null && password != null) {
134:                    command.createArgument().setValue(
135:                            "-login:" + username + "," + password + "");
136:                }
137:
138:                if (options != null) {
139:                    command.createArgument().setValue(options);
140:                }
141:
142:                LOG.debug("Executing command: " + command);
143:
144:                return command;
145:            }
146:
147:            private List execHistoryCommand(Commandline command, Date lastBuild)
148:                    throws InterruptedException, IOException, ParseException {
149:
150:                Process p = command.execute();
151:
152:                logErrorStream(p);
153:                InputStream svnStream = p.getInputStream();
154:                List modifications = parseStream(svnStream, lastBuild);
155:
156:                p.waitFor();
157:                p.getInputStream().close();
158:                p.getOutputStream().close();
159:                p.getErrorStream().close();
160:
161:                return modifications;
162:            }
163:
164:            /**
165:             * Helper method to send stderr from the tf command to CruiseControl stderr
166:             */
167:            private void logErrorStream(Process p) {
168:                Thread stderr = new Thread(StreamLogger.getWarnPumper(LOG, p
169:                        .getErrorStream()));
170:                stderr.start();
171:            }
172:
173:            /**
174:             * Parse the result stream. Delegates to the TFSHistoryParser.parse method.
175:             */
176:            private List parseStream(InputStream tfStream, Date lastBuild)
177:                    throws IOException, ParseException {
178:
179:                InputStreamReader reader = new InputStreamReader(tfStream,
180:                        "UTF-8");
181:                return TFHistoryParser.parse(reader, lastBuild);
182:            }
183:
184:            /**
185:             * Convert the passed date into the UTC Date format best used when talking
186:             * to Team Foundation Server command line.
187:             */
188:            static String formatUTCDate(Date date) {
189:                DateFormat f = new SimpleDateFormat(TFS_UTC_DATE_FORMAT);
190:                f.setTimeZone(TimeZone.getTimeZone("GMT"));
191:                return f.format(date);
192:            }
193:
194:            /**
195:             * @see net.sourceforge.cruisecontrol.SourceControl#getProperties()
196:             */
197:            public Map getProperties() {
198:                return properties.getPropertiesAndReset();
199:            }
200:
201:            /**
202:             * Validates that the plug-in has its mandatory inputs satisfied. The only
203:             * mandatory requirements are a server and project path.
204:             * 
205:             * @see net.sourceforge.cruisecontrol.SourceControl#validate()
206:             */
207:            public void validate() throws CruiseControlException {
208:                ValidationHelper.assertIsSet(server, "server", this .getClass());
209:                ValidationHelper.assertIsSet(projectPath, "projectPath", this 
210:                        .getClass());
211:                ValidationHelper.assertTrue(projectPath.startsWith("$/"),
212:                        "A TFS server path must begin with $/");
213:            }
214:
215:            /**
216:             * Internal class to handle parsing of TF command line output.
217:             */
218:            static final class TFHistoryParser {
219:
220:                private TFHistoryParser() {
221:                }
222:
223:                private static final String CHANGESET_SEPERATOR = "---------------------------------";
224:
225:                /**
226:                 * The magic regex to identify the key data elements within the
227:                 * changeset *
228:                 */
229:                private static final Pattern PATTERN_CHANGESET = Pattern
230:                        .compile("^[^:]*:[ \t]([0-9]*)\n"
231:                                + "[^:]*:[ \t](.*)\n[^:]*:[ \t](.*)\n"
232:                                + "[^:]*:((?:\n.*)*)\n\n[^\n :]*:(?=\n  )((?:\n[ \t]+.*)*)");
233:
234:                /**
235:                 * An additional regex to split the items into their parts (change type
236:                 * and filename) *
237:                 */
238:                private static final Pattern PATTERN_ITEM = Pattern
239:                        .compile("\n  ([^$]+) (\\$/.*)");
240:
241:                /**
242:                 * Parse the passed stream of data from the command line.
243:                 */
244:                static List parse(Reader reader, Date lastBuild)
245:                        throws IOException, ParseException {
246:                    ArrayList modifications = new ArrayList();
247:                    StringBuffer buffer = new StringBuffer();
248:
249:                    BufferedReader br = new BufferedReader(reader);
250:                    String line;
251:                    int linecount = 0;
252:
253:                    while ((line = br.readLine()) != null) {
254:                        linecount++;
255:                        if (line.startsWith(CHANGESET_SEPERATOR)) {
256:                            if (linecount > 1) {
257:                                // We are starting a new changeset.
258:                                modifications.addAll(parseChangeset(buffer
259:                                        .toString(), lastBuild));
260:                                buffer.setLength(0);
261:                            }
262:                        } else {
263:                            buffer.append(line).append('\n');
264:                        }
265:                    }
266:
267:                    // Add the last changeset
268:                    modifications.addAll(parseChangeset(buffer.toString(),
269:                            lastBuild));
270:
271:                    return modifications;
272:                }
273:
274:                /**
275:                 * Parse the changeset data and convert into a list of CruiseControl
276:                 * modifications.
277:                 */
278:                static ArrayList parseChangeset(String data, Date lastBuild)
279:                        throws ParseException {
280:                    if (LOG.isDebugEnabled()) {
281:                        LOG.debug("Parsing Changeset Data:\n" + data);
282:                    }
283:
284:                    ArrayList modifications = new ArrayList();
285:
286:                    Matcher m = PATTERN_CHANGESET.matcher(data);
287:                    if (m.find()) {
288:                        String revision = m.group(1);
289:                        String userName = m.group(2);
290:
291:                        Date modifiedTime = parseDate(m.group(3));
292:
293:                        // CC-735.  Ignore changesets that occured before the specified lastBuild.
294:                        if (modifiedTime.compareTo(lastBuild) < 0) {
295:                            return new ArrayList();
296:                        }
297:
298:                        // Remove the indentation from the comment
299:                        String comment = m.group(4).replaceAll("\n  ", "\n");
300:                        if (comment.length() > 0) {
301:                            // remove leading "\n"
302:                            comment = comment.trim();
303:                        }
304:
305:                        // Parse the items.
306:                        Matcher itemMatcher = PATTERN_ITEM.matcher(m.group(5));
307:                        int items = 0;
308:                        while (itemMatcher.find()) {
309:                            items++;
310:                            // Create the modification. Note that although the
311:                            // Modification class model supports more than one Modified
312:                            // file per modification most of the things downstream (such
313:                            // as the report JSP, email noticiation etc) do not take
314:                            // this into account. Therefore we flatten 1 changeset
315:                            // containing three files into three modifications
316:                            // with the same revision.
317:
318:                            Modification modification = new Modification("tfs");
319:                            modification.revision = revision;
320:                            modification.userName = userName;
321:                            modification.modifiedTime = modifiedTime;
322:                            modification.comment = comment;
323:
324:                            // In a similar way to Subversion, TFS will record additions
325:                            // of folders etc
326:                            // Therefore we have to report all modifictaion by the file
327:                            // and not split
328:                            // into file and folder as there is no easy way to
329:                            // distinguish
330:                            // $/path/filename
331:                            // from
332:                            // $/path/foldername
333:                            //
334:                            Modification.ModifiedFile modfile = modification
335:                                    .createModifiedFile(itemMatcher.group(2),
336:                                            null);
337:                            if (!modfile.fileName.startsWith("$/")) {
338:                                // If this happens then we have a bug, output some data
339:                                // to make it easy to figure out what the problem was so
340:                                // that we can fix it.
341:                                throw new ParseException(
342:                                        "Parse error. Mistakenly identified \""
343:                                                + modfile.fileName
344:                                                + "\" as an item, but it does not appear to "
345:                                                + "be a valid TFS path.  Please report this as a bug.  Changeset"
346:                                                + "data = \"\n" + data
347:                                                + "\n\".", itemMatcher.start());
348:                            }
349:                            modfile.action = itemMatcher.group(1).trim();
350:                            modfile.revision = modification.revision;
351:
352:                            modifications.add(modification);
353:                        }
354:                        if (items < 1) {
355:                            // We should always find at least one item. If we don't
356:                            // then this will be because we have not parsed correctly.
357:                            throw new ParseException(
358:                                    "Parse error. Unable to find an item within "
359:                                            + "a changeset.  Please report this as a bug.  Changeset"
360:                                            + "data = \"\n" + data + "\n\".", 0);
361:                        }
362:                    }
363:
364:                    return modifications;
365:                }
366:
367:                protected static Date parseDate(String dateString)
368:                        throws ParseException {
369:                    Date date = null;
370:                    try {
371:                        // Use the deprecated Date.parse method as this is very good at detecting
372:                        // dates commonly output by the US and UK standard locales of dotnet that
373:                        // are output by the Microsoft command line client.
374:                        date = new Date(Date.parse(dateString));
375:                    } catch (IllegalArgumentException e) {
376:                        // ignore - parse failed.
377:                    }
378:                    if (date == null) {
379:                        // The old fashioned way did not work. Let's try it using a more
380:                        // complex alternative.
381:                        DateFormat[] formats = createDateFormatsForLocaleAndTimeZone(
382:                                null, null);
383:                        return parseWithFormats(dateString, formats);
384:                    }
385:                    return date;
386:                }
387:
388:                private static Date parseWithFormats(String input,
389:                        DateFormat[] formats) throws ParseException {
390:                    ParseException parseException = null;
391:                    for (int i = 0; i < formats.length; i++) {
392:                        try {
393:                            return formats[i].parse(input);
394:                        } catch (ParseException ex) {
395:                            parseException = ex;
396:                        }
397:                    }
398:
399:                    throw parseException;
400:                }
401:
402:                /**
403:                 * Build an array of DateFormats that are commonly used for this locale
404:                 * and timezone.
405:                 */
406:                private static DateFormat[] createDateFormatsForLocaleAndTimeZone(
407:                        Locale locale, TimeZone timeZone) {
408:                    if (locale == null) {
409:                        locale = Locale.getDefault();
410:                    }
411:
412:                    if (timeZone == null) {
413:                        timeZone = TimeZone.getDefault();
414:                    }
415:
416:                    List formats = new ArrayList();
417:
418:                    for (int dateStyle = DateFormat.FULL; dateStyle <= DateFormat.SHORT; dateStyle++) {
419:                        for (int timeStyle = DateFormat.FULL; timeStyle <= DateFormat.SHORT; timeStyle++) {
420:                            DateFormat df = DateFormat.getDateTimeInstance(
421:                                    dateStyle, timeStyle, locale);
422:                            if (timeZone != null) {
423:                                df.setTimeZone(timeZone);
424:                            }
425:                            formats.add(df);
426:                        }
427:                    }
428:
429:                    for (int dateStyle = DateFormat.FULL; dateStyle <= DateFormat.SHORT; dateStyle++) {
430:                        DateFormat df = DateFormat.getDateInstance(dateStyle,
431:                                locale);
432:                        df.setTimeZone(timeZone);
433:                        formats.add(df);
434:                    }
435:
436:                    return (DateFormat[]) formats
437:                            .toArray(new DateFormat[formats.size()]);
438:                }
439:
440:            }
441:
442:            // --- Property setters
443:
444:            /**
445:             * If the username or password is not supplied, then none will be passed to
446:             * the command. On windows system using the Microsoft tf.exe command line
447:             * client, the credential of that the CruiseControl process is running as
448:             * will be used for the connection to the server.
449:             * 
450:             * @param password
451:             *            the password to set
452:             */
453:            public void setPassword(String password) {
454:                this .password = password;
455:            }
456:
457:            /**
458:             * Mandatory. The path from which you want to check for modifications.
459:             * Usually something like &quot;$/TeamProjectName/path/to/project&quot;
460:             * 
461:             * Any changes in and folder in that path or below will register as
462:             * modifications.
463:             * 
464:             * @param projectPath
465:             *            the projectPath to set
466:             */
467:            public void setProjectPath(String projectPath) {
468:                this .projectPath = projectPath;
469:            }
470:
471:            /**
472:             * The server to talk to. The easiest way to define this is in the URL
473:             * format http://servername:8080 where the URL is that to the TFS
474:             * Application Tier. On windows systems running in an environment where the
475:             * server has already been registered (using the Microsoft graphical client
476:             * for example) and the tf command being used is the Microsoft one, then the
477:             * servername only could be used as it will resolve this in the registry -
478:             * however the URL syntax is preferred as it is more accurate and easier to
479:             * change.
480:             * 
481:             * @param server
482:             *            the server to set
483:             */
484:            public void setServer(String server) {
485:                this .server = server;
486:            }
487:
488:            /**
489:             * The username to use when talking to TFS. This should be in the format
490:             * DOMAIN\name or name@DOMAIN if the domain portion is required. Note that
491:             * name@DOMAIN is the easiest format to use from Unix based systems. If the
492:             * username contains characters likely to cause problems when passed to the
493:             * command line then they can be escaped in quotes by passing the following
494:             * into the config.xml:- <code>&amp;quot;name&amp;quot;</code>
495:             * 
496:             * If the username or password is not supplied, then none will be passed to
497:             * the command. On windows system using the Microsoft tf.exe command line
498:             * client, the credential of that the CruiseControl process is running as
499:             * will be used for the connection to the server.
500:             * 
501:             * @param username
502:             *            the username to set
503:             */
504:            public void setUsername(String username) {
505:                this .username = username;
506:            }
507:
508:            /**
509:             * The path to the tf command. Either the &quot;tf.exe&quot; command
510:             * provided by Microsoft in the <a
511:             * href="http://download.microsoft.com/download/2/a/d/2ad44873-8ccb-4a1b-9c0d-23224b3ba34c/VSTFClient.img">
512:             * Team Explorer Client</a> can be used or the &quot;tf&quot; command line
513:             * client provided by <a href="http://www.teamprise.com">Teamprise</a> can
514:             * be used. The Teamprise client works cross-platform. Both clients are free
515:             * to use provided the developers using CruiseControl have a TFS Client
516:             * Access License (and in the case of Teamprise a license to the Teamprise
517:             * command line client).
518:             * 
519:             * If not supplied then the command "tf" will be called and CruiseControl
520:             * will rely on that command being able to be found in the path.
521:             * 
522:             * @param tfPath
523:             *            the path where the tf command resides
524:             */
525:            public void setTfPath(String tfPath) {
526:                this .tfPath = tfPath;
527:            }
528:
529:            /**
530:             * An optional argument to add to the end of the history command that is
531:             * generated
532:             * 
533:             * @param options
534:             *            the options to set
535:             */
536:            public void setOptions(String options) {
537:                this.options = options;
538:            }
539:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.