/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2001-2003, ThoughtWorks, Inc. * 200 E. Randolph, 25th Floor * Chicago, IL 60601 USA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * + Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ package net.sourceforge.cruisecontrol.publishers; import java.io.File; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.SendFailedException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import net.sourceforge.cruisecontrol.CruiseControlException; import net.sourceforge.cruisecontrol.Modification; import net.sourceforge.cruisecontrol.Publisher; import net.sourceforge.cruisecontrol.publishers.email.EmailMapper; import net.sourceforge.cruisecontrol.publishers.email.EmailMapperHelper; import net.sourceforge.cruisecontrol.publishers.email.EmailMapping; import net.sourceforge.cruisecontrol.publishers.email.EmailTransmissionResult; import net.sourceforge.cruisecontrol.util.ValidationHelper; import net.sourceforge.cruisecontrol.util.XMLLogHelper; import org.apache.log4j.Logger; import org.apache.oro.io.GlobFilenameFilter; import org.apache.oro.text.MalformedCachePatternException; import org.apache.commons.validator.EmailValidator; import org.jdom.Element; import org.masukomi.aspirin.core.MailQue; /** * Abstract implementation of the <code>Publisher</code> interface, specifically tailored for sending email. The only * abstract method is createMessage, which allows different implementations to send different messages as the body of * the email. As it currently stands, there is one concrete implementation-- <code>LinkEmailPublisher</code>, but the * ability to create <code>EmailPublisher</code>s that handle sending a text summary or an html summary is there. * * @author alden almagro, ThoughtWorks, Inc. 2002 */ public abstract class EmailPublisher implements Publisher { private static final Logger LOG = Logger.getLogger(EmailPublisher.class); private String mailHost; private String userName; private String password; private String mailPort; private boolean useSSL; private String buildResultsURL; private static final Always[] EMPTY_ALWAYS_ADDRESSES = new Always[0]; private Always[] alwaysAddresses = EMPTY_ALWAYS_ADDRESSES; private static final Failure[] EMPTY_FAILURE_ADDRESSES = new Failure[0]; private Failure[] failureAddresses = EMPTY_FAILURE_ADDRESSES; private static final Success[] EMPTY_SUCCESS_ADDRESSES = new Success[0]; private Success[] successAddresses = EMPTY_SUCCESS_ADDRESSES; private static final Alert[] EMPTY_ALERT_ADDRESSES = new Alert[0]; private Alert[] alertAddresses = EMPTY_ALERT_ADDRESSES; private static final Ignore[] EMPTY_IGNORE_ADDRESSES = new Ignore[0]; private Ignore[] ignoreUsers = EMPTY_IGNORE_ADDRESSES; private static final EmailMapper[] EMPTY_EMAIL_MAPPERS = new EmailMapper[0]; private EmailMapper[] emailMapper = EMPTY_EMAIL_MAPPERS; private final EmailMapperHelper mapperHelper = new EmailMapperHelper(); private String returnAddress; private String returnName; private String defaultSuffix = ""; private String reportSuccess = "always"; private boolean spamWhileBroken = true; private boolean skipUsers = false; private String subjectPrefix; private boolean failAsImportant = true; /** * Implementations of this method will create the email message body. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return <code>String</code> containing the message */ protected abstract String createMessage(XMLLogHelper logHelper); /* * Called after the configuration is read to make sure that all the mandatory parameters were specified.. @throws * CruiseControlException if there was a configuration error. */ public void validate() throws CruiseControlException { ValidationHelper.assertIsSet(getReturnAddress(), "returnaddress", this.getClass()); ValidationHelper.assertFalse(getUsername() != null && getPassword() == null, "'password' is required if 'username' is set for email."); ValidationHelper.assertFalse(getPassword() != null && getUsername() == null, "'username' is required if 'password' is set for email."); validateAddresses(alwaysAddresses); validateAddresses(alertAddresses); validateAddresses(failureAddresses); validateAddresses(successAddresses); for (int i = 0; i < ignoreUsers.length; i++) { ignoreUsers[i].validate(); } } private void validateAddresses(final Address[] addresses) throws CruiseControlException { for (int i = 0; i < addresses.length; i++) { addresses[i].validate(); } } /** * Creates the subject line for the email message. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return <code>String</code> containing the subject line. * @throws CruiseControlException if an error occurs while building subject string */ protected String createSubject(final XMLLogHelper logHelper) throws CruiseControlException { final StringBuffer subjectLine = new StringBuffer(); if (subjectPrefix != null) { subjectLine.append(subjectPrefix).append(" "); } subjectLine.append(logHelper.getProjectName()); // we want the label whether the build passed or failed final String label = logHelper.getLabel(); if (label.trim().length() > 0) { subjectLine.append(" ").append(label); } if (logHelper.isBuildSuccessful()) { // Anytime the build is "fixed" the subjest line // should read "fixed". It might confuse recipients...but // it shouldn't if (logHelper.isBuildFix()) { subjectLine.append(" Build Fixed"); } else { subjectLine.append(" Build Successful"); } } else { subjectLine.append(" Build Failed"); } return subjectLine.toString(); } /** * Determines if the conditions are right for the email to be sent. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return whether or not the mail message should be sent. * @throws CruiseControlException if the "lastbuildsuccessful" property name can not be found. */ protected boolean shouldSend(final XMLLogHelper logHelper) throws CruiseControlException { if (logHelper.isBuildSuccessful()) { if (reportSuccess.equalsIgnoreCase("always")) { return true; } if (reportSuccess.equalsIgnoreCase("fixes")) { if (logHelper.wasPreviousBuildSuccessful()) { LOG.debug("reportSuccess is set to 'fixes', not sending emails for repeated successful builds."); return false; } else { return true; } } if (reportSuccess.equalsIgnoreCase("never")) { LOG.debug("reportSuccess is set to 'never', not sending emails for successful builds."); return false; } } else { // build wasn't successful if (!logHelper.wasPreviousBuildSuccessful() && logHelper.isBuildNecessary() && !spamWhileBroken) { LOG.debug("spamWhileBroken is set to false, not sending email"); return false; } } return true; } /** * Creates the list of email addresses to receive the email message. Uses configured emailmappers to map user names * to email addresses. After all mappings are done, mapped users are checked for existence of the domain part * (i.e @mydomain.com) in the mapped email address. If it doesn't exist, default (if configured) is appended Any * addresses set in the <code>addAlwaysAddress</code> method will always receive the email if it is sent. Any * address set in the <code>addFailureAddress</code> method will receive the message if the build has failed. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return comma delimited <code>String</code> of email addresses to receive the email message. * @throws CruiseControlException if an error occurs while building userList string */ protected String createUserList(final XMLLogHelper logHelper) throws CruiseControlException { final Set<String> emails = createUserSet(logHelper); return createEmailString(emails); } /** * Creates the <code>Set</code> of email addresses to receive the email message. * <p> * Uses configured emailmappers to map user names to email addresses. After all mappings are done, mapped users are * checked for existence of the domain part (i.e @mydomain.com) in the mapped email address. If it doesn't exist, * default (if configured) is appended Any addresses set in the <code>addAlwaysAddress</code> method will always * receive the email if it is sent. Any address set in the <code>addFailureAddress</code> method will receive the * message if the build has failed. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return A <code>Set</code> of email addresses to receive the email message. * @throws CruiseControlException if an error occurs while building userSet */ protected Set<String> createUserSet(final XMLLogHelper logHelper) throws CruiseControlException { final Set<String> users = skipUsers ? new HashSet<String>() : logHelper.getBuildParticipants(); // remove users we want to exclude from the mail spam for (int i = 0; i < ignoreUsers.length; i++) { users.remove(ignoreUsers[i].getUser()); } // add always addresses for (int i = 0; i < alwaysAddresses.length; i++) { users.add(alwaysAddresses[i].getAddress()); } // if build failed, add failure addresses if (!logHelper.isBuildSuccessful()) { for (int i = 0; i < failureAddresses.length; i++) { users.add(failureAddresses[i].getAddress()); } } // If build fixed, add failure addresses that want to know about the fix if (logHelper.isBuildFix()) { for (int i = 0; i < failureAddresses.length; i++) { if (failureAddresses[i].shouldReportWhenFixed()) { users.add(failureAddresses[i].getAddress()); } } } // if build succeeded, add success addresses if (logHelper.isBuildSuccessful()) { for (int i = 0; i < successAddresses.length; i++) { users.add(successAddresses[i].getAddress()); } } final Set<String> emails = new TreeSet<String>(); mapperHelper.mapUsers(this, users, emails); for (Iterator iterator = emails.iterator(); iterator.hasNext();) { String email = (String) iterator.next(); if (!isValid(email)) { LOG.warn("Invalid email: " + email); } } return emails; } boolean isValid(final String address) { return EmailValidator.getInstance().isValid(address); } /** * Implementing the <code>Publisher</code> interface. Sends modification alert and regular build emails. If a user * is supposed to receive a modification alert and a regular build email, they will only receive the modification * alert. This prevents duplicate emails (currently only the subject is different). */ public void publish(final Element cruisecontrolLog) throws CruiseControlException { final XMLLogHelper helper = new XMLLogHelper(cruisecontrolLog); final boolean important = failAsImportant && !helper.isBuildSuccessful(); final Set<String> userSet = new HashSet<String>(); final Set<String> alertSet = createAlertUserSet(helper); final String subject = createSubject(helper); if (!alertSet.isEmpty()) { final String alertSubject = "[MOD ALERT] " + subject; sendMail(createEmailString(alertSet), alertSubject, createMessage(helper), important); } if (shouldSend(helper)) { userSet.addAll(createUserSet(helper)); // Do not send duplicates to users who received an alert email userSet.removeAll(alertSet); if (!userSet.isEmpty()) { sendMail(createEmailString(userSet), subject, createMessage(helper), important); } else { if (alertSet.isEmpty()) { LOG.info("No recipients, so not sending email"); } } } } /** * builds the properties object for the mail session * * @return a properties object containing configured properties. */ protected Properties getMailProperties() { final Properties props = System.getProperties(); props.put("mail.smtp.host", mailHost); props.put("mail.smtp.sendpartial", "true"); if (mailPort != null) { props.put("mail.smtp.port", mailPort); } LOG.debug("mailHost is " + mailHost + ", mailPort is " + (mailPort == null ? "default" : mailPort)); if (userName != null && password != null) { props.put("mail.smtp.auth", "true"); if (useSSL) { if (mailPort != null) { props.put("mail.smtp.socketFactory.port", mailPort); } props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.put("mail.smtp.socketFactory.fallback", "false"); } } return props; } /** * Sends an email message. * * @param toList * comma delimited <code>String</code> of email addresses * @param subject * subject line for the message * @param message * body of the message * @param important * if true, send mail with "High" importance. * @return Boolean value indicating if an email was sent. * @throws CruiseControlException if a MessagingException occurs. */ protected boolean sendMail(final String toList, final String subject, final String message, final boolean important) throws CruiseControlException { boolean emailSent = false; if (toList != null && toList.trim().length() != 0) { LOG.debug("Sending email to: " + toList); final Session session = initializeSession(); try { final MimeMessage msg = new MimeMessage(session); msg.setFrom(getFromAddress()); msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toList, false)); msg.setSubject(subject); msg.setSentDate(new Date()); final String importance = (important) ? "High" : "Normal"; msg.addHeader("Importance", importance); addContentToMessage(message, msg); emailSent = send(session, msg); } catch (SendFailedException e) { LOG.warn(e.getMessage(), e); } catch (MessagingException e) { throw new CruiseControlException(e.getClass().getName() + ": " + e.getMessage(), e); } } return emailSent; } private boolean send(Session session, MimeMessage message) throws MessagingException { if (shouldUseSMTPServer()) { if (userName != null && password != null) { message.saveChanges(); // implicit with send() final Transport transport = session.getTransport("smtp"); transport.connect(mailHost, userName, password); transport.sendMessage(message, message.getAllRecipients()); transport.close(); } else { Transport.send(message); } return true; } else { final MailQue queue = new MailQue(); final EmailTransmissionResult result = new EmailTransmissionResult(); queue.addWatcher(result); queue.queMail(message); return result.isSuccess(); } } private Session initializeSession() { if (shouldUseSMTPServer()) { final Session session = Session.getDefaultInstance(getMailProperties(), null); session.setDebug(LOG.isDebugEnabled()); return session; } else { final Properties configuration = new Properties(); configuration.setProperty("mail.transport.protocol", "smtp"); return Session.getDefaultInstance(configuration); } } private boolean shouldUseSMTPServer() { return !(getMailHost() == null || "".equals(getMailHost().trim())); } /** * Subclasses can override this method to control how the content is added to the Message. * * @param content * content returned by createMessage * @param msg * mail Message with headers and addresses added elsewhere * @throws MessagingException see Message.setText() */ protected void addContentToMessage(final String content, final Message msg) throws MessagingException { msg.setText(content); } protected InternetAddress getFromAddress() throws AddressException { InternetAddress fromAddress = new InternetAddress(returnAddress); if (returnName != null) { try { fromAddress = new InternetAddress(returnAddress, returnName); } catch (UnsupportedEncodingException e) { LOG.error("error setting returnName [" + returnName + "]: " + e.getMessage()); } } return fromAddress; } public void setMailHost(final String hostname) { mailHost = hostname; } public String getMailHost() { return mailHost; } public void setUsername(final String name) { userName = name; } public String getUsername() { return userName; } public void setPassword(final String passwd) { password = passwd; } public String getPassword() { return password; } public void setMailPort(final String port) { mailPort = port; } public String getMailPort() { return mailPort; } public void setUseSSL(final boolean useSSL) { this.useSSL = useSSL; } public void setSubjectPrefix(final String prefix) { subjectPrefix = prefix; } public String getSubjectPrefix() { return subjectPrefix; } public String getBuildResultsURL() { if (buildResultsURL == null) { return defaultBuildResultsURL(); } return buildResultsURL; } private static String defaultBuildResultsURL() { return "http://" + canonicalHostName() + port() + "/dashboard"; } private static String canonicalHostName() { try { return InetAddress.getLocalHost().getCanonicalHostName(); } catch (UnknownHostException e) { return "localhost"; } } private static String port() { if (System.getProperty("cc.webport") == null) { return ""; } return ":" + System.getProperty("cc.webport"); } public void setBuildResultsURL(final String url) { buildResultsURL = url; } public EmailMapper[] getEmailMapper() { return emailMapper; } public String getReturnAddress() { return returnAddress; } public void setReturnAddress(final String emailAddress) { returnAddress = emailAddress; } public String getReturnName() { return returnName; } public void setReturnName(final String emailReturnName) { returnName = emailReturnName; } public String getDefaultSuffix() { return defaultSuffix; } public void setDefaultSuffix(final String defaultEmailSuffix) { defaultSuffix = defaultEmailSuffix; } public void setReportSuccess(final String report) { reportSuccess = report; } public void setSkipUsers(final boolean skip) { skipUsers = skip; } public void setSpamWhileBroken(final boolean spam) { spamWhileBroken = spam; } public void setFailAsImportant(final boolean important) { failAsImportant = important; } public Ignore createIgnore() { final List<Ignore> ignoreList = new ArrayList<Ignore>(); ignoreList.addAll(Arrays.asList(ignoreUsers)); final Ignore ignore = new Ignore(); ignoreList.add(ignore); ignoreUsers = ignoreList.toArray(new Ignore[ignoreList.size()]); return ignore; } public Always createAlways() { final List<Always> alwaysList = new ArrayList<Always>(); alwaysList.addAll(Arrays.asList(alwaysAddresses)); final Always always = new Always(); alwaysList.add(always); alwaysAddresses = alwaysList.toArray(new Always[alwaysList.size()]); return always; } public Failure createFailure() { final List<Failure> failureList = new ArrayList<Failure>(); failureList.addAll(Arrays.asList(failureAddresses)); final Failure failure = new Failure(); failureList.add(failure); failureAddresses = failureList.toArray(new Failure[failureList.size()]); return failure; } public Success createSuccess() { final List<Success> successList = new ArrayList<Success>(); successList.addAll(Arrays.asList(successAddresses)); final Success success = new Success(); successList.add(success); successAddresses = successList.toArray(new Success[successList.size()]); return success; } public Alert createAlert() { final List<Alert> alertsList = new ArrayList<Alert>(); alertsList.addAll(Arrays.asList(alertAddresses)); final Alert alert = new Alert(); alertsList.add(alert); alertAddresses = alertsList.toArray(new Alert[alertsList.size()]); return alert; } /* * for the <map ... /> entries, just stuff them into the cache in EmailMapperHelper */ public void add(final EmailMapping mapping) { EmailMapperHelper.addCacheEntry(this, mapping.getAlias(), mapping.getAddress()); } public void add(final EmailMapper mapper) { final List<EmailMapper> mapperList = new ArrayList<EmailMapper>(); mapperList.addAll(Arrays.asList(emailMapper)); mapper.setPublisher(this); mapperList.add(mapper); emailMapper = mapperList.toArray(new EmailMapper[mapperList.size()]); } public static class Ignore implements Serializable { private static final long serialVersionUID = 4763763343885476189L; private String user; public String getUser() { return user; } public void validate() throws CruiseControlException { ValidationHelper.assertIsSet(user, "user", getClass()); } public void setUser(final String theUser) { user = theUser; } } public static class Address implements Serializable { private static final long serialVersionUID = -7143350548506511400L; private String address; public String getAddress() { return address; } public void setAddress(final String theAddress) { address = theAddress; } public void validate() throws CruiseControlException { ValidationHelper.assertIsSet(address, "address", getClass()); ValidationHelper.assertFalse(address.equals(""), "empty string is not a valid value of address for " + getClass().getName()); } } public static class Always extends Address { private static final long serialVersionUID = -4906620821397147420L; } public static class Failure extends Address { private static final long serialVersionUID = -6027290532428946586L; /** * Set to true to send an email to this "address" when the build gets fixed. */ private boolean reportWhenFixed = false; public boolean shouldReportWhenFixed() { return reportWhenFixed; } public void setReportWhenFixed(final boolean reportWhenFixed) { this.reportWhenFixed = reportWhenFixed; } } public static class Success extends Address { private static final long serialVersionUID = 8001068483405344504L; } public static class Alert extends Address { private static final long serialVersionUID = 3644326033589789766L; public void validate() throws CruiseControlException { super.validate(); ValidationHelper.assertIsSet(fileRegExpr, "fileregexpr", getClass()); try { fileFilter = new GlobFilenameFilter(fileRegExpr); } catch (MalformedCachePatternException mcpe) { ValidationHelper.fail("invalid regexp '" + fileRegExpr + "'", mcpe); } } /** * A regExpr for the file you are interested in watching */ private String fileRegExpr = null; /** * The compiled regExp, set on validation of the Publisher */ private GlobFilenameFilter fileFilter = null; /** * A <code>String</code> representing the regexp to match against modified files * * @param f * A <code>String</code> representing the file that was modified */ public void setFileRegExpr(final String f) { this.fileRegExpr = f; } } /** * Creates the list of email addresses to receive an alert email message. * <p> * The full path of each modified file is compared against the regular expressions specified in the configuration. * If a modified file's path matches a regular expression, the user's email address is included in the returned * <code>String</code>. * <p> * Uses configured emailmappers to map user names to email addresses. After all mappings are done, mapped users are * checked for existence of the domain part (i.e @mydomain.com) in the mapped email address. If it doesn't exist, * default (if configured) is appended. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return comma delimited <code>String</code> of email addresses to receive the email message. */ protected String createAlertUserList(final XMLLogHelper logHelper) { return createEmailString(createAlertUserSet(logHelper)); } /** * Creates a <code>Set</code> of email addresses to receive an alert email message based on the logHelper. * * @param logHelper * <code>XMLLogHelper</code> wrapper for the build log. * @return A <code>Set</code> of email addresses to receive the email message. */ protected Set<String> createAlertUserSet(final XMLLogHelper logHelper) { if (alertAddresses.length == 0) { return Collections.emptySet(); } final Set<String> users = new HashSet<String>(); final Set<Modification> modificationSet = logHelper.getModifications(); for (Iterator modificationIter = modificationSet.iterator(); modificationIter.hasNext();) { final Modification mod = (Modification) modificationIter.next(); final String modifiedFile = mod.getFullPath(); LOG.debug("Modified file: " + modifiedFile); // Compare the modified file to the regExpr's for (int i = 0; i < alertAddresses.length; i++) { final String emailAddress = alertAddresses[i].getAddress(); if (emailAddress != null && !"".equals(emailAddress.trim()) && !users.contains(emailAddress) && matchRegExpr(modifiedFile, alertAddresses[i].fileFilter)) { // We have a new match, send an alert email users.add(emailAddress); LOG.info("Alert '" + emailAddress + "' because their fileRegExpr '" + alertAddresses[i].fileRegExpr + "' matched " + modifiedFile); } } } final Set<String> emails = new TreeSet<String>(); mapperHelper.mapUsers(this, users, emails); return emails; } /** * Creates a comma delimited <code>String</code> from a <code>Set</code> of <code>String</code>s. * * @param emails * A <code>Set</code> containing <code>String</code>s of emails addresses * @return A comma delimited <code>String</code> of email addresses */ protected String createEmailString(final Set<String> emails) { final StringBuffer commaDelimitedString = new StringBuffer(); final Iterator emailIterator = appendDefaultSuffix(emails).iterator(); while (emailIterator.hasNext()) { final String mappedUser = (String) emailIterator.next(); commaDelimitedString.append(mappedUser); if (emailIterator.hasNext()) { commaDelimitedString.append(","); } } LOG.debug("List of emails: " + commaDelimitedString); return commaDelimitedString.toString(); } private Set<String> appendDefaultSuffix(final Set emails) { final Set<String> result = new TreeSet<String>(); final Iterator emailIterator = emails.iterator(); while (emailIterator.hasNext()) { String mappedUser = (String) emailIterator.next(); // append default suffix if need to if (mappedUser.indexOf("@") < 0) { mappedUser += defaultSuffix; } result.add(mappedUser); } return result; } /** * Compare the input <code>String</code> against a regular expression pattern. * * @param input * A <code>String</code> to compare against the regExpr pattern * @param pattern * A <code>GlobFilenameFilter</code> pattern * @return True if the file matches the regular expression pattern. Otherwise false. */ protected boolean matchRegExpr(final String input, final GlobFilenameFilter pattern) { final File file = new File(input); String path = file.toString(); // On systems with a '\' as pathseparator convert it to a forward slash '/' // That makes patterns platform independent if (File.separatorChar == '\\') { path = path.replace('\\', '/'); } return pattern.accept(file, path); } }