1 package org.apache.maven.continuum.notification.mail;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.StringWriter;
23 import java.io.UnsupportedEncodingException;
24 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import javax.mail.Message;
36 import javax.mail.MessagingException;
37 import javax.mail.internet.AddressException;
38 import javax.mail.internet.InternetAddress;
39 import javax.mail.internet.MimeMessage;
40
41 import org.apache.continuum.model.project.ProjectScmRoot;
42 import org.apache.maven.continuum.Continuum;
43 import org.apache.maven.continuum.configuration.ConfigurationService;
44 import org.apache.maven.continuum.execution.ExecutorConfigurator;
45 import org.apache.maven.continuum.execution.ant.AntBuildExecutor;
46 import org.apache.maven.continuum.execution.maven.m1.MavenOneBuildExecutor;
47 import org.apache.maven.continuum.execution.maven.m2.MavenTwoBuildExecutor;
48 import org.apache.maven.continuum.installation.InstallationException;
49 import org.apache.maven.continuum.installation.InstallationService;
50 import org.apache.maven.continuum.model.project.BuildDefinition;
51 import org.apache.maven.continuum.model.project.BuildResult;
52 import org.apache.maven.continuum.model.project.Project;
53 import org.apache.maven.continuum.model.project.ProjectDeveloper;
54 import org.apache.maven.continuum.model.project.ProjectGroup;
55 import org.apache.maven.continuum.model.project.ProjectNotifier;
56 import org.apache.maven.continuum.model.scm.ChangeSet;
57 import org.apache.maven.continuum.model.scm.ScmResult;
58 import org.apache.maven.continuum.model.system.Installation;
59 import org.apache.maven.continuum.model.system.Profile;
60 import org.apache.maven.continuum.notification.AbstractContinuumNotifier;
61 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
62 import org.apache.maven.continuum.notification.MessageContext;
63 import org.apache.maven.continuum.notification.NotificationException;
64 import org.apache.maven.continuum.project.ContinuumProjectState;
65 import org.apache.maven.continuum.reports.surefire.ReportTestResult;
66 import org.apache.maven.continuum.reports.surefire.ReportTestSuiteGenerator;
67 import org.apache.velocity.VelocityContext;
68 import org.apache.velocity.exception.ResourceNotFoundException;
69 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
70 import org.codehaus.plexus.util.StringUtils;
71 import org.codehaus.plexus.velocity.VelocityComponent;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74 import org.springframework.mail.javamail.JavaMailSender;
75
76
77
78
79
80 public class MailContinuumNotifier
81 extends AbstractContinuumNotifier
82 implements Initializable
83 {
84 private static final Logger log = LoggerFactory.getLogger( MailContinuumNotifier.class );
85
86
87
88
89
90
91
92
93 private VelocityComponent velocity;
94
95
96
97
98 private ConfigurationService configurationService;
99
100
101
102
103 private Continuum continuum;
104
105
106
107
108 private JavaMailSender javaMailSender;
109
110
111
112
113 private ReportTestSuiteGenerator reportTestSuiteGenerator;
114
115
116
117
118
119
120
121 private String fromMailbox;
122
123
124
125
126 private String fromName;
127
128
129
130
131 private String toOverride;
132
133
134
135
136 private String timestampFormat;
137
138
139
140
141 private boolean includeBuildSummary = true;
142
143
144
145
146 private boolean includeTestSummary = true;
147
148
149
150
151 private boolean includeBuildOutput = false;
152
153
154
155
156
157
158
159
160
161
162
163 private String buildSubjectFormat = "[continuum] BUILD ${state}: ${project.groupId} ${project.name}";
164
165
166
167
168
169
170 private String prepareBuildSubjectFormat = "[continuum] PREPARE BUILD ${state]: ${projectScmRoot.projectGroup.name}";
171
172
173
174
175
176 private String buildHost;
177
178 private FormatterTool formatterTool;
179
180
181
182
183
184 private static final String FALLBACK_FROM_MAILBOX = "continuum@localhost";
185
186
187
188
189
190 public void initialize()
191 {
192 try
193 {
194 InetAddress address = InetAddress.getLocalHost();
195
196 buildHost = StringUtils.clean( address.getHostName() );
197
198 if ( buildHost == null )
199 {
200 buildHost = "localhost";
201 }
202 }
203 catch ( UnknownHostException ex )
204 {
205 fromName = "Continuum";
206 }
207
208
209
210
211
212 if ( StringUtils.isEmpty( fromMailbox ) )
213 {
214 log.info( "The from mailbox is not configured, will use the nag email address from the project." );
215
216 fromMailbox = null;
217 }
218 else
219 {
220 log.info( "Using '" + fromMailbox + "' as the from mailbox for all emails." );
221 }
222
223 if ( StringUtils.isEmpty( fromName ) )
224 {
225 fromName = "Continuum@" + buildHost;
226 }
227
228 log.info( "From name: " + fromName );
229
230 log.info( "Build host name: " + buildHost );
231
232
233
234
235
236 formatterTool = new FormatterTool( timestampFormat );
237 }
238
239
240
241
242
243 public String getType()
244 {
245 return "mail";
246 }
247
248 public void sendMessage( String messageId, MessageContext context )
249 throws NotificationException
250 {
251 Project project = context.getProject();
252 List<ProjectNotifier> notifiers = context.getNotifiers();
253 BuildResult build = context.getBuildResult();
254 BuildDefinition buildDefinition = context.getBuildDefinition();
255 ProjectScmRoot projectScmRoot = context.getProjectScmRoot();
256
257 boolean isPrepareBuildComplete =
258 messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_PREPARE_BUILD_COMPLETE );
259
260 if ( projectScmRoot == null && isPrepareBuildComplete )
261 {
262 return;
263 }
264
265
266
267
268
269 if ( build == null && !isPrepareBuildComplete )
270 {
271 return;
272 }
273
274
275
276
277
278 if ( messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_BUILD_COMPLETE ) )
279 {
280 buildComplete( project, notifiers, build, messageId, context, buildDefinition );
281 }
282 else if ( isPrepareBuildComplete )
283 {
284 prepareBuildComplete( projectScmRoot, notifiers, messageId, context );
285 }
286 }
287
288 private void buildComplete( Project project, List<ProjectNotifier> notifiers, BuildResult build, String messageId,
289 MessageContext context, BuildDefinition buildDefinition )
290 throws NotificationException
291 {
292 BuildResult previousBuild = getPreviousBuild( project, buildDefinition, build );
293
294 List<ProjectNotifier> notifiersList = new ArrayList<ProjectNotifier>();
295 for ( ProjectNotifier notifier : notifiers )
296 {
297
298
299
300
301 if ( shouldNotify( build, previousBuild, notifier ) )
302 {
303 notifiersList.add( notifier );
304 }
305 }
306 buildComplete( project, notifiersList, build, previousBuild, messageId, context, buildDefinition );
307 }
308
309 private void buildComplete( Project project, List<ProjectNotifier> notifiers, BuildResult build,
310 BuildResult previousBuild, String messageId, MessageContext messageContext,
311 BuildDefinition buildDefinition )
312 throws NotificationException
313 {
314
315
316
317
318 String packageName = getClass().getPackage().getName().replace( '.', '/' );
319
320 String templateName = packageName + "/templates/" + project.getExecutorId() + "/" + messageId + ".vm";
321
322 StringWriter writer = new StringWriter();
323
324 String content;
325
326 try
327 {
328 VelocityContext context = new VelocityContext();
329
330 context.put( "includeTestSummary", includeTestSummary );
331
332 context.put( "includeOutput", includeBuildOutput );
333
334 if ( includeBuildOutput )
335 {
336 context.put( "buildOutput", getBuildOutput( project, build ) );
337 }
338
339 if ( includeBuildSummary )
340 {
341 context.put( "build", build );
342
343 ReportTestResult reportTestResult =
344 reportTestSuiteGenerator.generateReportTestResult( build.getId(), project.getId() );
345
346 context.put( "testResult", reportTestResult );
347
348 context.put( "project", project );
349
350 context.put( "changesSinceLastSuccess",
351 continuum.getChangesSinceLastSuccess( project.getId(), build.getId() ) );
352
353 context.put( "previousBuild", previousBuild );
354
355
356
357
358
359 context.put( "formatter", formatterTool );
360
361
362
363 context.put( "buildHost", buildHost );
364
365 String osName = System.getProperty( "os.name" );
366
367 String osPatchLevel = System.getProperty( "sun.os.patch.level" );
368
369 if ( osPatchLevel != null )
370 {
371 osName = osName + "(" + osPatchLevel + ")";
372 }
373
374 context.put( "osName", osName );
375
376 context.put( "javaVersion",
377 System.getProperty( "java.version" ) + "(" + System.getProperty( "java.vendor" ) + ")" );
378
379
380 context.put( "javaHomeInformations", getJavaHomeInformations( buildDefinition ) );
381
382 context.put( "builderVersions", getBuilderVersion( buildDefinition, project ) );
383 }
384
385
386
387
388
389 context.put( "reportUrl", getReportUrl( project, build, configurationService ) );
390
391
392
393
394
395
396
397 velocity.getEngine().mergeTemplate( templateName, context, writer );
398
399 content = writer.getBuffer().toString();
400 }
401 catch ( ResourceNotFoundException e )
402 {
403 log.info( "No such template: '" + templateName + "'." );
404
405 return;
406 }
407 catch ( Exception e )
408 {
409 throw new NotificationException( "Error while generating mail contents.", e );
410 }
411
412
413
414
415
416 String subject;
417 try
418 {
419 subject = generateSubject( project, build );
420 }
421 catch ( Exception e )
422 {
423 throw new NotificationException( "Error while generating mail subject.", e );
424 }
425
426 sendMessage( project, notifiers, subject, content, messageContext );
427 }
428
429 private void prepareBuildComplete( ProjectScmRoot projectScmRoot, List<ProjectNotifier> notifiers, String messageId,
430 MessageContext messageContext )
431 throws NotificationException
432 {
433
434
435
436
437 String packageName = getClass().getPackage().getName().replace( '.', '/' );
438
439 String templateName = packageName + "/templates/" + messageId + ".vm";
440
441 StringWriter writer = new StringWriter();
442
443 String content;
444
445 try
446 {
447 VelocityContext context = new VelocityContext();
448
449
450
451
452
453 context.put( "reportUrl",
454 getReportUrl( projectScmRoot.getProjectGroup(), projectScmRoot, configurationService ) );
455
456 context.put( "projectScmRoot", projectScmRoot );
457
458
459
460
461
462
463
464 velocity.getEngine().mergeTemplate( templateName, context, writer );
465
466 content = writer.getBuffer().toString();
467 }
468 catch ( ResourceNotFoundException e )
469 {
470 log.info( "No such template: '" + templateName + "'." );
471
472 return;
473 }
474 catch ( Exception e )
475 {
476 throw new NotificationException( "Error while generating mail contents.", e );
477 }
478
479
480
481
482
483 String subject;
484 try
485 {
486 subject = generateSubject( projectScmRoot );
487 }
488 catch ( Exception e )
489 {
490 throw new NotificationException( "Error while generating mail subject.", e );
491 }
492
493 sendMessage( projectScmRoot, notifiers, subject, content, messageContext );
494 }
495
496
497
498
499
500 private List<String> getJavaHomeInformations( BuildDefinition buildDefinition )
501 throws InstallationException
502 {
503 if ( buildDefinition == null )
504 {
505 return continuum.getInstallationService().getDefaultJdkInformations();
506 }
507 Profile profile = buildDefinition.getProfile();
508 if ( profile == null )
509 {
510 return continuum.getInstallationService().getDefaultJdkInformations();
511 }
512 return continuum.getInstallationService().getJdkInformations( profile.getJdk() );
513 }
514
515 private List<String> getBuilderVersion( BuildDefinition buildDefinition, Project project )
516 throws InstallationException
517 {
518 ExecutorConfigurator executorConfigurator;
519 Installation builder = null;
520 Profile profile = null;
521 if ( buildDefinition != null )
522 {
523 profile = buildDefinition.getProfile();
524 if ( profile != null )
525 {
526 builder = profile.getBuilder();
527 }
528 }
529 if ( builder != null )
530 {
531 executorConfigurator = continuum.getInstallationService().getExecutorConfigurator( builder.getType() );
532 }
533 else
534 {
535
536 if ( MavenTwoBuildExecutor.ID.equals( project.getExecutorId() ) )
537 {
538 executorConfigurator =
539 continuum.getInstallationService().getExecutorConfigurator( InstallationService.MAVEN2_TYPE );
540 }
541 else if ( MavenOneBuildExecutor.ID.equals( project.getExecutorId() ) )
542 {
543 executorConfigurator =
544 continuum.getInstallationService().getExecutorConfigurator( InstallationService.MAVEN1_TYPE );
545 }
546 else if ( AntBuildExecutor.ID.equals( project.getExecutorId() ) )
547 {
548 executorConfigurator =
549 continuum.getInstallationService().getExecutorConfigurator( InstallationService.ANT_TYPE );
550 }
551 else
552 {
553 return Arrays.asList( "No builder defined" );
554 }
555 }
556
557 return continuum.getInstallationService().getExecutorConfiguratorVersion(
558 builder == null ? null : builder.getVarValue(), executorConfigurator, profile );
559 }
560
561 private String generateSubject( Project project, BuildResult build )
562 throws Exception
563 {
564 String state = getState( project, build );
565
566 VelocityContext context = new VelocityContext();
567 context.put( "project", project );
568 context.put( "build", build );
569 context.put( "state", state );
570
571 StringWriter writer = new StringWriter();
572
573 boolean velocityRes = velocity.getEngine().evaluate( context, writer, "subjectPattern", buildSubjectFormat );
574
575 return writer.toString();
576 }
577
578 private String generateSubject( ProjectScmRoot projectScmRoot )
579 throws Exception
580 {
581 String state = getState( projectScmRoot );
582
583 VelocityContext context = new VelocityContext();
584 context.put( "projectScmRoot", projectScmRoot );
585 context.put( "state", state );
586
587 StringWriter writer = new StringWriter();
588
589 boolean velocityResults =
590 velocity.getEngine().evaluate( context, writer, "subjectPattern", prepareBuildSubjectFormat );
591
592 return writer.toString();
593 }
594
595 private String getState( Project project, BuildResult build )
596 {
597 int state = project.getState();
598
599 if ( build != null )
600 {
601 state = build.getState();
602 }
603
604 if ( state == ContinuumProjectState.OK )
605 {
606 return "SUCCESSFUL";
607 }
608 else if ( state == ContinuumProjectState.FAILED )
609 {
610 return "FAILURE";
611 }
612 else if ( state == ContinuumProjectState.ERROR )
613 {
614 return "ERROR";
615 }
616 else
617 {
618 log.warn( "Unknown build state " + state + " for project " + project.getId() );
619
620 return "ERROR: Unknown build state " + state;
621 }
622 }
623
624 private String getState( ProjectScmRoot projectScmRoot )
625 {
626 int state = projectScmRoot.getState();
627
628 if ( state == ContinuumProjectState.UPDATED )
629 {
630 return "SUCCESSFUL";
631 }
632 else if ( state == ContinuumProjectState.ERROR )
633 {
634 return "ERROR";
635 }
636 else
637 {
638 log.warn(
639 "Unknown prepare build state " + state + " for SCM Root URL " + projectScmRoot.getScmRootAddress() +
640 " in projectGroup " + projectScmRoot.getProjectGroup().getId() );
641
642 return "ERROR: Unknown build state " + state;
643 }
644 }
645
646 private void sendMessage( Project project, List<ProjectNotifier> notifiers, String subject, String content,
647 MessageContext context )
648 throws NotificationException
649 {
650 if ( notifiers.size() == 0 )
651 {
652
653
654
655 log.info( "No mail notifier for '" + project.getName() + "'." );
656
657 return;
658 }
659
660 String fromMailbox = getFromMailbox( notifiers );
661
662 if ( fromMailbox == null )
663 {
664 log.warn( project.getName() +
665 ": Project is missing nag email and global from mailbox is missing, not sending mail." );
666
667 return;
668 }
669
670 try
671 {
672
673 MimeMessage message = javaMailSender.createMimeMessage();
674
675 message.addHeader( "X-Continuum-Build-Host", buildHost );
676
677 message.addHeader( "X-Continuum-Project-Id", Integer.toString( project.getId() ) );
678
679 message.addHeader( "X-Continuum-Project-Name", project.getName() );
680
681 message.setSubject( subject );
682
683 log.info( "Message Subject: '" + subject + "'." );
684
685 message.setText( content );
686
687 InternetAddress from = new InternetAddress( fromMailbox, fromName );
688
689 message.setFrom( from );
690
691 log.info( "Sending message: From '" + from + "'." );
692
693 if ( StringUtils.isEmpty( toOverride ) )
694 {
695 Set<String> listRecipents = new HashSet<String>();
696 for ( ProjectNotifier notifier : notifiers )
697 {
698 Map<String, String> conf = notifier.getConfiguration();
699 if ( conf != null )
700 {
701 String addressField = conf.get( ADDRESS_FIELD );
702
703 if ( StringUtils.isNotEmpty( addressField ) )
704 {
705 String[] addresses = StringUtils.split( addressField, "," );
706 for ( String address : addresses )
707 {
708 if (!listRecipents.contains(address.trim())) {
709
710
711 InternetAddress to = new InternetAddress(address.trim());
712
713 log.info("Recipient: To '" + to + "'.");
714 message.addRecipient(Message.RecipientType.TO, to);
715 listRecipents.add(address.trim());
716 }
717 }
718
719 }
720
721 String committerField = (String) notifier.getConfiguration().get( COMMITTER_FIELD );
722 if ( StringUtils.isNotEmpty( committerField ) && context.getBuildResult() != null )
723 {
724 if ( Boolean.parseBoolean( committerField ) )
725 {
726 ScmResult scmResult = context.getBuildResult().getScmResult();
727 if ( scmResult != null && scmResult.getChanges() != null &&
728 !scmResult.getChanges().isEmpty() )
729 {
730 List<ProjectDeveloper> developers = project.getDevelopers();
731 if ( developers == null || developers.isEmpty() )
732 {
733 log.warn( "No developers have been configured...notifcation email " +
734 "will not be sent" );
735 return;
736 }
737
738 Map<String, String> developerToEmailMap = mapDevelopersToRecipients( developers );
739
740 List<ChangeSet> changes = scmResult.getChanges();
741
742 for ( ChangeSet changeSet : changes )
743 {
744 String scmId = changeSet.getAuthor();
745 if (StringUtils.isNotEmpty(scmId))
746 {
747 String email = developerToEmailMap.get( scmId );
748 if ( StringUtils.isEmpty( email ) )
749 {
750
751 log.warn(
752 "no email address is defined in developers list for '" + scmId +
753 "' scm id." );
754 }
755 else if (!listRecipents.contains(email.trim()))
756 {
757
758
759 InternetAddress to = new InternetAddress( email.trim() );
760 log.info( "Recipient: To '" + to + "'." );
761
762 message.addRecipient( Message.RecipientType.TO, to );
763 listRecipents.add(email.trim());
764 }
765 }
766 }
767 }
768 }
769 }
770 }
771 }
772 }
773 else
774 {
775
776
777 InternetAddress to = new InternetAddress( toOverride.trim() );
778 log.info( "Recipient: To '" + to + "'." );
779
780 message.addRecipient( Message.RecipientType.TO, to );
781 }
782
783 message.setSentDate( new Date() );
784
785 if ( message.getAllRecipients() != null && ( message.getAllRecipients() ).length > 0 )
786 {
787 javaMailSender.send( message );
788 }
789 }
790 catch ( AddressException ex )
791 {
792 throw new NotificationException( "Exception while sending message.", ex );
793 }
794 catch ( MessagingException ex )
795 {
796 throw new NotificationException( "Exception while sending message.", ex );
797 }
798 catch ( UnsupportedEncodingException ex )
799 {
800 throw new NotificationException( "Exception while sending message.", ex );
801 }
802 }
803
804 private void sendMessage( ProjectScmRoot projectScmRoot, List<ProjectNotifier> notifiers, String subject,
805 String content, MessageContext context )
806 throws NotificationException
807 {
808 ProjectGroup projectGroup = projectScmRoot.getProjectGroup();
809
810 if ( notifiers.size() == 0 )
811 {
812
813
814
815 log.info( "No mail notifier for '" + projectGroup.getName() + "'." );
816
817 return;
818 }
819
820 String fromMailbox = getFromMailbox( notifiers );
821
822 if ( fromMailbox == null )
823 {
824 log.warn( projectGroup.getName() +
825 ": ProjectGroup is missing nag email and global from mailbox is missing, not sending mail." );
826
827 return;
828 }
829
830 MimeMessage message = javaMailSender.createMimeMessage();
831
832 try
833 {
834 message.setSubject( subject );
835
836 log.info( "Message Subject: '" + subject + "'." );
837
838 message.setText( content );
839
840 InternetAddress from = new InternetAddress( fromMailbox, fromName );
841
842 message.setFrom( from );
843
844 log.info( "Sending message: From '" + from + "'." );
845
846 if ( StringUtils.isEmpty( toOverride ) )
847 {
848 for ( ProjectNotifier notifier : notifiers )
849 {
850 if ( !shouldNotify( projectScmRoot, notifier ) )
851 {
852 continue;
853 }
854
855 Map<String, String> conf = notifier.getConfiguration();
856 if ( conf != null )
857 {
858 String addressField = conf.get( ADDRESS_FIELD );
859
860 if ( StringUtils.isNotEmpty( addressField ) )
861 {
862 String[] addresses = StringUtils.split( addressField, "," );
863
864 for ( String address : addresses )
865 {
866
867 InternetAddress to = new InternetAddress( address.trim() );
868
869 log.info( "Recipient: To '" + to + "'." );
870 message.addRecipient( Message.RecipientType.TO, to );
871 }
872 }
873 }
874 }
875 }
876 else
877 {
878
879
880 InternetAddress to = new InternetAddress( toOverride.trim() );
881 log.info( "Recipient: To '" + to + "'." );
882
883 message.addRecipient( Message.RecipientType.TO, to );
884 }
885
886 message.setSentDate( new Date() );
887
888 if ( message.getAllRecipients() != null && ( message.getAllRecipients() ).length > 0 )
889 {
890 javaMailSender.send( message );
891 }
892 }
893 catch ( AddressException ex )
894 {
895 throw new NotificationException( "Exception while sending message.", ex );
896 }
897 catch ( MessagingException ex )
898 {
899 throw new NotificationException( "Exception while sending message.", ex );
900 }
901 catch ( UnsupportedEncodingException ex )
902 {
903 throw new NotificationException( "Exception while sending message.", ex );
904 }
905 }
906
907 private Map<String, String> mapDevelopersToRecipients( List<ProjectDeveloper> developers )
908 {
909 Map<String, String> developersMap = new HashMap<String, String>();
910
911 for ( ProjectDeveloper developer : developers )
912 {
913 if ( StringUtils.isNotEmpty( developer.getScmId() ) && StringUtils.isNotEmpty( developer.getEmail() ) )
914 {
915 developersMap.put( developer.getScmId(), developer.getEmail() );
916 }
917 }
918
919 return developersMap;
920 }
921
922 private String getFromMailbox( List<ProjectNotifier> notifiers )
923 {
924 if ( fromMailbox != null )
925 {
926 return fromMailbox;
927 }
928
929 String address = null;
930
931 for ( ProjectNotifier notifier : notifiers )
932 {
933 Map<String, String> configuration = notifier.getConfiguration();
934 if ( configuration != null && StringUtils.isNotEmpty( configuration.get( ADDRESS_FIELD ) ) )
935 {
936 address = configuration.get( ADDRESS_FIELD );
937 break;
938 }
939 }
940
941 if ( StringUtils.isEmpty( address ) )
942 {
943 return FALLBACK_FROM_MAILBOX;
944 }
945
946 if ( address != null && address.contains( "," ) )
947 {
948 String[] addresses = StringUtils.split( address, "," );
949 return addresses[0];
950 }
951 return address;
952 }
953
954 public String getBuildHost()
955 {
956 return buildHost;
957 }
958
959 public void setBuildHost( String buildHost )
960 {
961 this.buildHost = buildHost;
962 }
963
964 public String getToOverride()
965 {
966 return toOverride;
967 }
968
969 public void setToOverride( String toOverride )
970 {
971 this.toOverride = toOverride;
972 }
973 }