You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2010/04/02 19:58:03 UTC

svn commit: r930337 [2/3] - in /james/server/trunk/spoolmanager/src/main/java/org/apache/james/transport: camel/ mailets/

Modified: james/server/trunk/spoolmanager/src/main/java/org/apache/james/transport/mailets/AbstractRemoteDelivery.java
URL: http://svn.apache.org/viewvc/james/server/trunk/spoolmanager/src/main/java/org/apache/james/transport/mailets/AbstractRemoteDelivery.java?rev=930337&r1=930336&r2=930337&view=diff
==============================================================================
--- james/server/trunk/spoolmanager/src/main/java/org/apache/james/transport/mailets/AbstractRemoteDelivery.java (original)
+++ james/server/trunk/spoolmanager/src/main/java/org/apache/james/transport/mailets/AbstractRemoteDelivery.java Fri Apr  2 17:58:03 2010
@@ -97,8 +97,8 @@ import org.apache.oro.text.regex.Perl5Ma
  * <p>
  * You really want to read the JavaMail documentation if you are working in
  * here, and you will want to view the list of JavaMail attributes, which are
- * documented <a href='http://java.sun.com/products/javamail/javadocs/com/sun/mail/smtp/package-summary.html''>here</a
- * > as well as other places.
+ * documented <a href='http://java.sun.com/products/javamail/javadocs/com/sun/mail/smtp/package-summary.html''>here</
+ * a > as well as other places.
  * </p>
  * 
  * @version CVS $Revision: 915502 $ $Date: 2010-02-23 21:27:49 +0100 (Di, 23 Feb
@@ -106,1659 +106,1491 @@ import org.apache.oro.text.regex.Perl5Ma
  */
 public abstract class AbstractRemoteDelivery extends GenericMailet implements CamelContextAware {
 
-	/** Default Delay Time (Default is 6*60*60*1000 Milliseconds (6 hours)). */
-	private static final long DEFAULT_DELAY_TIME = 21600000;
+    /** Default Delay Time (Default is 6*60*60*1000 Milliseconds (6 hours)). */
+    private static final long DEFAULT_DELAY_TIME = 21600000;
 
-	/** Pattern to match [attempts*]delay[units]. */
-	private static final String PATTERN_STRING = "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";
+    /** Pattern to match [attempts*]delay[units]. */
+    private static final String PATTERN_STRING = "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";
 
-	/** Compiled pattern of the above String. */
-	private static Pattern PATTERN = null;
+    /** Compiled pattern of the above String. */
+    private static Pattern PATTERN = null;
 
-	/** The DNSService */
-	private DNSService dnsServer;
-
-	/**
-	 * Static initializer.
-	 * <p>
-	 * Compiles pattern for processing delaytime entries.
-	 * <p>
-	 * Initializes MULTIPLIERS with the supported unit quantifiers
-	 */
-	static {
-		try {
-			Perl5Compiler compiler = new Perl5Compiler();
-			PATTERN = compiler.compile(PATTERN_STRING,
-					Perl5Compiler.READ_ONLY_MASK);
-		} catch (MalformedPatternException mpe) {
-			// this should not happen as the pattern string is hardcoded.
-			mpe.printStackTrace(System.err);
-		}
-	}
-
-	/** Flag to define verbose logging messages. */
-	private boolean isDebug = false;
-
-	/** List of Delay Times. Controls frequency of retry attempts. */
-	private long[] delayTimes;
-
-	/** Maximum no. of retries (Defaults to 5). */
-	private int maxRetries = 5;
-
-	/** Default number of ms to timeout on smtp delivery */
-	private long smtpTimeout = 180000;
-
-	/** If false then ANY address errors will cause the transmission to fail */
-	private boolean sendPartial = false;
-
-	/**
-	 * The amount of time JavaMail will wait before giving up on a socket
-	 * connect()
-	 */
-	private int connectionTimeout = 60000;
-
-	/** The server(s) to send all email to */
-	private Collection<String> gatewayServer = null;
-
-	/** Auth for gateway server */
-	private String authUser = null;
-
-	/** Password for gateway server */
-	private String authPass = null;
-
-	/**
-	 * JavaMail delivery socket binds to this local address. If null the
-	 * JavaMail default will be used.
-	 */
-	private String bindAddress = null;
-
-	/**
-	 * True, if the bind configuration parameter is supplied,
-	 * RemoteDeliverySocketFactory will be used in this case.
-	 */
-	private boolean isBindUsed = false;
-
-	/** the processor for creating Bounces */
-	private String bounceProcessor = null;
-
-	/** Default properties for the JavaMail Session */
-	private Properties defprops = new Properties();
-
-	/** The retry count dnsProblemErrors */
-	private int dnsProblemRetry = 0;
-
-	private MailServer mailServer;
-
-	private ProducerTemplate producerTemplate;
-
-	private CamelContext context;
-
-	private String outgoingQueue;
-
-	private String outgoingRetryQueue;
-
-	@Resource(name = "producerTemplate")
-	public void setProducerTemplate(ProducerTemplate producerTemplate) {
-		this.producerTemplate = producerTemplate;
-	}
-
-	@Resource(name = "dnsserver")
-	public void setDNSService(DNSService dnsService) {
-		this.dnsServer = dnsService;
-	}
-
-	@Resource(name = "James")
-	public void setMailServer(MailServer mailServer) {
-		this.mailServer = mailServer;
-	}
-
-	/**
-	 * Initializes all arguments based on configuration values specified in the
-	 * James configuration file.
-	 * 
-	 * @throws MessagingException
-	 *             on failure to initialize attributes.
-	 */
-	public void init() throws MessagingException {
-		// Set isDebug flag.
-		isDebug = (getInitParameter("debug") == null) ? false : new Boolean(
-				getInitParameter("debug")).booleanValue();
-
-		// Create list of Delay Times.
-		ArrayList<Delay> delayTimesList = new ArrayList<Delay>();
-		try {
-			if (getInitParameter("delayTime") != null) {
-
-				// parses delayTimes specified in config file.
-				final Perl5Matcher delayTimeMatcher = new Perl5Matcher();
-				String delayTimesParm = getInitParameter("delayTime");
-
-				// Split on commas
-				StringTokenizer st = new StringTokenizer(delayTimesParm, ",");
-				while (st.hasMoreTokens()) {
-					String delayTime = st.nextToken();
-					delayTimesList.add(new Delay(delayTimeMatcher, delayTime));
-				}
-			} else {
-				// Use default delayTime.
-				delayTimesList.add(new Delay());
-			}
-		} catch (Exception e) {
-			log("Invalid delayTime setting: " + getInitParameter("delayTime"));
-		}
-
-		try {
-			// Get No. of Max Retries.
-			if (getInitParameter("maxRetries") != null) {
-				maxRetries = Integer.parseInt(getInitParameter("maxRetries"));
-			}
-
-			// Check consistency of 'maxRetries' with delayTimesList attempts.
-			int totalAttempts = calcTotalAttempts(delayTimesList);
-
-			// If inconsistency found, fix it.
-			if (totalAttempts > maxRetries) {
-				log("Total number of delayTime attempts exceeds maxRetries specified. "
-						+ " Increasing maxRetries from "
-						+ maxRetries
-						+ " to "
-						+ totalAttempts);
-				maxRetries = totalAttempts;
-			} else {
-				int extra = maxRetries - totalAttempts;
-				if (extra != 0) {
-					log("maxRetries is larger than total number of attempts specified.  "
-							+ "Increasing last delayTime with "
-							+ extra
-							+ " attempts ");
-
-					// Add extra attempts to the last delayTime.
-					if (delayTimesList.size() != 0) {
-						// Get the last delayTime.
-						Delay delay = (Delay) delayTimesList.get(delayTimesList
-								.size() - 1);
-
-						// Increase no. of attempts.
-						delay.setAttempts(delay.getAttempts() + extra);
-						log("Delay of " + delay.getDelayTime()
-								+ " msecs is now attempted: "
-								+ delay.getAttempts() + " times");
-					} else {
-						throw new MessagingException(
-								"No delaytimes, cannot continue");
-					}
-				}
-			}
-			delayTimes = expandDelays(delayTimesList);
-
-		} catch (Exception e) {
-			log("Invalid maxRetries setting: " + getInitParameter("maxRetries"));
-		}
-
-		outgoingQueue = getInitParameter("outgoingQueue");
-		if (outgoingQueue == null) {
-			outgoingQueue = "outgoing";
-		}
-
-		outgoingRetryQueue = getInitParameter("outgoingRetryQueue");
-		if (outgoingRetryQueue == null) {
-			outgoingRetryQueue = "outgoing.retry";
-		}
-
-		try {
-			if (getInitParameter("timeout") != null) {
-				smtpTimeout = Integer.parseInt(getInitParameter("timeout"));
-			}
-		} catch (Exception e) {
-			log("Invalid timeout setting: " + getInitParameter("timeout"));
-		}
-
-		try {
-			if (getInitParameter("connectiontimeout") != null) {
-				connectionTimeout = Integer
-						.parseInt(getInitParameter("connectiontimeout"));
-			}
-		} catch (Exception e) {
-			log("Invalid timeout setting: " + getInitParameter("timeout"));
-		}
-
-		sendPartial = (getInitParameter("sendpartial") == null) ? false
-				: new Boolean(getInitParameter("sendpartial")).booleanValue();
-
-		bounceProcessor = getInitParameter("bounceProcessor");
-
-		String gateway = getInitParameter("gateway");
-		String gatewayPort = getInitParameter("gatewayPort");
-
-		if (gateway != null) {
-			gatewayServer = new ArrayList<String>();
-			StringTokenizer st = new StringTokenizer(gateway, ",");
-			while (st.hasMoreTokens()) {
-				String server = st.nextToken().trim();
-				if (server.indexOf(':') < 0 && gatewayPort != null) {
-					server += ":";
-					server += gatewayPort;
-				}
-
-				if (isDebug)
-					log("Adding SMTP gateway: " + server);
-				gatewayServer.add(server);
-			}
-			authUser = getInitParameter("gatewayUsername");
-			// backward compatibility with 2.3.x
-			if (authUser == null) {
-				authUser = getInitParameter("gatewayusername");
-			}
-			authPass = getInitParameter("gatewayPassword");
-		}
-
-		bindAddress = getInitParameter("bind");
-		isBindUsed = bindAddress != null;
-		try {
-			if (isBindUsed)
-				RemoteDeliverySocketFactory.setBindAdress(bindAddress);
-		} catch (UnknownHostException e) {
-			log("Invalid bind setting (" + bindAddress + "): " + e.toString());
-		}
-
-		// deal with <mail.*> attributes, passing them to javamail
-		Iterator<String> i = getInitParameterNames();
-		while (i.hasNext()) {
-			String name = i.next();
-			if (name.startsWith("mail.")) {
-				defprops.put(name, getInitParameter(name));
-			}
-
-		}
-
-		String dnsRetry = getInitParameter("maxDnsProblemRetries");
-		if (dnsRetry != null && !dnsRetry.equals("")) {
-			dnsProblemRetry = Integer.parseInt(dnsRetry);
-		}
-
-		try {
-			getCamelContext().addRoutes(new RemoteDeliveryRouteBuilder());
-		} catch (Exception e) {
-			throw new MessagingException("Unable to add camel route");
-		}
-	}
-
-	/**
-	 * Calculates Total no. of attempts for the specified delayList.
-	 * 
-	 * @param delayList
-	 *            list of 'Delay' objects
-	 * @return total no. of retry attempts
-	 */
-	private int calcTotalAttempts(ArrayList<Delay> delayList) {
-		int sum = 0;
-		Iterator<Delay> i = delayList.iterator();
-		while (i.hasNext()) {
-			Delay delay = i.next();
-			sum += delay.getAttempts();
-		}
-		return sum;
-	}
-
-	/**
-	 * This method expands an ArrayList containing Delay objects into an array
-	 * holding the only delaytime in the order.
-	 * <p>
-	 * So if the list has 2 Delay objects the first having attempts=2 and
-	 * delaytime 4000 the second having attempts=1 and delaytime=300000 will be
-	 * expanded into this array:
-	 * <p>
-	 * long[0] = 4000
-	 * <p>
-	 * long[1] = 4000
-	 * <p>
-	 * long[2] = 300000
-	 * <p>
-	 * 
-	 * @param list
-	 *            the list to expand
-	 * @return the expanded list
-	 **/
-	private long[] expandDelays(ArrayList<Delay> list) {
-		long[] delays = new long[calcTotalAttempts(list)];
-		Iterator<Delay> i = list.iterator();
-		int idx = 0;
-		while (i.hasNext()) {
-			Delay delay = i.next();
-			for (int j = 0; j < delay.getAttempts(); j++) {
-				delays[idx++] = delay.getDelayTime();
-			}
-		}
-		return delays;
-	}
-
-	/**
-	 * This method returns, given a retry-count, the next delay time to use.
-	 * 
-	 * @param retry_count
-	 *            the current retry_count.
-	 * @return the next delay time to use, given the retry count
-	 **/
-	private long getNextDelay(int retry_count) {
-		if (retry_count > delayTimes.length) {
-			return DEFAULT_DELAY_TIME;
-		}
-		return delayTimes[retry_count - 1];
-	}
-
-	/**
-	 * This class is used to hold a delay time and its corresponding number of
-	 * retries.
-	 **/
-	private final static class Delay {
-		private int attempts = 1;
-
-		private long delayTime = DEFAULT_DELAY_TIME;
-
-		/**
-		 * This constructor expects Strings of the form
-		 * "[attempt\*]delaytime[unit]".
-		 * <p>
-		 * The optional attempt is the number of tries this delay should be used
-		 * (default = 1). The unit, if present, must be one of
-		 * (msec,sec,minute,hour,day). The default value of unit is 'msec'.
-		 * <p>
-		 * The constructor multiplies the delaytime by the relevant multiplier
-		 * for the unit, so the delayTime instance variable is always in msec.
-		 * 
-		 * @param initString
-		 *            the string to initialize this Delay object from
-		 **/
-		public Delay(final Perl5Matcher delayTimeMatcher, String initString)
-				throws MessagingException {
-			// Default unit value to 'msec'.
-			String unit = "msec";
-
-			if (delayTimeMatcher.matches(initString, PATTERN)) {
-				MatchResult res = delayTimeMatcher.getMatch();
-
-				// The capturing groups will now hold:
-				// at 1: attempts * (if present)
-				// at 2: delaytime
-				// at 3: unit (if present)
-				if (res.group(1) != null && !res.group(1).equals("")) {
-					// We have an attempt *
-					String attemptMatch = res.group(1);
-
-					// Strip the * and whitespace.
-					attemptMatch = attemptMatch.substring(0,
-							attemptMatch.length() - 1).trim();
-					attempts = Integer.parseInt(attemptMatch);
-				}
-
-				delayTime = Long.parseLong(res.group(2));
-
-				if (!res.group(3).equals("")) {
-					// We have a value for 'unit'.
-					unit = res.group(3).toLowerCase(Locale.US);
-				}
-			} else {
-				throw new MessagingException(initString + " does not match "
-						+ PATTERN_STRING);
-			}
-
-			// calculate delayTime.
-			try {
-				delayTime = TimeConverter.getMilliSeconds(delayTime, unit);
-			} catch (NumberFormatException e) {
-				throw new MessagingException(e.getMessage());
-			}
-		}
-
-		/**
-		 * This constructor makes a default Delay object with attempts = 1 and
-		 * delayTime = DEFAULT_DELAY_TIME.
-		 **/
-		public Delay() {
-		}
-
-		/**
-		 * @return the delayTime for this Delay
-		 **/
-		public long getDelayTime() {
-			return delayTime;
-		}
-
-		/**
-		 * @return the number attempts this Delay should be used.
-		 **/
-		public int getAttempts() {
-			return attempts;
-		}
-
-		/**
-		 * Set the number attempts this Delay should be used.
-		 **/
-		public void setAttempts(int value) {
-			attempts = value;
-		}
-
-		/**
-		 * Pretty prints this Delay
-		 **/
-		public String toString() {
-			String message = getAttempts() + "*" + getDelayTime() + "msecs";
-			return message;
-		}
-	}
-
-	public String getMailetInfo() {
-		return "RemoteDelivery Mailet";
-	}
-
-	/**
-	 * For this message, we take the list of recipients, organize these into
-	 * distinct servers, and duplicate the message for each of these servers,
-	 * and then call the deliver (messagecontainer) method for each
-	 * server-specific messagecontainer ... that will handle storing it in the
-	 * outgoing queue if needed.
-	 * 
-	 * @param mail
-	 *            org.apache.mailet.Mail
-	 */
-	public void service(Mail mail) throws MessagingException {
-		// Do I want to give the internal key, or the message's Message ID
-		if (isDebug) {
-			log("Remotely delivering mail " + mail.getName());
-		}
-		Collection<MailAddress> recipients = mail.getRecipients();
-
-		if (gatewayServer == null) {
-			// Must first organize the recipients into distinct servers (name
-			// made case insensitive)
-			Hashtable<String, Collection<MailAddress>> targets = new Hashtable<String, Collection<MailAddress>>();
-			for (Iterator<MailAddress> i = recipients.iterator(); i.hasNext();) {
-				MailAddress target = i.next();
-				String targetServer = target.getDomain().toLowerCase(Locale.US);
-				Collection<MailAddress> temp = targets.get(targetServer);
-				if (temp == null) {
-					temp = new ArrayList<MailAddress>();
-					targets.put(targetServer, temp);
-				}
-				temp.add(target);
-			}
-
-			// We have the recipients organized into distinct servers... put
-			// them into the
-			// delivery store organized like this... this is ultra inefficient I
-			// think...
-
-			// Store the new message containers, organized by server, in the
-			// outgoing mail repository
-			String name = mail.getName();
-			for (Iterator<String> i = targets.keySet().iterator(); i.hasNext();) {
-				String host = i.next();
-				Collection<MailAddress> rec = targets.get(host);
-				if (isDebug) {
-					StringBuilder logMessageBuffer = new StringBuilder(128)
-							.append("Sending mail to ").append(rec).append(
-									" on host ").append(host);
-					log(logMessageBuffer.toString());
-				}
-				Mail m = new InMemoryMail(mail);
-				m.setRecipients(rec);
-				StringBuilder nameBuffer = new StringBuilder(128).append(name)
-						.append("-to-").append(host);
-				m.setName(nameBuffer.toString());
-
-				producerTemplate.sendBody("activemq:queue:"
-						+ outgoingQueue, m);
-				// workRepository.store(mail);
-				// Set it to try to deliver (in a separate thread) immediately
-				// (triggered by storage)
-			}
-		} else {
-			// Store the mail unaltered for processing by the gateway server(s)
-			if (isDebug) {
-				StringBuilder logMessageBuffer = new StringBuilder(128).append(
-						"Sending mail to ").append(mail.getRecipients())
-						.append(" via ").append(gatewayServer);
-				log(logMessageBuffer.toString());
-			}
-
-			producerTemplate.sendBody("activemq:queue:"
-					+ outgoingQueue , new InMemoryMail(mail));
-
-			// Set it to try to deliver (in a separate thread) immediately
-			// (triggered by storage)
-			// workRepository.store(mail);
-		}
-		mail.setState(Mail.GHOST);
-	}
-
-	/**
-	 * Processor which handles the delivery of messages. If the delivery should
-	 * get done again in the future it will set the
-	 * {@link JamesCamelConstants.JAMES_RETRY_DELIVERY} header to true. The next
-	 * delivery time is set in milliseconds in the
-	 * {@link JamesCamelConstants.JAMES_NEXT_DELIVERY} header
-	 * 
-	 * 
-	 */
-	public final class DeliveryProcessor implements Processor {
-
-		/*
-		 * (non-Javadoc)
-		 * 
-		 * @see org.apache.camel.Processor#process(org.apache.camel.Exchange)
-		 */
-		public void process(Exchange arg0) throws Exception {
-		
-			Message inMessage = arg0.getIn();
-			// Checks the pool and delivers a mail message
-			Properties props = new Properties();
-			// Not needed for production environment
-			props.put("mail.debug", "false");
-			// Reactivated: javamail 1.3.2 should no more have problems with
-			// "250 OK"
-			// messages (WAS "false": Prevents problems encountered with 250 OK
-			// Messages)
-			props.put("mail.smtp.ehlo", "true");
-			// By setting this property to true the transport is allowed to
-			// send 8 bit data to the server (if it supports the 8bitmime
-			// extension).
-			// 2006/03/01 reverted to false because of a javamail bug converting
-			// to 8bit
-			// messages created by an inputstream.
-			props.setProperty("mail.smtp.allow8bitmime", "true");
-			// Sets timeout on going connections
-			props.put("mail.smtp.timeout", smtpTimeout + "");
-
-			props.put("mail.smtp.connectiontimeout", connectionTimeout + "");
-			props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
-
-			// Set the hostname we'll use as this server
-			props.put("mail.smtp.localhost", mailServer.getHelloName());
-
-			if (isBindUsed) {
-				// undocumented JavaMail 1.2 feature, smtp transport will use
-				// our socket factory, which will also set the local address
-				props.put("mail.smtp.socketFactory.class",
-						RemoteDeliverySocketFactory.class.getClass());
-				// Don't fallback to the standard socket factory on error, do
-				// throw an exception
-				props.put("mail.smtp.socketFactory.fallback", "false");
-			}
-
-			if (authUser != null) {
-				props.put("mail.smtp.auth", "true");
-			}
-
-			props.putAll(defprops);
-
-			Session session = obtainSession(props);
-
-			Mail mail = inMessage.getBody(Mail.class);
-			String key = mail.getName();
-			if (isDebug) {
-				String message = Thread.currentThread().getName()
-						+ " will process mail " + key;
-				log(message);
-			}
-
-			// Deliver message
-			if (deliver(mail, session)) {
-				inMessage
-						.removeHeader(JamesCamelConstants.JAMES_RETRY_DELIVERY);
-
-				// Message was successfully delivered/fully failed...
-				// delete it
-				// LifecycleUtil.dispose(mail);
-				// workRepository.remove(key);
-			} else {
-
-				// Test the time...
-				int retries = 0;
-
-				try {
-					retries = Integer.parseInt(mail.getErrorMessage());
-				} catch (NumberFormatException e) {
-					// Something strange was happen with the errorMessage..
-					// Ignore the Exception and try
-					// to deliver it now!
-				}
-
-				long delay = getNextDelay(retries);
-				long timeToProcess = delay + mail.getLastUpdated().getTime();
-
-				inMessage.setHeader(JamesCamelConstants.JAMES_RETRY_DELIVERY,
-						true);
-				inMessage.setHeader(JamesCamelConstants.JAMES_NEXT_DELIVERY,
-						timeToProcess);
-			}
-
-		}
-
-		/**
-		 * We can assume that the recipients of this message are all going to
-		 * the same mail server. We will now rely on the DNS server to do DNS MX
-		 * record lookup and try to deliver to the multiple mail servers. If it
-		 * fails, it should throw an exception.
-		 * 
-		 * Creation date: (2/24/00 11:25:00 PM)
-		 * 
-		 * @param mail
-		 *            org.apache.james.core.MailImpl
-		 * @param session
-		 *            javax.mail.Session
-		 * @return boolean Whether the delivery was successful and the message
-		 *         can be deleted
-		 */
-		private boolean deliver(Mail mail, Session session) {
-			try {
-				if (isDebug) {
-					log("Attempting to deliver " + mail.getName());
-				}
-				MimeMessage message = mail.getMessage();
-
-				// Create an array of the recipients as InternetAddress objects
-				Collection<MailAddress> recipients = mail.getRecipients();
-				InternetAddress addr[] = new InternetAddress[recipients.size()];
-				int j = 0;
-				for (Iterator<MailAddress> i = recipients.iterator(); i
-						.hasNext(); j++) {
-					MailAddress rcpt = i.next();
-					addr[j] = rcpt.toInternetAddress();
-				}
-
-				if (addr.length <= 0) {
-					log("No recipients specified... not sure how this could have happened.");
-					return true;
-				}
-
-				// Figure out which servers to try to send to. This collection
-				// will hold all the possible target servers
-				Iterator<HostAddress> targetServers = null;
-				if (gatewayServer == null) {
-					MailAddress rcpt = (MailAddress) recipients.iterator()
-							.next();
-					String host = rcpt.getDomain();
-
-					// Lookup the possible targets
-					try {
-						targetServers = dnsServer.getSMTPHostAddresses(host);
-					} catch (TemporaryResolutionException e) {
-						log("Temporary problem looking up mail server for host: "
-								+ host);
-						StringBuilder exceptionBuffer = new StringBuilder(128)
-								.append(
-										"Temporary problem looking up mail server for host: ")
-								.append(host)
-								.append(
-										".  I cannot determine where to send this message.");
-
-						// temporary problems
-						return failMessage(mail, new MessagingException(
-								exceptionBuffer.toString()), false);
-					}
-					if (!targetServers.hasNext()) {
-						log("No mail server found for: " + host);
-						StringBuilder exceptionBuffer = new StringBuilder(128)
-								.append(
-										"There are no DNS entries for the hostname ")
-								.append(host)
-								.append(
-										".  I cannot determine where to send this message.");
-
-						int retry = 0;
-						try {
-							retry = Integer.parseInt(mail.getErrorMessage());
-						} catch (NumberFormatException e) {
-							// Unable to parse retryCount
-						}
-						if (retry == 0 || retry > dnsProblemRetry) {
-							// The domain has no dns entry.. Return a permanent
-							// error
-							return failMessage(mail, new MessagingException(
-									exceptionBuffer.toString()), true);
-						} else {
-							return failMessage(mail, new MessagingException(
-									exceptionBuffer.toString()), false);
-						}
-					}
-				} else {
-					targetServers = getGatewaySMTPHostAddresses(gatewayServer);
-				}
-
-				MessagingException lastError = null;
-
-				while (targetServers.hasNext()) {
-					try {
-						HostAddress outgoingMailServer = targetServers.next();
-						StringBuilder logMessageBuffer = new StringBuilder(256)
-								.append("Attempting delivery of ").append(
-										mail.getName()).append(" to host ")
-								.append(outgoingMailServer.getHostName())
-								.append(" at ").append(
-										outgoingMailServer.getHost()).append(
-										" for addresses ").append(
-										Arrays.asList(addr));
-						log(logMessageBuffer.toString());
-
-						Properties props = session.getProperties();
-						if (mail.getSender() == null) {
-							props.put("mail.smtp.from", "<>");
-						} else {
-							String sender = mail.getSender().toString();
-							props.put("mail.smtp.from", sender);
-						}
-
-						// Many of these properties are only in later JavaMail
-						// versions
-						// "mail.smtp.ehlo" //default true
-						// "mail.smtp.auth" //default false
-						// "mail.smtp.dsn.ret" //default to nothing... appended
-						// as RET= after MAIL FROM line.
-						// "mail.smtp.dsn.notify" //default to
-						// nothing...appended as NOTIFY= after RCPT TO line.
-
-						Transport transport = null;
-						try {
-							transport = session
-									.getTransport(outgoingMailServer);
-							try {
-								if (authUser != null) {
-									transport.connect(outgoingMailServer
-											.getHostName(), authUser, authPass);
-								} else {
-									transport.connect();
-								}
-							} catch (MessagingException me) {
-								// Any error on connect should cause the mailet
-								// to attempt
-								// to connect to the next SMTP server associated
-								// with this
-								// MX record. Just log the exception. We'll
-								// worry about
-								// failing the message at the end of the loop.
-								log(me.getMessage());
-								continue;
-							}
-							// if the transport is a SMTPTransport (from sun)
-							// some
-							// performance enhancement can be done.
-							if (transport.getClass().getName().endsWith(
-									".SMTPTransport")) {
-								boolean supports8bitmime = false;
-								try {
-									Method supportsExtension = transport
-											.getClass()
-											.getMethod(
-													"supportsExtension",
-													new Class[] { String.class });
-									supports8bitmime = ((Boolean) supportsExtension
-											.invoke(transport,
-													new Object[] { "8BITMIME" }))
-											.booleanValue();
-								} catch (NoSuchMethodException nsme) {
-									// An SMTPAddressFailedException with no
-									// getAddress method.
-								} catch (IllegalAccessException iae) {
-								} catch (IllegalArgumentException iae) {
-								} catch (InvocationTargetException ite) {
-									// Other issues with getAddress invokation.
-								}
-
-								// if the message is alredy 8bit or binary and
-								// the
-								// server doesn't support the 8bit extension it
-								// has
-								// to be converted to 7bit. Javamail api doesn't
-								// perform
-								// that conversion, but it is required to be a
-								// rfc-compliant smtp server.
-
-								// Temporarily disabled. See JAMES-638
-								if (!supports8bitmime) {
-									try {
-										convertTo7Bit(message);
-									} catch (IOException e) {
-										// An error has occured during the 7bit
-										// conversion.
-										// The error is logged and the message
-										// is sent anyway.
-
-										log(
-												"Error during the conversion to 7 bit.",
-												e);
-									}
-								}
-							} else {
-								// If the transport is not the one
-								// developed by Sun we are not sure of how it
-								// handles the 8 bit mime stuff,
-								// so I convert the message to 7bit.
-								try {
-									convertTo7Bit(message);
-								} catch (IOException e) {
-									log(
-											"Error during the conversion to 7 bit.",
-											e);
-								}
-							}
-							transport.sendMessage(message, addr);
-						} finally {
-							if (transport != null) {
-								try {
-									// James-899: transport.close() sends QUIT
-									// to the server; if that fails
-									// (e.g. because the server has already
-									// closed the connection) the message
-									// should be considered to be delivered
-									// because the error happened outside
-									// of the mail transaction (MAIL, RCPT,
-									// DATA).
-									transport.close();
-								} catch (MessagingException e) {
-									log("Warning: could not close the SMTP transport after sending mail ("
-											+ mail.getName()
-											+ ") to "
-											+ outgoingMailServer.getHostName()
-											+ " at "
-											+ outgoingMailServer.getHost()
-											+ " for "
-											+ mail.getRecipients()
-											+ "; probably the server has already closed the "
-											+ "connection. Message is considered to be delivered. Exception: "
-											+ e.getMessage());
-								}
-								transport = null;
-							}
-						}
-						logMessageBuffer = new StringBuilder(256).append(
-								"Mail (").append(mail.getName()).append(
-								") sent successfully to ").append(
-								outgoingMailServer.getHostName())
-								.append(" at ").append(
-										outgoingMailServer.getHost()).append(
-										" for ").append(mail.getRecipients());
-						log(logMessageBuffer.toString());
-						return true;
-					} catch (SendFailedException sfe) {
-						logSendFailedException(sfe);
-
-						if (sfe.getValidSentAddresses() != null) {
-							Address[] validSent = sfe.getValidSentAddresses();
-							if (validSent.length > 0) {
-								StringBuilder logMessageBuffer = new StringBuilder(
-										256).append("Mail (").append(
-										mail.getName()).append(
-										") sent successfully for ").append(
-										Arrays.asList(validSent));
-								log(logMessageBuffer.toString());
-							}
-						}
-
-						/*
-						 * SMTPSendFailedException introduced in JavaMail 1.3.2,
-						 * and provides detailed protocol reply code for the
-						 * operation
-						 */
-						if (sfe.getClass().getName().endsWith(
-								".SMTPSendFailedException")) {
-							try {
-								int returnCode = ((Integer) invokeGetter(sfe,
-										"getReturnCode")).intValue();
-								// if 5xx, terminate this delivery attempt by
-								// re-throwing the exception.
-								if (returnCode >= 500 && returnCode <= 599)
-									throw sfe;
-							} catch (ClassCastException cce) {
-							} catch (IllegalArgumentException iae) {
-							}
-						}
-
-						if (sfe.getValidUnsentAddresses() != null
-								&& sfe.getValidUnsentAddresses().length > 0) {
-							if (isDebug)
-								log("Send failed, "
-										+ sfe.getValidUnsentAddresses().length
-										+ " valid addresses remain, continuing with any other servers");
-							lastError = sfe;
-							continue;
-						} else {
-							// There are no valid addresses left to send, so
-							// rethrow
-							throw sfe;
-						}
-					} catch (MessagingException me) {
-						// MessagingException are horribly difficult to figure
-						// out what actually happened.
-						StringBuilder exceptionBuffer = new StringBuilder(256)
-								.append("Exception delivering message (")
-								.append(mail.getName()).append(") - ").append(
-										me.getMessage());
-						log(exceptionBuffer.toString());
-						if ((me.getNextException() != null)
-								&& (me.getNextException() instanceof java.io.IOException)) {
-							// This is more than likely a temporary failure
-
-							// If it's an IO exception with no nested exception,
-							// it's probably
-							// some socket or weird I/O related problem.
-							lastError = me;
-							continue;
-						}
-						// This was not a connection or I/O error particular to
-						// one
-						// SMTP server of an MX set. Instead, it is almost
-						// certainly
-						// a protocol level error. In this case we assume that
-						// this
-						// is an error we'd encounter with any of the SMTP
-						// servers
-						// associated with this MX record, and we pass the
-						// exception
-						// to the code in the outer block that determines its
-						// severity.
-						throw me;
-					}
-				} // end while
-				// If we encountered an exception while looping through,
-				// throw the last MessagingException we caught. We only
-				// do this if we were unable to send the message to any
-				// server. If sending eventually succeeded, we exit
-				// deliver() though the return at the end of the try
-				// block.
-				if (lastError != null) {
-					throw lastError;
-				}
-			} catch (SendFailedException sfe) {
-				logSendFailedException(sfe);
-
-				Collection<MailAddress> recipients = mail.getRecipients();
-
-				boolean deleteMessage = false;
-
-				/*
-				 * If you send a message that has multiple invalid addresses,
-				 * you'll get a top-level SendFailedException that that has the
-				 * valid, valid-unsent, and invalid address lists, with all of
-				 * the server response messages will be contained within the
-				 * nested exceptions. [Note: the content of the nested
-				 * exceptions is implementation dependent.]
-				 * 
-				 * sfe.getInvalidAddresses() should be considered permanent.
-				 * sfe.getValidUnsentAddresses() should be considered temporary.
-				 * 
-				 * JavaMail v1.3 properly populates those collections based upon
-				 * the 4xx and 5xx response codes to RCPT TO. Some servers, such
-				 * as Yahoo! don't respond to the RCPT TO, and provide a 5xx
-				 * reply after DATA. In that case, we will pick up the failure
-				 * from SMTPSendFailedException.
-				 */
-
-				/*
-				 * SMTPSendFailedException introduced in JavaMail 1.3.2, and
-				 * provides detailed protocol reply code for the operation
-				 */
-				try {
-					if (sfe.getClass().getName().endsWith(
-							".SMTPSendFailedException")) {
-						int returnCode = ((Integer) invokeGetter(sfe,
-								"getReturnCode")).intValue();
-						// If we got an SMTPSendFailedException, use its RetCode
-						// to determine default permanent/temporary failure
-						deleteMessage = (returnCode >= 500 && returnCode <= 599);
-					} else {
-						// Sometimes we'll get a normal SendFailedException with
-						// nested SMTPAddressFailedException, so use the latter
-						// RetCode
-						MessagingException me = sfe;
-						Exception ne;
-						while ((ne = me.getNextException()) != null
-								&& ne instanceof MessagingException) {
-							me = (MessagingException) ne;
-							if (me.getClass().getName().endsWith(
-									".SMTPAddressFailedException")) {
-								int returnCode = ((Integer) invokeGetter(me,
-										"getReturnCode")).intValue();
-								deleteMessage = (returnCode >= 500 && returnCode <= 599);
-							}
-						}
-					}
-				} catch (IllegalStateException ise) {
-					// unexpected exception (not a compatible javamail
-					// implementation)
-				} catch (ClassCastException cce) {
-					// unexpected exception (not a compatible javamail
-					// implementation)
-				}
-
-				// log the original set of intended recipients
-				if (isDebug)
-					log("Recipients: " + recipients);
-
-				if (sfe.getInvalidAddresses() != null) {
-					Address[] address = sfe.getInvalidAddresses();
-					if (address.length > 0) {
-						recipients.clear();
-						for (int i = 0; i < address.length; i++) {
-							try {
-								recipients.add(new MailAddress(address[i]
-										.toString()));
-							} catch (ParseException pe) {
-								// this should never happen ... we should have
-								// caught malformed addresses long before we
-								// got to this code.
-								log("Can't parse invalid address: "
-										+ pe.getMessage());
-							}
-						}
-						if (isDebug)
-							log("Invalid recipients: " + recipients);
-						deleteMessage = failMessage(mail, sfe, true);
-					}
-				}
-
-				if (sfe.getValidUnsentAddresses() != null) {
-					Address[] address = sfe.getValidUnsentAddresses();
-					if (address.length > 0) {
-						recipients.clear();
-						for (int i = 0; i < address.length; i++) {
-							try {
-								recipients.add(new MailAddress(address[i]
-										.toString()));
-							} catch (ParseException pe) {
-								// this should never happen ... we should have
-								// caught malformed addresses long before we
-								// got to this code.
-								log("Can't parse unsent address: "
-										+ pe.getMessage());
-							}
-						}
-						if (isDebug)
-							log("Unsent recipients: " + recipients);
-						if (sfe.getClass().getName().endsWith(
-								".SMTPSendFailedException")) {
-							int returnCode = ((Integer) invokeGetter(sfe,
-									"getReturnCode")).intValue();
-							deleteMessage = failMessage(mail, sfe,
-									returnCode >= 500 && returnCode <= 599);
-						} else {
-							deleteMessage = failMessage(mail, sfe, false);
-						}
-					}
-				}
-
-				return deleteMessage;
-			} catch (MessagingException ex) {
-				// We should do a better job checking this... if the failure is
-				// a general
-				// connect exception, this is less descriptive than more
-				// specific SMTP command
-				// failure... have to lookup and see what are the various
-				// Exception
-				// possibilities
-
-				// Unable to deliver message after numerous tries... fail
-				// accordingly
-
-				// We check whether this is a 5xx error message, which
-				// indicates a permanent failure (like account doesn't exist
-				// or mailbox is full or domain is setup wrong).
-				// We fail permanently if this was a 5xx error
-				return failMessage(mail, ex, ('5' == ex.getMessage().charAt(0)));
-			} catch (Exception ex) {
-				// Generic exception = permanent failure
-				return failMessage(mail, ex, true);
-			}
-
-			/*
-			 * If we get here, we've exhausted the loop of servers without
-			 * sending the message or throwing an exception. One case where this
-			 * might happen is if we get a MessagingException on each
-			 * transport.connect(), e.g., if there is only one server and we get
-			 * a connect exception.
-			 */
-			return failMessage(mail, new MessagingException(
-					"No mail server(s) available at this time."), false);
-		}
-
-		/**
-		 * Try to return a usefull logString created of the Exception which was
-		 * given. Return null if nothing usefull could be done
-		 * 
-		 * @param e
-		 *            The MessagingException to use
-		 * @return logString
-		 */
-		private String exceptionToLogString(Exception e) {
-			if (e.getClass().getName().endsWith(".SMTPSendFailedException")) {
-				return "RemoteHost said: " + e.getMessage();
-			} else if (e instanceof SendFailedException) {
-				SendFailedException exception = (SendFailedException) e;
-
-				// No error
-				if (exception.getInvalidAddresses().length == 0
-						&& exception.getValidUnsentAddresses().length == 0)
-					return null;
-
-				Exception ex;
-				StringBuilder sb = new StringBuilder();
-				boolean smtpExFound = false;
-				sb.append("RemoteHost said:");
-
-				if (e instanceof MessagingException)
-					while ((ex = ((MessagingException) e).getNextException()) != null
-							&& ex instanceof MessagingException) {
-						e = ex;
-						if (ex.getClass().getName().endsWith(
-								".SMTPAddressFailedException")) {
-							try {
-								InternetAddress ia = (InternetAddress) invokeGetter(
-										ex, "getAddress");
-								sb.append(" ( " + ia + " - ["
-										+ ex.getMessage().replaceAll("\\n", "")
-										+ "] )");
-								smtpExFound = true;
-							} catch (IllegalStateException ise) {
-								// Error invoking the getAddress method
-							} catch (ClassCastException cce) {
-								// The getAddress method returned something
-								// different than InternetAddress
-							}
-						}
-					}
-				if (!smtpExFound) {
-					boolean invalidAddr = false;
-					sb.append(" ( ");
-
-					if (exception.getInvalidAddresses().length > 0) {
-						sb.append(exception.getInvalidAddresses());
-						invalidAddr = true;
-					}
-					if (exception.getValidUnsentAddresses().length > 0) {
-						if (invalidAddr == true)
-							sb.append(" ");
-						sb.append(exception.getValidUnsentAddresses());
-					}
-					sb.append(" - [");
-					sb.append(exception.getMessage().replaceAll("\\n", ""));
-					sb.append("] )");
-				}
-				return sb.toString();
-			}
-			return null;
-		}
-
-		/**
-		 * Utility method used to invoke getters for javamail implementation
-		 * specific classes.
-		 * 
-		 * @param target
-		 *            the object whom method will be invoked
-		 * @param getter
-		 *            the no argument method name
-		 * @return the result object
-		 * @throws IllegalStateException
-		 *             on invocation error
-		 */
-		private Object invokeGetter(Object target, String getter) {
-			try {
-				Method getAddress = target.getClass().getMethod(getter, null);
-				return getAddress.invoke(target, null);
-			} catch (NoSuchMethodException nsme) {
-				// An SMTPAddressFailedException with no getAddress method.
-			} catch (IllegalAccessException iae) {
-			} catch (IllegalArgumentException iae) {
-			} catch (InvocationTargetException ite) {
-				// Other issues with getAddress invokation.
-			}
-			return new IllegalStateException("Exception invoking " + getter
-					+ " on a " + target.getClass() + " object");
-		}
-
-		/*
-		 * private method to log the extended SendFailedException introduced in
-		 * JavaMail 1.3.2.
-		 */
-		private void logSendFailedException(SendFailedException sfe) {
-			if (isDebug) {
-				MessagingException me = sfe;
-				if (me.getClass().getName()
-						.endsWith(".SMTPSendFailedException")) {
-					try {
-						String command = (String) invokeGetter(sfe,
-								"getCommand");
-						Integer returnCode = (Integer) invokeGetter(sfe,
-								"getReturnCode");
-						log("SMTP SEND FAILED:");
-						log(sfe.toString());
-						log("  Command: " + command);
-						log("  RetCode: " + returnCode);
-						log("  Response: " + sfe.getMessage());
-					} catch (IllegalStateException ise) {
-						// Error invoking the getAddress method
-						log("Send failed: " + me.toString());
-					} catch (ClassCastException cce) {
-						// The getAddress method returned something different
-						// than InternetAddress
-						log("Send failed: " + me.toString());
-					}
-				} else {
-					log("Send failed: " + me.toString());
-				}
-				Exception ne;
-				while ((ne = me.getNextException()) != null
-						&& ne instanceof MessagingException) {
-					me = (MessagingException) ne;
-					if (me.getClass().getName().endsWith(
-							".SMTPAddressFailedException")
-							|| me.getClass().getName().endsWith(
-									".SMTPAddressSucceededException")) {
-						try {
-							String action = me.getClass().getName().endsWith(
-									".SMTPAddressFailedException") ? "FAILED"
-									: "SUCCEEDED";
-							InternetAddress address = (InternetAddress) invokeGetter(
-									me, "getAddress");
-							String command = (String) invokeGetter(me,
-									"getCommand");
-							Integer returnCode = (Integer) invokeGetter(me,
-									"getReturnCode");
-							log("ADDRESS " + action + ":");
-							log(me.toString());
-							log("  Address: " + address);
-							log("  Command: " + command);
-							log("  RetCode: " + returnCode);
-							log("  Response: " + me.getMessage());
-						} catch (IllegalStateException ise) {
-							// Error invoking the getAddress method
-						} catch (ClassCastException cce) {
-							// A method returned something different than
-							// expected
-						}
-					}
-				}
-			}
-		}
-
-		/**
-		 * Converts a message to 7 bit.
-		 * 
-		 * @param part
-		 */
-		private void convertTo7Bit(MimePart part) throws MessagingException,
-				IOException {
-			if (part.isMimeType("multipart/*")) {
-				MimeMultipart parts = (MimeMultipart) part.getContent();
-				int count = parts.getCount();
-				for (int i = 0; i < count; i++) {
-					convertTo7Bit((MimePart) parts.getBodyPart(i));
-				}
-			} else if ("8bit".equals(part.getEncoding())) {
-				// The content may already be in encoded the form (likely with
-				// mail created from a
-				// stream). In that case, just changing the encoding to
-				// quoted-printable will mangle
-				// the result when this is transmitted. We must first convert
-				// the content into its
-				// native format, set it back, and only THEN set the transfer
-				// encoding to force the
-				// content to be encoded appropriately.
-
-				// if the part doesn't contain text it will be base64 encoded.
-				String contentTransferEncoding = part.isMimeType("text/*") ? "quoted-printable"
-						: "base64";
-				part.setContent(part.getContent(), part.getContentType());
-				part.setHeader("Content-Transfer-Encoding",
-						contentTransferEncoding);
-				part.addHeader("X-MIME-Autoconverted", "from 8bit to "
-						+ contentTransferEncoding + " by "
-						+ getMailetContext().getServerInfo());
-			}
-		}
-
-		/**
-		 * Insert the method's description here. Creation date: (2/25/00 1:14:18
-		 * AM)
-		 * 
-		 * @param mail
-		 *            org.apache.james.core.MailImpl
-		 * @param ex
-		 *            javax.mail.MessagingException
-		 * @param permanent
-		 * @return boolean Whether the message failed fully and can be deleted
-		 */
-		private boolean failMessage(Mail mail, Exception ex, boolean permanent) {
-			StringWriter sout = new StringWriter();
-			PrintWriter out = new PrintWriter(sout, true);
-			if (permanent) {
-				out.print("Permanent");
-			} else {
-				out.print("Temporary");
-			}
-
-			String exceptionLog = exceptionToLogString(ex);
-
-			StringBuilder logBuffer = new StringBuilder(64).append(
-					" exception delivering mail (").append(mail.getName());
-
-			if (exceptionLog != null) {
-				logBuffer.append(". ");
-				logBuffer.append(exceptionLog);
-			}
-
-			logBuffer.append(": ");
-			out.print(logBuffer.toString());
-			if (isDebug)
-				ex.printStackTrace(out);
-			log(sout.toString());
-			if (!permanent) {
-				if (!mail.getState().equals(Mail.ERROR)) {
-					mail.setState(Mail.ERROR);
-					mail.setErrorMessage("0");
-					mail.setLastUpdated(new Date());
-				}
-
-				int retries = 0;
-				try {
-					retries = Integer.parseInt(mail.getErrorMessage());
-				} catch (NumberFormatException e) {
-					// Something strange was happen with the errorMessage..
-				}
-
-				if (retries < maxRetries) {
-					logBuffer = new StringBuilder(128).append(
-							"Storing message ").append(mail.getName()).append(
-							" into outgoing after ").append(retries).append(
-							" retries");
-					log(logBuffer.toString());
-					++retries;
-					mail.setErrorMessage(retries + "");
-					mail.setLastUpdated(new Date());
-					return false;
-				} else {
-					logBuffer = new StringBuilder(128).append(
-							"Bouncing message ").append(mail.getName()).append(
-							" after ").append(retries).append(" retries");
-					log(logBuffer.toString());
-				}
-			}
-
-			if (mail.getSender() == null) {
-				log("Null Sender: no bounce will be generated for "
-						+ mail.getName());
-				return true;
-			}
-
-			if (bounceProcessor != null) {
-				// do the new DSN bounce
-				// setting attributes for DSN mailet
-				mail.setAttribute("delivery-error", ex);
-				mail.setState(bounceProcessor);
-				// re-insert the mail into the spool for getting it passed to
-				// the dsn-processor
-				MailetContext mc = getMailetContext();
-				try {
-					mc.sendMail(mail);
-				} catch (MessagingException e) {
-					// we shouldn't get an exception, because the mail was
-					// already processed
-					log("Exception re-inserting failed mail: ", e);
-				}
-			} else {
-				// do an old style bounce
-				bounce(mail, ex);
-			}
-			return true;
-		}
-
-		private void bounce(Mail mail, Exception ex) {
-			StringWriter sout = new StringWriter();
-			PrintWriter out = new PrintWriter(sout, true);
-			String machine = "[unknown]";
-			try {
-				machine = dnsServer.getLocalHost().getHostName();
-
-			} catch (Exception e) {
-				machine = "[address unknown]";
-			}
-			StringBuilder bounceBuffer = new StringBuilder(128).append(
-					"Hi. This is the James mail server at ").append(machine)
-					.append(".");
-			out.println(bounceBuffer.toString());
-			out
-					.println("I'm afraid I wasn't able to deliver your message to the following addresses.");
-			out
-					.println("This is a permanent error; I've given up. Sorry it didn't work out.  Below");
-			out
-					.println("I include the list of recipients and the reason why I was unable to deliver");
-			out.println("your message.");
-			out.println();
-			for (Iterator<MailAddress> i = mail.getRecipients().iterator(); i
-					.hasNext();) {
-				out.println(i.next());
-			}
-			if (ex instanceof MessagingException) {
-				if (((MessagingException) ex).getNextException() == null) {
-					out.println(ex.getMessage().trim());
-				} else {
-					Exception ex1 = ((MessagingException) ex)
-							.getNextException();
-					if (ex1 instanceof SendFailedException) {
-						out.println("Remote mail server told me: "
-								+ ex1.getMessage().trim());
-					} else if (ex1 instanceof UnknownHostException) {
-						out.println("Unknown host: " + ex1.getMessage().trim());
-						out
-								.println("This could be a DNS server error, a typo, or a problem with the recipient's mail server.");
-					} else if (ex1 instanceof ConnectException) {
-						// Already formatted as "Connection timed out: connect"
-						out.println(ex1.getMessage().trim());
-					} else if (ex1 instanceof SocketException) {
-						out.println("Socket exception: "
-								+ ex1.getMessage().trim());
-					} else {
-						out.println(ex1.getMessage().trim());
-					}
-				}
-			}
-			out.println();
-
-			log("Sending failure message " + mail.getName());
-			try {
-				getMailetContext().bounce(mail, sout.toString());
-			} catch (MessagingException me) {
-				log("Encountered unexpected messaging exception while bouncing message: "
-						+ me.getMessage());
-			} catch (Exception e) {
-				log("Encountered unexpected exception while bouncing message: "
-						+ e.getMessage());
-			}
-		}
-
-		/**
-		 * Returns the javamail Session object.
-		 * 
-		 * @param props
-		 * @param authenticator
-		 * @return
-		 */
-		protected Session obtainSession(Properties props) {
-			return Session.getInstance(props);
-		}
-
-		/**
-		 * Returns an Iterator over org.apache.mailet.HostAddress, a specialized
-		 * subclass of javax.mail.URLName, which provides location information
-		 * for servers that are specified as mail handlers for the given
-		 * hostname. If no host is found, the Iterator returned will be empty
-		 * and the first call to hasNext() will return false. The Iterator is a
-		 * nested iterator: the outer iteration is over each gateway, and the
-		 * inner iteration is over potentially multiple A records for each
-		 * gateway.
-		 * 
-		 * @see org.apache.james.DNSServer#getSMTPHostAddresses(String)
-		 * @since v2.2.0a16-unstable
-		 * @param gatewayServers
-		 *            - Collection of host[:port] Strings
-		 * @return an Iterator over HostAddress instances, sorted by priority
-		 */
-		private Iterator<HostAddress> getGatewaySMTPHostAddresses(
-				final Collection<String> gatewayServers) {
-			return new Iterator<HostAddress>() {
-				private Iterator<String> gateways = gatewayServers.iterator();
-				private Iterator<HostAddress> addresses = null;
-
-				public boolean hasNext() {
-					/*
-					 * Make sure that when next() is called, that we can provide
-					 * a HostAddress. This means that we need to have an inner
-					 * iterator, and verify that it has addresses. We could, for
-					 * example, run into a situation where the next gateway
-					 * didn't have any valid addresses.
-					 */
-					if (!hasNextAddress() && gateways.hasNext()) {
-						do {
-							String server = (String) gateways.next();
-							String port = "25";
-
-							int idx = server.indexOf(':');
-							if (idx > 0) {
-								port = server.substring(idx + 1);
-								server = server.substring(0, idx);
-							}
-
-							final String nextGateway = server;
-							final String nextGatewayPort = port;
-							try {
-								final InetAddress[] ips = dnsServer
-										.getAllByName(nextGateway);
-								addresses = new Iterator<HostAddress>() {
-									private InetAddress[] ipAddresses = ips;
-									int i = 0;
-
-									public boolean hasNext() {
-										return i < ipAddresses.length;
-									}
-
-									public HostAddress next() {
-										return new org.apache.mailet.HostAddress(
-												nextGateway,
-												"smtp://"
-														+ (ipAddresses[i++])
-																.getHostAddress()
-														+ ":" + nextGatewayPort);
-									}
-
-									public void remove() {
-										throw new UnsupportedOperationException(
-												"remove not supported by this iterator");
-									}
-								};
-							} catch (java.net.UnknownHostException uhe) {
-								log("Unknown gateway host: "
-										+ uhe.getMessage().trim());
-								log("This could be a DNS server error or configuration error.");
-							}
-						} while (!hasNextAddress() && gateways.hasNext());
-					}
-
-					return hasNextAddress();
-				}
-
-				private boolean hasNextAddress() {
-					return addresses != null && addresses.hasNext();
-				}
-
-				public HostAddress next() {
-					return (addresses != null) ? addresses.next() : null;
-				}
-
-				public void remove() {
-					throw new UnsupportedOperationException(
-							"remove not supported by this iterator");
-				}
-			};
-		}
-
-	}
-
-	/*
-	 * (non-Javadoc)
-	 * 
-	 * @see org.apache.camel.CamelContextAware#getCamelContext()
-	 */
-	public CamelContext getCamelContext() {
-		return context;
-	}
-
-	/*
-	 * (non-Javadoc)
-	 * 
-	 * @seeorg.apache.camel.CamelContextAware#setCamelContext(org.apache.camel.
-	 * CamelContext)
-	 */
-	public void setCamelContext(CamelContext context) {
-		this.context = context;
-	}
-
-	/**
-	 * RouteBuilder which builds the Camel Route for the whole RemoteDelivery
-	 * Process.
-	 * 
-	 * 
-	 * 
-	 */
-	private final class RemoteDeliveryRouteBuilder extends RouteBuilder {
+    /** The DNSService */
+    private DNSService dnsServer;
+
+    /**
+     * Static initializer.
+     * <p>
+     * Compiles pattern for processing delaytime entries.
+     * <p>
+     * Initializes MULTIPLIERS with the supported unit quantifiers
+     */
+    static {
+        try {
+            Perl5Compiler compiler = new Perl5Compiler();
+            PATTERN = compiler.compile(PATTERN_STRING, Perl5Compiler.READ_ONLY_MASK);
+        } catch (MalformedPatternException mpe) {
+            // this should not happen as the pattern string is hardcoded.
+            mpe.printStackTrace(System.err);
+        }
+    }
+
+    /** Flag to define verbose logging messages. */
+    private boolean isDebug = false;
+
+    /** List of Delay Times. Controls frequency of retry attempts. */
+    private long[] delayTimes;
+
+    /** Maximum no. of retries (Defaults to 5). */
+    private int maxRetries = 5;
+
+    /** Default number of ms to timeout on smtp delivery */
+    private long smtpTimeout = 180000;
+
+    /** If false then ANY address errors will cause the transmission to fail */
+    private boolean sendPartial = false;
+
+    /**
+     * The amount of time JavaMail will wait before giving up on a socket
+     * connect()
+     */
+    private int connectionTimeout = 60000;
+
+    /** The server(s) to send all email to */
+    private Collection<String> gatewayServer = null;
+
+    /** Auth for gateway server */
+    private String authUser = null;
+
+    /** Password for gateway server */
+    private String authPass = null;
+
+    /**
+     * JavaMail delivery socket binds to this local address. If null the
+     * JavaMail default will be used.
+     */
+    private String bindAddress = null;
+
+    /**
+     * True, if the bind configuration parameter is supplied,
+     * RemoteDeliverySocketFactory will be used in this case.
+     */
+    private boolean isBindUsed = false;
+
+    /** the processor for creating Bounces */
+    private String bounceProcessor = null;
+
+    /** Default properties for the JavaMail Session */
+    private Properties defprops = new Properties();
+
+    /** The retry count dnsProblemErrors */
+    private int dnsProblemRetry = 0;
+
+    private MailServer mailServer;
+
+    private ProducerTemplate producerTemplate;
+
+    private CamelContext context;
+
+    private String outgoingQueue;
+
+    private String outgoingRetryQueue;
+
+    @Resource(name = "producerTemplate")
+    public void setProducerTemplate(ProducerTemplate producerTemplate) {
+        this.producerTemplate = producerTemplate;
+    }
+
+    @Resource(name = "dnsserver")
+    public void setDNSService(DNSService dnsService) {
+        this.dnsServer = dnsService;
+    }
+
+    @Resource(name = "James")
+    public void setMailServer(MailServer mailServer) {
+        this.mailServer = mailServer;
+    }
+
+    /**
+     * Initializes all arguments based on configuration values specified in the
+     * James configuration file.
+     * 
+     * @throws MessagingException
+     *             on failure to initialize attributes.
+     */
+    public void init() throws MessagingException {
+        // Set isDebug flag.
+        isDebug = (getInitParameter("debug") == null) ? false : new Boolean(getInitParameter("debug")).booleanValue();
+
+        // Create list of Delay Times.
+        ArrayList<Delay> delayTimesList = new ArrayList<Delay>();
+        try {
+            if (getInitParameter("delayTime") != null) {
+
+                // parses delayTimes specified in config file.
+                final Perl5Matcher delayTimeMatcher = new Perl5Matcher();
+                String delayTimesParm = getInitParameter("delayTime");
+
+                // Split on commas
+                StringTokenizer st = new StringTokenizer(delayTimesParm, ",");
+                while (st.hasMoreTokens()) {
+                    String delayTime = st.nextToken();
+                    delayTimesList.add(new Delay(delayTimeMatcher, delayTime));
+                }
+            } else {
+                // Use default delayTime.
+                delayTimesList.add(new Delay());
+            }
+        } catch (Exception e) {
+            log("Invalid delayTime setting: " + getInitParameter("delayTime"));
+        }
+
+        try {
+            // Get No. of Max Retries.
+            if (getInitParameter("maxRetries") != null) {
+                maxRetries = Integer.parseInt(getInitParameter("maxRetries"));
+            }
+
+            // Check consistency of 'maxRetries' with delayTimesList attempts.
+            int totalAttempts = calcTotalAttempts(delayTimesList);
+
+            // If inconsistency found, fix it.
+            if (totalAttempts > maxRetries) {
+                log("Total number of delayTime attempts exceeds maxRetries specified. " + " Increasing maxRetries from " + maxRetries + " to " + totalAttempts);
+                maxRetries = totalAttempts;
+            } else {
+                int extra = maxRetries - totalAttempts;
+                if (extra != 0) {
+                    log("maxRetries is larger than total number of attempts specified.  " + "Increasing last delayTime with " + extra + " attempts ");
+
+                    // Add extra attempts to the last delayTime.
+                    if (delayTimesList.size() != 0) {
+                        // Get the last delayTime.
+                        Delay delay = (Delay) delayTimesList.get(delayTimesList.size() - 1);
+
+                        // Increase no. of attempts.
+                        delay.setAttempts(delay.getAttempts() + extra);
+                        log("Delay of " + delay.getDelayTime() + " msecs is now attempted: " + delay.getAttempts() + " times");
+                    } else {
+                        throw new MessagingException("No delaytimes, cannot continue");
+                    }
+                }
+            }
+            delayTimes = expandDelays(delayTimesList);
+
+        } catch (Exception e) {
+            log("Invalid maxRetries setting: " + getInitParameter("maxRetries"));
+        }
+
+        outgoingQueue = getInitParameter("outgoingQueue");
+        if (outgoingQueue == null) {
+            outgoingQueue = "outgoing";
+        }
+
+        outgoingRetryQueue = getInitParameter("outgoingRetryQueue");
+        if (outgoingRetryQueue == null) {
+            outgoingRetryQueue = "outgoing.retry";
+        }
+
+        try {
+            if (getInitParameter("timeout") != null) {
+                smtpTimeout = Integer.parseInt(getInitParameter("timeout"));
+            }
+        } catch (Exception e) {
+            log("Invalid timeout setting: " + getInitParameter("timeout"));
+        }
+
+        try {
+            if (getInitParameter("connectiontimeout") != null) {
+                connectionTimeout = Integer.parseInt(getInitParameter("connectiontimeout"));
+            }
+        } catch (Exception e) {
+            log("Invalid timeout setting: " + getInitParameter("timeout"));
+        }
+
+        sendPartial = (getInitParameter("sendpartial") == null) ? false : new Boolean(getInitParameter("sendpartial")).booleanValue();
+
+        bounceProcessor = getInitParameter("bounceProcessor");
+
+        String gateway = getInitParameter("gateway");
+        String gatewayPort = getInitParameter("gatewayPort");
+
+        if (gateway != null) {
+            gatewayServer = new ArrayList<String>();
+            StringTokenizer st = new StringTokenizer(gateway, ",");
+            while (st.hasMoreTokens()) {
+                String server = st.nextToken().trim();
+                if (server.indexOf(':') < 0 && gatewayPort != null) {
+                    server += ":";
+                    server += gatewayPort;
+                }
+
+                if (isDebug)
+                    log("Adding SMTP gateway: " + server);
+                gatewayServer.add(server);
+            }
+            authUser = getInitParameter("gatewayUsername");
+            // backward compatibility with 2.3.x
+            if (authUser == null) {
+                authUser = getInitParameter("gatewayusername");
+            }
+            authPass = getInitParameter("gatewayPassword");
+        }
+
+        bindAddress = getInitParameter("bind");
+        isBindUsed = bindAddress != null;
+        try {
+            if (isBindUsed)
+                RemoteDeliverySocketFactory.setBindAdress(bindAddress);
+        } catch (UnknownHostException e) {
+            log("Invalid bind setting (" + bindAddress + "): " + e.toString());
+        }
+
+        // deal with <mail.*> attributes, passing them to javamail
+        Iterator<String> i = getInitParameterNames();
+        while (i.hasNext()) {
+            String name = i.next();
+            if (name.startsWith("mail.")) {
+                defprops.put(name, getInitParameter(name));
+            }
+
+        }
+
+        String dnsRetry = getInitParameter("maxDnsProblemRetries");
+        if (dnsRetry != null && !dnsRetry.equals("")) {
+            dnsProblemRetry = Integer.parseInt(dnsRetry);
+        }
+
+        try {
+            getCamelContext().addRoutes(new RemoteDeliveryRouteBuilder());
+        } catch (Exception e) {
+            throw new MessagingException("Unable to add camel route");
+        }
+    }
+
+    /**
+     * Calculates Total no. of attempts for the specified delayList.
+     * 
+     * @param delayList
+     *            list of 'Delay' objects
+     * @return total no. of retry attempts
+     */
+    private int calcTotalAttempts(ArrayList<Delay> delayList) {
+        int sum = 0;
+        Iterator<Delay> i = delayList.iterator();
+        while (i.hasNext()) {
+            Delay delay = i.next();
+            sum += delay.getAttempts();
+        }
+        return sum;
+    }
+
+    /**
+     * This method expands an ArrayList containing Delay objects into an array
+     * holding the only delaytime in the order.
+     * <p>
+     * So if the list has 2 Delay objects the first having attempts=2 and
+     * delaytime 4000 the second having attempts=1 and delaytime=300000 will be
+     * expanded into this array:
+     * <p>
+     * long[0] = 4000
+     * <p>
+     * long[1] = 4000
+     * <p>
+     * long[2] = 300000
+     * <p>
+     * 
+     * @param list
+     *            the list to expand
+     * @return the expanded list
+     **/
+    private long[] expandDelays(ArrayList<Delay> list) {
+        long[] delays = new long[calcTotalAttempts(list)];
+        Iterator<Delay> i = list.iterator();
+        int idx = 0;
+        while (i.hasNext()) {
+            Delay delay = i.next();
+            for (int j = 0; j < delay.getAttempts(); j++) {
+                delays[idx++] = delay.getDelayTime();
+            }
+        }
+        return delays;
+    }
+
+    /**
+     * This method returns, given a retry-count, the next delay time to use.
+     * 
+     * @param retry_count
+     *            the current retry_count.
+     * @return the next delay time to use, given the retry count
+     **/
+    private long getNextDelay(int retry_count) {
+        if (retry_count > delayTimes.length) {
+            return DEFAULT_DELAY_TIME;
+        }
+        return delayTimes[retry_count - 1];
+    }
+
+    /**
+     * This class is used to hold a delay time and its corresponding number of
+     * retries.
+     **/
+    private final static class Delay {
+        private int attempts = 1;
+
+        private long delayTime = DEFAULT_DELAY_TIME;
+
+        /**
+         * This constructor expects Strings of the form
+         * "[attempt\*]delaytime[unit]".
+         * <p>
+         * The optional attempt is the number of tries this delay should be used
+         * (default = 1). The unit, if present, must be one of
+         * (msec,sec,minute,hour,day). The default value of unit is 'msec'.
+         * <p>
+         * The constructor multiplies the delaytime by the relevant multiplier
+         * for the unit, so the delayTime instance variable is always in msec.
+         * 
+         * @param initString
+         *            the string to initialize this Delay object from
+         **/
+        public Delay(final Perl5Matcher delayTimeMatcher, String initString) throws MessagingException {
+            // Default unit value to 'msec'.
+            String unit = "msec";
+
+            if (delayTimeMatcher.matches(initString, PATTERN)) {
+                MatchResult res = delayTimeMatcher.getMatch();
+
+                // The capturing groups will now hold:
+                // at 1: attempts * (if present)
+                // at 2: delaytime
+                // at 3: unit (if present)
+                if (res.group(1) != null && !res.group(1).equals("")) {
+                    // We have an attempt *
+                    String attemptMatch = res.group(1);
+
+                    // Strip the * and whitespace.
+                    attemptMatch = attemptMatch.substring(0, attemptMatch.length() - 1).trim();
+                    attempts = Integer.parseInt(attemptMatch);
+                }
+
+                delayTime = Long.parseLong(res.group(2));
+
+                if (!res.group(3).equals("")) {
+                    // We have a value for 'unit'.
+                    unit = res.group(3).toLowerCase(Locale.US);
+                }
+            } else {
+                throw new MessagingException(initString + " does not match " + PATTERN_STRING);
+            }
+
+            // calculate delayTime.
+            try {
+                delayTime = TimeConverter.getMilliSeconds(delayTime, unit);
+            } catch (NumberFormatException e) {
+                throw new MessagingException(e.getMessage());
+            }
+        }
+
+        /**
+         * This constructor makes a default Delay object with attempts = 1 and
+         * delayTime = DEFAULT_DELAY_TIME.
+         **/
+        public Delay() {
+        }
+
+        /**
+         * @return the delayTime for this Delay
+         **/
+        public long getDelayTime() {
+            return delayTime;
+        }
+
+        /**
+         * @return the number attempts this Delay should be used.
+         **/
+        public int getAttempts() {
+            return attempts;
+        }
+
+        /**
+         * Set the number attempts this Delay should be used.
+         **/
+        public void setAttempts(int value) {
+            attempts = value;
+        }
+
+        /**
+         * Pretty prints this Delay
+         **/
+        public String toString() {
+            String message = getAttempts() + "*" + getDelayTime() + "msecs";
+            return message;
+        }
+    }
+
+    public String getMailetInfo() {
+        return "RemoteDelivery Mailet";
+    }
+
+    /**
+     * For this message, we take the list of recipients, organize these into
+     * distinct servers, and duplicate the message for each of these servers,
+     * and then call the deliver (messagecontainer) method for each
+     * server-specific messagecontainer ... that will handle storing it in the
+     * outgoing queue if needed.
+     * 
+     * @param mail
+     *            org.apache.mailet.Mail
+     */
+    public void service(Mail mail) throws MessagingException {
+        // Do I want to give the internal key, or the message's Message ID
+        if (isDebug) {
+            log("Remotely delivering mail " + mail.getName());
+        }
+        Collection<MailAddress> recipients = mail.getRecipients();
+
+        if (gatewayServer == null) {
+            // Must first organize the recipients into distinct servers (name
+            // made case insensitive)
+            Hashtable<String, Collection<MailAddress>> targets = new Hashtable<String, Collection<MailAddress>>();
+            for (Iterator<MailAddress> i = recipients.iterator(); i.hasNext();) {
+                MailAddress target = i.next();
+                String targetServer = target.getDomain().toLowerCase(Locale.US);
+                Collection<MailAddress> temp = targets.get(targetServer);
+                if (temp == null) {
+                    temp = new ArrayList<MailAddress>();
+                    targets.put(targetServer, temp);
+                }
+                temp.add(target);
+            }
+
+            // We have the recipients organized into distinct servers... put
+            // them into the
+            // delivery store organized like this... this is ultra inefficient I
+            // think...
+
+            // Store the new message containers, organized by server, in the
+            // outgoing mail repository
+            String name = mail.getName();
+            for (Iterator<String> i = targets.keySet().iterator(); i.hasNext();) {
+                String host = i.next();
+                Collection<MailAddress> rec = targets.get(host);
+                if (isDebug) {
+                    StringBuilder logMessageBuffer = new StringBuilder(128).append("Sending mail to ").append(rec).append(" on host ").append(host);
+                    log(logMessageBuffer.toString());
+                }
+                Mail m = new InMemoryMail(mail);
+                m.setRecipients(rec);
+                StringBuilder nameBuffer = new StringBuilder(128).append(name).append("-to-").append(host);
+                m.setName(nameBuffer.toString());
+
+                producerTemplate.sendBody("activemq:queue:" + outgoingQueue, m);
+                // workRepository.store(mail);
+                // Set it to try to deliver (in a separate thread) immediately
+                // (triggered by storage)
+            }
+        } else {
+            // Store the mail unaltered for processing by the gateway server(s)
+            if (isDebug) {
+                StringBuilder logMessageBuffer = new StringBuilder(128).append("Sending mail to ").append(mail.getRecipients()).append(" via ").append(gatewayServer);
+                log(logMessageBuffer.toString());
+            }
+
+            producerTemplate.sendBody("activemq:queue:" + outgoingQueue, new InMemoryMail(mail));
+
+            // Set it to try to deliver (in a separate thread) immediately
+            // (triggered by storage)
+            // workRepository.store(mail);
+        }
+        mail.setState(Mail.GHOST);
+    }
+
+    /**
+     * Processor which handles the delivery of messages. If the delivery should
+     * get done again in the future it will set the
+     * {@link JamesCamelConstants.JAMES_RETRY_DELIVERY} header to true. The next
+     * delivery time is set in milliseconds in the
+     * {@link JamesCamelConstants.JAMES_NEXT_DELIVERY} header
+     * 
+     * 
+     */
+    public final class DeliveryProcessor implements Processor {
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see org.apache.camel.Processor#process(org.apache.camel.Exchange)
+         */
+        public void process(Exchange arg0) throws Exception {
+
+            Message inMessage = arg0.getIn();
+            // Checks the pool and delivers a mail message
+            Properties props = new Properties();
+            // Not needed for production environment
+            props.put("mail.debug", "false");
+            // Reactivated: javamail 1.3.2 should no more have problems with
+            // "250 OK"
+            // messages (WAS "false": Prevents problems encountered with 250 OK
+            // Messages)
+            props.put("mail.smtp.ehlo", "true");
+            // By setting this property to true the transport is allowed to
+            // send 8 bit data to the server (if it supports the 8bitmime
+            // extension).
+            // 2006/03/01 reverted to false because of a javamail bug converting
+            // to 8bit
+            // messages created by an inputstream.
+            props.setProperty("mail.smtp.allow8bitmime", "true");
+            // Sets timeout on going connections
+            props.put("mail.smtp.timeout", smtpTimeout + "");
+
+            props.put("mail.smtp.connectiontimeout", connectionTimeout + "");
+            props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
+
+            // Set the hostname we'll use as this server
+            props.put("mail.smtp.localhost", mailServer.getHelloName());
+
+            if (isBindUsed) {
+                // undocumented JavaMail 1.2 feature, smtp transport will use
+                // our socket factory, which will also set the local address
+                props.put("mail.smtp.socketFactory.class", RemoteDeliverySocketFactory.class.getClass());
+                // Don't fallback to the standard socket factory on error, do
+                // throw an exception
+                props.put("mail.smtp.socketFactory.fallback", "false");
+            }
+
+            if (authUser != null) {
+                props.put("mail.smtp.auth", "true");
+            }
+
+            props.putAll(defprops);
+
+            Session session = obtainSession(props);
+
+            Mail mail = inMessage.getBody(Mail.class);
+            String key = mail.getName();
+            if (isDebug) {
+                String message = Thread.currentThread().getName() + " will process mail " + key;
+                log(message);
+            }
+
+            // Deliver message
+            if (deliver(mail, session)) {
+                inMessage.removeHeader(JamesCamelConstants.JAMES_RETRY_DELIVERY);
+
+                // Message was successfully delivered/fully failed...
+                // delete it
+                // LifecycleUtil.dispose(mail);
+                // workRepository.remove(key);
+            } else {
+
+                // Test the time...
+                int retries = 0;
+
+                try {
+                    retries = Integer.parseInt(mail.getErrorMessage());
+                } catch (NumberFormatException e) {
+                    // Something strange was happen with the errorMessage..
+                    // Ignore the Exception and try
+                    // to deliver it now!
+                }
+
+                long delay = getNextDelay(retries);
+                long timeToProcess = delay + mail.getLastUpdated().getTime();
+
+                inMessage.setHeader(JamesCamelConstants.JAMES_RETRY_DELIVERY, true);
+                inMessage.setHeader(JamesCamelConstants.JAMES_NEXT_DELIVERY, timeToProcess);
+            }
+
+        }
+
+        /**
+         * We can assume that the recipients of this message are all going to
+         * the same mail server. We will now rely on the DNS server to do DNS MX
+         * record lookup and try to deliver to the multiple mail servers. If it
+         * fails, it should throw an exception.
+         * 
+         * Creation date: (2/24/00 11:25:00 PM)
+         * 
+         * @param mail
+         *            org.apache.james.core.MailImpl
+         * @param session
+         *            javax.mail.Session
+         * @return boolean Whether the delivery was successful and the message
+         *         can be deleted
+         */
+        private boolean deliver(Mail mail, Session session) {
+            try {
+                if (isDebug) {
+                    log("Attempting to deliver " + mail.getName());
+                }
+                MimeMessage message = mail.getMessage();
+
+                // Create an array of the recipients as InternetAddress objects
+                Collection<MailAddress> recipients = mail.getRecipients();
+                InternetAddress addr[] = new InternetAddress[recipients.size()];
+                int j = 0;
+                for (Iterator<MailAddress> i = recipients.iterator(); i.hasNext(); j++) {
+                    MailAddress rcpt = i.next();
+                    addr[j] = rcpt.toInternetAddress();
+                }
+
+                if (addr.length <= 0) {
+                    log("No recipients specified... not sure how this could have happened.");
+                    return true;
+                }
+
+                // Figure out which servers to try to send to. This collection
+                // will hold all the possible target servers
+                Iterator<HostAddress> targetServers = null;
+                if (gatewayServer == null) {
+                    MailAddress rcpt = (MailAddress) recipients.iterator().next();
+                    String host = rcpt.getDomain();
+
+                    // Lookup the possible targets
+                    try {
+                        targetServers = dnsServer.getSMTPHostAddresses(host);
+                    } catch (TemporaryResolutionException e) {
+                        log("Temporary problem looking up mail server for host: " + host);
+                        StringBuilder exceptionBuffer = new StringBuilder(128).append("Temporary problem looking up mail server for host: ").append(host).append(".  I cannot determine where to send this message.");
+
+                        // temporary problems
+                        return failMessage(mail, new MessagingException(exceptionBuffer.toString()), false);
+                    }
+                    if (!targetServers.hasNext()) {
+                        log("No mail server found for: " + host);
+                        StringBuilder exceptionBuffer = new StringBuilder(128).append("There are no DNS entries for the hostname ").append(host).append(".  I cannot determine where to send this message.");
+
+                        int retry = 0;
+                        try {
+                            retry = Integer.parseInt(mail.getErrorMessage());
+                        } catch (NumberFormatException e) {
+                            // Unable to parse retryCount
+                        }
+                        if (retry == 0 || retry > dnsProblemRetry) {
+                            // The domain has no dns entry.. Return a permanent
+                            // error
+                            return failMessage(mail, new MessagingException(exceptionBuffer.toString()), true);
+                        } else {
+                            return failMessage(mail, new MessagingException(exceptionBuffer.toString()), false);
+                        }
+                    }
+                } else {
+                    targetServers = getGatewaySMTPHostAddresses(gatewayServer);
+                }
+
+                MessagingException lastError = null;
+
+                while (targetServers.hasNext()) {
+                    try {
+                        HostAddress outgoingMailServer = targetServers.next();
+                        StringBuilder logMessageBuffer = new StringBuilder(256).append("Attempting delivery of ").append(mail.getName()).append(" to host ").append(outgoingMailServer.getHostName()).append(" at ").append(outgoingMailServer.getHost()).append(" for addresses ").append(
+                                Arrays.asList(addr));
+                        log(logMessageBuffer.toString());
+
+                        Properties props = session.getProperties();
+                        if (mail.getSender() == null) {
+                            props.put("mail.smtp.from", "<>");
+                        } else {
+                            String sender = mail.getSender().toString();
+                            props.put("mail.smtp.from", sender);
+                        }
+
+                        // Many of these properties are only in later JavaMail
+                        // versions
+                        // "mail.smtp.ehlo" //default true
+                        // "mail.smtp.auth" //default false
+                        // "mail.smtp.dsn.ret" //default to nothing... appended
+                        // as RET= after MAIL FROM line.
+                        // "mail.smtp.dsn.notify" //default to
+                        // nothing...appended as NOTIFY= after RCPT TO line.
+
+                        Transport transport = null;
+                        try {
+                            transport = session.getTransport(outgoingMailServer);
+                            try {
+                                if (authUser != null) {
+                                    transport.connect(outgoingMailServer.getHostName(), authUser, authPass);
+                                } else {
+                                    transport.connect();
+                                }
+                            } catch (MessagingException me) {
+                                // Any error on connect should cause the mailet
+                                // to attempt
+                                // to connect to the next SMTP server associated
+                                // with this
+                                // MX record. Just log the exception. We'll
+                                // worry about
+                                // failing the message at the end of the loop.
+                                log(me.getMessage());
+                                continue;
+                            }
+                            // if the transport is a SMTPTransport (from sun)
+                            // some
+                            // performance enhancement can be done.
+                            if (transport.getClass().getName().endsWith(".SMTPTransport")) {
+                                boolean supports8bitmime = false;
+                                try {
+                                    Method supportsExtension = transport.getClass().getMethod("supportsExtension", new Class[] { String.class });
+                                    supports8bitmime = ((Boolean) supportsExtension.invoke(transport, new Object[] { "8BITMIME" })).booleanValue();
+                                } catch (NoSuchMethodException nsme) {
+                                    // An SMTPAddressFailedException with no
+                                    // getAddress method.
+                                } catch (IllegalAccessException iae) {
+                                } catch (IllegalArgumentException iae) {
+                                } catch (InvocationTargetException ite) {
+                                    // Other issues with getAddress invokation.
+                                }
+
+                                // if the message is alredy 8bit or binary and
+                                // the
+                                // server doesn't support the 8bit extension it
+                                // has
+                                // to be converted to 7bit. Javamail api doesn't
+                                // perform
+                                // that conversion, but it is required to be a
+                                // rfc-compliant smtp server.
+
+                                // Temporarily disabled. See JAMES-638
+                                if (!supports8bitmime) {
+                                    try {
+                                        convertTo7Bit(message);
+                                    } catch (IOException e) {
+                                        // An error has occured during the 7bit
+                                        // conversion.
+                                        // The error is logged and the message
+                                        // is sent anyway.
+
+                                        log("Error during the conversion to 7 bit.", e);
+                                    }
+                                }
+                            } else {
+                                // If the transport is not the one
+                                // developed by Sun we are not sure of how it
+                                // handles the 8 bit mime stuff,
+                                // so I convert the message to 7bit.
+                                try {
+                                    convertTo7Bit(message);
+                                } catch (IOException e) {
+                                    log("Error during the conversion to 7 bit.", e);
+                                }
+                            }
+                            transport.sendMessage(message, addr);
+                        } finally {
+                            if (transport != null) {
+                                try {
+                                    // James-899: transport.close() sends QUIT
+                                    // to the server; if that fails
+                                    // (e.g. because the server has already
+                                    // closed the connection) the message
+                                    // should be considered to be delivered
+                                    // because the error happened outside
+                                    // of the mail transaction (MAIL, RCPT,
+                                    // DATA).
+                                    transport.close();
+                                } catch (MessagingException e) {
+                                    log("Warning: could not close the SMTP transport after sending mail (" + mail.getName() + ") to " + outgoingMailServer.getHostName() + " at " + outgoingMailServer.getHost() + " for " + mail.getRecipients() + "; probably the server has already closed the "
+                                            + "connection. Message is considered to be delivered. Exception: " + e.getMessage());
+                                }
+                                transport = null;
+                            }
+                        }
+                        logMessageBuffer = new StringBuilder(256).append("Mail (").append(mail.getName()).append(") sent successfully to ").append(outgoingMailServer.getHostName()).append(" at ").append(outgoingMailServer.getHost()).append(" for ").append(mail.getRecipients());
+                        log(logMessageBuffer.toString());
+                        return true;
+                    } catch (SendFailedException sfe) {
+                        logSendFailedException(sfe);
+
+                        if (sfe.getValidSentAddresses() != null) {
+                            Address[] validSent = sfe.getValidSentAddresses();
+                            if (validSent.length > 0) {
+                                StringBuilder logMessageBuffer = new StringBuilder(256).append("Mail (").append(mail.getName()).append(") sent successfully for ").append(Arrays.asList(validSent));
+                                log(logMessageBuffer.toString());
+                            }
+                        }
+
+                        /*
+                         * SMTPSendFailedException introduced in JavaMail 1.3.2,
+                         * and provides detailed protocol reply code for the
+                         * operation
+                         */
+                        if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
+                            try {
+                                int returnCode = ((Integer) invokeGetter(sfe, "getReturnCode")).intValue();
+                                // if 5xx, terminate this delivery attempt by
+                                // re-throwing the exception.
+                                if (returnCode >= 500 && returnCode <= 599)
+                                    throw sfe;

[... 724 lines stripped ...]


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org