1 37 package net.sourceforge.cruisecontrol.publishers; 38 39 import java.io.File ; 40 import java.io.UnsupportedEncodingException ; 41 import java.util.ArrayList ; 42 import java.util.Arrays ; 43 import java.util.Collections ; 44 import java.util.Date ; 45 import java.util.HashSet ; 46 import java.util.Iterator ; 47 import java.util.List ; 48 import java.util.Properties ; 49 import java.util.Set ; 50 import java.util.TreeSet ; 51 52 import javax.mail.Message ; 53 import javax.mail.MessagingException ; 54 import javax.mail.SendFailedException ; 55 import javax.mail.Session ; 56 import javax.mail.Transport ; 57 import javax.mail.internet.AddressException ; 58 import javax.mail.internet.InternetAddress ; 59 import javax.mail.internet.MimeMessage ; 60 61 import net.sourceforge.cruisecontrol.CruiseControlException; 62 import net.sourceforge.cruisecontrol.Modification; 63 import net.sourceforge.cruisecontrol.Publisher; 64 import net.sourceforge.cruisecontrol.publishers.email.EmailMapper; 65 import net.sourceforge.cruisecontrol.publishers.email.EmailMapperHelper; 66 import net.sourceforge.cruisecontrol.publishers.email.EmailMapping; 67 import net.sourceforge.cruisecontrol.util.ValidationHelper; 68 import net.sourceforge.cruisecontrol.util.XMLLogHelper; 69 70 import org.apache.log4j.Logger; 71 import org.apache.oro.io.GlobFilenameFilter; 72 import org.apache.oro.text.MalformedCachePatternException; 73 import org.jdom.Element; 74 75 86 public abstract class EmailPublisher implements Publisher { 87 private static final Logger LOG = Logger.getLogger(EmailPublisher.class); 88 89 private String mailHost; 90 private String userName; 91 private String password; 92 private String mailPort; 93 private boolean useSSL; 94 private String buildResultsURL; 95 private Always[] alwaysAddresses = new Always[0]; 96 private Failure[] failureAddresses = new Failure[0]; 97 private Success[] successAddresses = new Success[0]; 98 private Alert[] alertAddresses = new Alert[0]; 99 private EmailMapper[] emailMapper = new EmailMapper[0]; 100 private EmailMapperHelper mapperHelper = new EmailMapperHelper(); 101 private String returnAddress; 102 private String returnName; 103 private String defaultSuffix = ""; 104 private String reportSuccess = "always"; 105 private boolean spamWhileBroken = true; 106 private boolean skipUsers = false; 107 private String subjectPrefix; 108 private boolean failAsImportant = true; 109 110 116 protected abstract String createMessage(XMLLogHelper logHelper); 117 118 124 public void validate() throws CruiseControlException { 125 ValidationHelper.assertIsSet(getMailHost(), "mailhost", this.getClass()); 126 ValidationHelper.assertIsSet(getReturnAddress(), "returnaddress", this.getClass()); 127 ValidationHelper.assertFalse(getUsername() != null && getPassword() == null, 128 "'password' is required if 'username' is set for email."); 129 ValidationHelper.assertFalse(getPassword() != null && getUsername() == null, 130 "'username' is required if 'password' is set for email."); 131 132 for (int i = 0; i < alertAddresses.length; i++) { 133 try { 134 alertAddresses[i].fileFilter = new GlobFilenameFilter(alertAddresses[i].fileRegExpr); 135 } catch (MalformedCachePatternException mcpe) { 136 ValidationHelper.fail("invalid regexp '" + alertAddresses[i].fileRegExpr + "'", mcpe); 137 } 138 } 139 } 140 141 147 protected String createSubject(XMLLogHelper logHelper) throws CruiseControlException { 148 StringBuffer subjectLine = new StringBuffer (); 149 if (subjectPrefix != null) { 150 subjectLine.append(subjectPrefix).append(" "); 151 } 152 subjectLine.append(logHelper.getProjectName()); 153 if (logHelper.isBuildSuccessful()) { 154 String label = logHelper.getLabel(); 155 if (label.trim().length() > 0) { 156 subjectLine.append(" ").append(logHelper.getLabel()); 157 } 158 159 if (logHelper.isBuildFix()) { 163 subjectLine.append(" Build Fixed"); 164 } else { 165 subjectLine.append(" Build Successful"); 166 } 167 } else { 168 subjectLine.append(" Build Failed"); 169 } 170 return subjectLine.toString(); 171 } 172 173 179 protected boolean shouldSend(XMLLogHelper logHelper) throws CruiseControlException { 180 if (logHelper.isBuildSuccessful()) { 181 if (reportSuccess.equalsIgnoreCase("always")) { 182 return true; 183 } 184 if (reportSuccess.equalsIgnoreCase("fixes")) { 185 if (logHelper.wasPreviousBuildSuccessful()) { 186 LOG.debug( 187 "reportSuccess is set to 'fixes', not sending emails for repeated successful builds."); 188 return false; 189 } else { 190 return true; 191 } 192 } 193 if (reportSuccess.equalsIgnoreCase("never")) { 194 LOG.debug( 195 "reportSuccess is set to 'never', not sending emails for successful builds."); 196 return false; 197 } 198 } else { if (!logHelper.wasPreviousBuildSuccessful() 200 && logHelper.isBuildNecessary() 201 && !spamWhileBroken) { 202 203 LOG.debug("spamWhileBroken is set to false, not sending email"); 204 return false; 205 } 206 } 207 return true; 208 } 209 210 226 protected String createUserList(XMLLogHelper logHelper) throws CruiseControlException { 227 228 Set emails = createUserSet(logHelper); 229 return createEmailString(emails); 230 } 231 232 233 234 252 protected Set createUserSet(XMLLogHelper logHelper) throws CruiseControlException { 253 254 Set users = skipUsers ? new HashSet () : logHelper.getBuildParticipants(); 255 256 for (int i = 0; i < alwaysAddresses.length; i++) { 258 users.add(alwaysAddresses[i].getAddress()); 259 } 260 261 if (!logHelper.isBuildSuccessful()) { 263 for (int i = 0; i < failureAddresses.length; i++) { 264 users.add(failureAddresses[i].getAddress()); 265 } 266 } 267 268 if (logHelper.isBuildFix()) { 270 for (int i = 0; i < failureAddresses.length; i++) { 271 if (failureAddresses[i].shouldReportWhenFixed()) { 272 users.add(failureAddresses[i].getAddress()); 273 } 274 } 275 } 276 277 if (logHelper.isBuildSuccessful()) { 279 for (int i = 0; i < successAddresses.length; i++) { 280 users.add(successAddresses[i].getAddress()); 281 } 282 } 283 284 Set emails = new TreeSet (); 285 mapperHelper.mapUsers(this, users, emails); 286 return emails; 287 } 288 289 290 298 public void publish(Element cruisecontrolLog) throws CruiseControlException { 299 XMLLogHelper helper = new XMLLogHelper(cruisecontrolLog); 300 boolean important = failAsImportant && !helper.isBuildSuccessful(); 301 302 Set userSet = new HashSet (); 303 Set alertSet = createAlertUserSet(helper); 304 String subject = createSubject(helper); 305 306 if (!alertSet.isEmpty()) { 307 String alertSubject = "[MOD ALERT] " + subject; 308 sendMail(createEmailString(alertSet), alertSubject, createMessage(helper), important); 309 } 310 311 if (shouldSend(helper)) { 312 userSet.addAll(createUserSet(helper)); 313 userSet.removeAll(alertSet); 315 316 if (!userSet.isEmpty()) { 317 sendMail(createEmailString(userSet), subject, createMessage(helper), important); 318 } else { 319 if (alertSet.isEmpty()) { 320 LOG.info("No recipients, so not sending email"); 321 } 322 } 323 } 324 } 325 326 330 protected Properties getMailProperties() { 331 Properties props = System.getProperties(); 332 props.put("mail.smtp.host", mailHost); 333 props.put("mail.smtp.sendpartial", "true"); 334 if (mailPort != null) { 335 props.put("mail.smtp.port", mailPort); 336 } 337 LOG.debug( 338 "mailHost is " + mailHost + ", mailPort is " + mailPort == null ? "default" : mailPort); 339 if (userName != null && password != null) { 340 props.put("mail.smtp.auth", "true"); 341 if (useSSL) { 342 props.put("mail.smtp.socketFactory.port", mailPort); 343 props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); 344 props.put("mail.smtp.socketFactory.fallback", "false"); 345 } 346 } 347 return props; 348 } 349 350 358 protected boolean sendMail(String toList, String subject, String message, boolean important) 359 throws CruiseControlException { 360 361 boolean emailSent = false; 362 363 if (toList != null && toList.trim().length() != 0) { 364 365 LOG.debug("Sending email to: " + toList); 366 367 Session session = Session.getDefaultInstance(getMailProperties(), null); 368 session.setDebug(LOG.isDebugEnabled()); 369 370 try { 371 Message msg = new MimeMessage (session); 372 msg.setFrom(getFromAddress()); 373 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toList, false)); 374 msg.setSubject(subject); 375 msg.setSentDate(new Date ()); 376 String importance = (important) ? "High" : "Normal"; 377 msg.addHeader("Importance", importance); 378 379 addContentToMessage(message, msg); 380 381 if (userName != null && password != null) { 382 msg.saveChanges(); Transport transport = session.getTransport("smtp"); 384 transport.connect(mailHost, userName, password); 385 transport.sendMessage(msg, msg.getAllRecipients()); 386 transport.close(); 387 } else { 388 Transport.send(msg); 389 } 390 391 emailSent = true; 392 393 } catch (SendFailedException e) { 394 LOG.warn(e.getMessage(), e); 395 } catch (MessagingException e) { 396 throw new CruiseControlException(e.getClass().getName() + ": " + e.getMessage(), e); 397 } 398 } 399 400 return emailSent; 401 } 402 403 411 protected void addContentToMessage(String content, Message msg) throws MessagingException { 412 msg.setText(content); 413 } 414 415 protected InternetAddress getFromAddress() throws AddressException { 416 InternetAddress fromAddress = new InternetAddress (returnAddress); 417 if (returnName != null) { 418 try { 419 fromAddress = new InternetAddress (returnAddress, returnName); 420 } catch (UnsupportedEncodingException e) { 421 LOG.error("error setting returnName [" + returnName + "]: " + e.getMessage()); 422 } 423 } 424 return fromAddress; 425 } 426 427 public void setMailHost(String hostname) { 428 mailHost = hostname; 429 } 430 431 public String getMailHost() { 432 return mailHost; 433 } 434 435 public void setUsername(String name) { 436 userName = name; 437 } 438 439 public String getUsername() { 440 return userName; 441 } 442 443 public void setPassword(String passwd) { 444 password = passwd; 445 } 446 447 public String getPassword() { 448 return password; 449 } 450 451 public void setMailPort(String port) { 452 mailPort = port; 453 } 454 455 public String getMailPort() { 456 return mailPort; 457 } 458 459 public void setUseSSL(boolean useSSL) { 460 this.useSSL = useSSL; 461 } 462 463 public void setSubjectPrefix(String prefix) { 464 subjectPrefix = prefix; 465 } 466 467 public String getSubjectPrefix() { 468 return subjectPrefix; 469 } 470 471 public String getBuildResultsURL() { 472 return buildResultsURL; 473 } 474 475 public void setBuildResultsURL(String url) { 476 buildResultsURL = url; 477 } 478 479 public EmailMapper[] getEmailMapper() { 480 return emailMapper; 481 } 482 483 public String getReturnAddress() { 484 return returnAddress; 485 } 486 487 public void setReturnAddress(String emailAddress) { 488 returnAddress = emailAddress; 489 } 490 491 public String getReturnName() { 492 return returnName; 493 } 494 495 public void setReturnName(String emailReturnName) { 496 returnName = emailReturnName; 497 } 498 499 public String getDefaultSuffix() { 500 return defaultSuffix; 501 } 502 503 public void setDefaultSuffix(String defaultEmailSuffix) { 504 defaultSuffix = defaultEmailSuffix; 505 } 506 507 public void setReportSuccess(String report) { 508 reportSuccess = report; 509 } 510 511 public void setSkipUsers(boolean skip) { 512 skipUsers = skip; 513 } 514 515 public void setSpamWhileBroken(boolean spam) { 516 spamWhileBroken = spam; 517 } 518 519 public void setFailAsImportant(boolean important) { 520 failAsImportant = important; 521 } 522 523 public Always createAlways() { 524 List alwaysList = new ArrayList (); 525 alwaysList.addAll(Arrays.asList(alwaysAddresses)); 526 527 Always always = new Always(); 528 alwaysList.add(always); 529 530 alwaysAddresses = (Always[]) alwaysList.toArray(new Always[0]); 531 532 return always; 533 } 534 535 public Failure createFailure() { 536 List failureList = new ArrayList (); 537 failureList.addAll(Arrays.asList(failureAddresses)); 538 539 Failure failure = new Failure(); 540 failureList.add(failure); 541 542 failureAddresses = (Failure[]) failureList.toArray(new Failure[0]); 543 544 return failure; 545 } 546 547 public Success createSuccess() { 548 List successList = new ArrayList (); 549 successList.addAll(Arrays.asList(successAddresses)); 550 551 Success success = new Success(); 552 successList.add(success); 553 554 successAddresses = (Success[]) successList.toArray(new Success[0]); 555 556 return success; 557 } 558 559 public Alert createAlert() { 560 List alertsList = new ArrayList (); 561 alertsList.addAll(Arrays.asList(alertAddresses)); 562 563 Alert alert = new Alert(); 564 alertsList.add(alert); 565 566 alertAddresses = (Alert[]) alertsList.toArray(new Alert[0]); 567 568 return alert; 569 } 570 571 574 public void add(EmailMapping mapping) { 575 EmailMapperHelper.addCacheEntry(this, mapping.getAlias(), mapping.getAddress()); 576 } 577 578 public void add(EmailMapper mapper) { 579 List mapperList = new ArrayList (); 580 mapperList.addAll(Arrays.asList(emailMapper)); 581 582 mapper.setPublisher(this); 583 mapperList.add(mapper); 584 585 emailMapper = (EmailMapper[]) mapperList.toArray(new EmailMapper[0]); 586 } 587 588 public static class Address { 589 private String address; 590 591 public String getAddress() { 592 return address; 593 } 594 595 public void setAddress(String theAddress) { 596 address = theAddress; 597 } 598 } 599 600 public static class Always extends Address { 601 } 602 603 public static class Failure extends Address { 604 608 private boolean reportWhenFixed = false; 609 610 public boolean shouldReportWhenFixed() { 611 return reportWhenFixed; 612 } 613 614 public void setReportWhenFixed(boolean reportWhenFixed) { 615 this.reportWhenFixed = reportWhenFixed; 616 } 617 } 618 619 public static class Success extends Address { 620 } 621 622 623 public static class Alert extends Address { 624 628 private String fileRegExpr = null; 629 630 633 private GlobFilenameFilter fileFilter = null; 634 635 639 public void setFileRegExpr(String f) { 640 this.fileRegExpr = f; 641 } 642 } 643 644 public static void main(String [] args) { 645 EmailPublisher pub = new EmailPublisher() { 646 protected String createMessage(XMLLogHelper logHelper) { 647 return "This is a test message."; 648 } 649 }; 650 pub.setMailHost(args[0]); 651 pub.setUsername(args[1]); 652 pub.setPassword(args[2]); 653 pub.setReturnAddress(args[3]); 654 try { 655 pub.sendMail(args[4], "test subject", "test message", false); 656 } catch (CruiseControlException e) { 657 LOG.error("test failed", e); 658 } 659 } 660 661 679 protected String createAlertUserList(XMLLogHelper logHelper) throws CruiseControlException { 680 return createEmailString(createAlertUserSet(logHelper)); 681 } 682 683 684 692 protected Set createAlertUserSet(XMLLogHelper logHelper) throws CruiseControlException { 693 if (alertAddresses.length == 0) { 694 return Collections.EMPTY_SET; 695 } 696 697 Set users = new HashSet (); 698 Set modificationSet = logHelper.getModifications(); 699 700 for (Iterator modificationIter = modificationSet.iterator(); modificationIter.hasNext(); ) { 701 Modification mod = (Modification) modificationIter.next(); 702 String modifiedFile = mod.getFullPath(); 703 704 LOG.debug("Modified file: " + modifiedFile); 705 706 for (int i = 0; i < alertAddresses.length; i++) { 708 String emailAddress = alertAddresses[i].getAddress(); 709 710 if (emailAddress != null && !"".equals(emailAddress.trim()) 711 && !users.contains(emailAddress) 712 && matchRegExpr(modifiedFile, alertAddresses[i].fileFilter)) { 713 714 users.add(emailAddress); 716 717 LOG.info("Alert '" + emailAddress 718 + "' because their fileRegExpr '" + alertAddresses[i].fileRegExpr 719 + "' matched " + modifiedFile); 720 } 721 } 722 } 723 724 Set emails = new TreeSet (); 725 mapperHelper.mapUsers(this, users, emails); 726 return emails; 727 } 728 729 730 737 protected String createEmailString(Set emails) { 738 StringBuffer commaDelimitedString = new StringBuffer (); 739 Iterator emailIterator = appendDefaultSuffix(emails).iterator(); 740 741 while (emailIterator.hasNext()) { 742 String mappedUser = (String ) emailIterator.next(); 743 commaDelimitedString.append(mappedUser); 744 if (emailIterator.hasNext()) { 745 commaDelimitedString.append(","); 746 } 747 } 748 749 LOG.debug("List of emails: " + commaDelimitedString); 750 751 return commaDelimitedString.toString(); 752 } 753 754 private Set appendDefaultSuffix(Set emails) { 755 Set result = new TreeSet (); 756 Iterator emailIterator = emails.iterator(); 757 758 while (emailIterator.hasNext()) { 759 String mappedUser = (String ) emailIterator.next(); 760 if (mappedUser.indexOf("@") < 0) { 762 mappedUser += defaultSuffix; 763 } 764 result.add(mappedUser); 765 } 766 return result; 767 } 768 769 770 777 protected boolean matchRegExpr(String input, GlobFilenameFilter pattern) { 778 File file = new File (input); 779 String path = file.toString(); 780 781 if (File.separatorChar == '\\') { 784 path = path.replace('\\', '/'); 785 } 786 787 return pattern.accept(file, path); 788 } 789 } 790 | Popular Tags |