http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java deleted file mode 100644 index 8d48757..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.mail.MessagingException; - -import org.apache.james.util.TimeConverter; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.MoreObjects; -import com.google.common.base.Objects; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; - -public class Delay { - /** - * <p> The optional attempt is the number of tries this delay should be used (default = 1). - * The delayTime is parsed by {@link TimeConverter}</p> - * - * @param initString the string to initialize this Delay object from. It has the form "[attempt\*]delaytime[unit]" - */ - public static Delay from(String initString) throws MessagingException { - if (Strings.isNullOrEmpty(initString)) { - throw new NumberFormatException("Null or Empty strings are not permitted"); - } - List<String> parts = Splitter.on('*').trimResults().splitToList(initString); - - if (parts.size() == 1) { - return new Delay(DEFAULT_ATTEMPTS, TimeConverter.getMilliSeconds(parts.get(0))); - } - if (parts.size() == 2) { - int attempts = Integer.parseInt(parts.get(0)); - if (attempts < 0) { - throw new MessagingException("Number of attempts negative in " + initString); - } - return new Delay(attempts, TimeConverter.getMilliSeconds(parts.get(1))); - } - throw new MessagingException(initString + " contains too much parts"); - } - - public static final long DEFAULT_DELAY_TIME = TimeUnit.HOURS.toMillis(6); - public static final int DEFAULT_ATTEMPTS = 1; - - private final int attempts; - private final long delayTimeInMs; - - public Delay() { - this(DEFAULT_ATTEMPTS, DEFAULT_DELAY_TIME); - } - - @VisibleForTesting - Delay(int attempts, long delayTime) { - this.attempts = attempts; - this.delayTimeInMs = delayTime; - } - - public long getDelayTimeInMs() { - return delayTimeInMs; - } - - public int getAttempts() { - return attempts; - } - - public List<Long> getExpendendDelays() { - return Repeat.repeat(delayTimeInMs, attempts); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("attempts", attempts) - .add("delayTime", delayTimeInMs) - .toString(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof Delay) { - Delay that = (Delay) o; - - return Objects.equal(this.attempts, that.attempts) - && Objects.equal(this.delayTimeInMs, that.delayTimeInMs); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hashCode(attempts, delayTimeInMs); - } -}
http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java deleted file mode 100644 index e2b43ba..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.List; - -import javax.mail.MessagingException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - -public class DelaysAndMaxRetry { - - private static final Logger LOGGER = LoggerFactory.getLogger(DelaysAndMaxRetry.class); - - public static DelaysAndMaxRetry defaults() { - return new DelaysAndMaxRetry(RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY, Repeat.repeat(new Delay(), RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY)); - } - - public static DelaysAndMaxRetry from(int intendedMaxRetries, String delaysAsString) throws MessagingException { - List<Delay> delayTimesList = createDelayList(delaysAsString); - int totalAttempts = computeTotalAttempts(delayTimesList); - return getDelaysAndMaxRetry(intendedMaxRetries, totalAttempts, delayTimesList); - } - - private static DelaysAndMaxRetry getDelaysAndMaxRetry(int intendedMaxRetries, int totalAttempts, List<Delay> delayTimesList) throws MessagingException { - if (totalAttempts > intendedMaxRetries) { - LOGGER.warn("Total number of delayTime attempts exceeds maxRetries specified. Increasing maxRetries from {} to {}", intendedMaxRetries, totalAttempts); - return new DelaysAndMaxRetry(totalAttempts, delayTimesList); - } else { - int extra = intendedMaxRetries - totalAttempts; - if (extra > 0) { - LOGGER.warn("maxRetries is larger than total number of attempts specified. Increasing last delayTime with {} attempts ", extra); - return addExtraAttemptToLastDelay(intendedMaxRetries, extra, delayTimesList); - } - return new DelaysAndMaxRetry(intendedMaxRetries, delayTimesList); - } - } - - private static DelaysAndMaxRetry addExtraAttemptToLastDelay(int intendedMaxRetries, int extra, List<Delay> delayTimesList) throws MessagingException { - if (delayTimesList.size() != 0) { - Delay lastDelay = delayTimesList.get(delayTimesList.size() - 1); - LOGGER.warn("Delay of {} msecs is now attempted: {} times", lastDelay.getDelayTimeInMs(), lastDelay.getAttempts()); - return new DelaysAndMaxRetry(intendedMaxRetries, - ImmutableList.copyOf( - Iterables.concat( - Iterables.limit(delayTimesList, delayTimesList.size() - 1), - ImmutableList.of(new Delay(lastDelay.getAttempts() + extra, lastDelay.getDelayTimeInMs()))))); - } else { - throw new MessagingException("No delaytimes, cannot continue"); - } - } - - private static List<Delay> createDelayList(String delaysAsString) { - if (delaysAsString == null) { - // Use default delayTime. - return ImmutableList.of(new Delay()); - } - - List<String> delayStrings = Splitter.on(',') - .omitEmptyStrings() - .trimResults() - .splitToList(delaysAsString); - - ImmutableList.Builder<Delay> builder = ImmutableList.builder(); - try { - for (String s : delayStrings) { - builder.add(Delay.from(s)); - } - return builder.build(); - } catch (Exception e) { - LOGGER.warn("Invalid delayTime setting: {}", delaysAsString); - return builder.build(); - } - } - - private static int computeTotalAttempts(List<Delay> delayList) { - return delayList.stream() - .mapToInt(Delay::getAttempts) - .sum(); - } - - private final int maxRetries; - private final List<Delay> delays; - - @VisibleForTesting - DelaysAndMaxRetry(int maxRetries, List<Delay> delays) { - this.maxRetries = maxRetries; - this.delays = ImmutableList.copyOf(delays); - } - - public int getMaxRetries() { - return maxRetries; - } - - /** - * <p> - * This method expands an ArrayList containing Delay objects into an array - * holding the only delaytime in the order. - * </p> - * <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/> - * <pre> - * long[0] = 4000 - * long[1] = 4000 - * long[2] = 300000 - * </pre> - * - * @param list the list to expand - * @return the expanded list - */ - public List<Long> getExpandedDelays() { - ImmutableList.Builder<Long> builder = ImmutableList.builder(); - for (Delay delay: delays) { - builder.addAll(delay.getExpendendDelays()); - } - return builder.build(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof DelaysAndMaxRetry) { - DelaysAndMaxRetry that = (DelaysAndMaxRetry) o; - return Objects.equal(this.maxRetries, that.maxRetries) - && Objects.equal(this.delays, that.delays); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hashCode(maxRetries, delays); - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java deleted file mode 100644 index 81e5c8c..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.io.Serializable; - -import org.apache.mailet.Mail; - -public class DeliveryRetriesHelper { - - public static final String DELIVERY_RETRY_COUNT = "delivery_retry_count"; - - public static int retrieveRetries(Mail mail) { - try { - Serializable value = mail.getAttribute(DELIVERY_RETRY_COUNT); - if (value != null) { - return (Integer) value; - } - return 0; - } catch (ClassCastException e) { - return 0; - } - } - - public static void initRetries(Mail mail) { - mail.setAttribute(DELIVERY_RETRY_COUNT, 0); - } - - public static void incrementRetries(Mail mail) { - mail.setAttribute(DELIVERY_RETRY_COUNT, retrieveRetries(mail) + 1); - } - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java deleted file mode 100644 index b7115a0..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; - -import org.apache.james.dnsservice.api.DNSService; -import org.apache.james.lifecycle.api.LifecycleUtil; -import org.apache.james.metrics.api.Metric; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.apache.james.queue.api.MailPrioritySupport; -import org.apache.james.queue.api.MailQueue; -import org.apache.mailet.Mail; -import org.apache.mailet.MailetContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; - -public class DeliveryRunnable implements Runnable { - private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryRunnable.class); - - public static final Supplier<Date> CURRENT_DATE_SUPPLIER = Date::new; - public static final AtomicBoolean DEFAULT_NOT_STARTED = new AtomicBoolean(false); - private static final String OUTGOING_MAILS = "outgoingMails"; - public static final String REMOTE_DELIVERY_TRIAL = "RemoteDeliveryTrial"; - - private final MailQueue queue; - private final RemoteDeliveryConfiguration configuration; - private final Metric outgoingMailsMetric; - private final MetricFactory metricFactory; - private final Bouncer bouncer; - private final MailDelivrer mailDelivrer; - private final AtomicBoolean isDestroyed; - private final Supplier<Date> dateSupplier; - - public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, MetricFactory metricFactory, - MailetContext mailetContext, Bouncer bouncer, AtomicBoolean isDestroyed) { - this(queue, configuration, metricFactory, bouncer, - new MailDelivrer(configuration, new MailDelivrerToHost(configuration, mailetContext), dnsServer, bouncer), - isDestroyed, CURRENT_DATE_SUPPLIER); - } - - @VisibleForTesting - DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, MetricFactory metricFactory, Bouncer bouncer, - MailDelivrer mailDelivrer, AtomicBoolean isDestroyeds, Supplier<Date> dateSupplier) { - this.queue = queue; - this.configuration = configuration; - this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS); - this.bouncer = bouncer; - this.mailDelivrer = mailDelivrer; - this.isDestroyed = isDestroyeds; - this.dateSupplier = dateSupplier; - this.metricFactory = metricFactory; - } - - @Override - public void run() { - try { - while (!Thread.interrupted() && !isDestroyed.get()) { - runStep(); - } - } finally { - // Restore the thread state to non-interrupted. - Thread.interrupted(); - } - } - - private void runStep() { - TimeMetric timeMetric = null; - try { - // Get the 'mail' object that is ready for deliverying. If no message is - // ready, the 'accept' will block until message is ready. - // The amount of time to block is determined by the 'getWaitTime' method of the MultipleDelayFilter. - MailQueue.MailQueueItem queueItem = queue.deQueue(); - timeMetric = metricFactory.timer(REMOTE_DELIVERY_TRIAL); - Mail mail = queueItem.getMail(); - - try { - if (configuration.isDebug()) { - LOGGER.debug("{} will process mail {}", Thread.currentThread().getName(), mail.getName()); - } - attemptDelivery(mail); - LifecycleUtil.dispose(mail); - mail = null; - queueItem.done(true); - } catch (Exception e) { - // Prevent unexpected exceptions from causing looping by removing message from outgoing. - // DO NOT CHANGE THIS to catch Error! - // For example, if there were an OutOfMemory condition caused because - // something else in the server was abusing memory, we would not want to start purging the retrying spool! - LOGGER.error("Exception caught in RemoteDelivery.run()", e); - LifecycleUtil.dispose(mail); - queueItem.done(false); - } - - } catch (Throwable e) { - if (!isDestroyed.get()) { - LOGGER.error("Exception caught in RemoteDelivery.run()", e); - } - } finally { - if (timeMetric != null) { - timeMetric.stopAndPublish(); - } - } - } - - @VisibleForTesting - void attemptDelivery(Mail mail) throws MailQueue.MailQueueException { - ExecutionResult executionResult = mailDelivrer.deliver(mail); - switch (executionResult.getExecutionState()) { - case SUCCESS: - outgoingMailsMetric.increment(); - break; - case TEMPORARY_FAILURE: - handleTemporaryFailure(mail, executionResult); - break; - case PERMANENT_FAILURE: - bouncer.bounce(mail, executionResult.getException().orElse(null)); - break; - } - } - - private void handleTemporaryFailure(Mail mail, ExecutionResult executionResult) throws MailQueue.MailQueueException { - if (!mail.getState().equals(Mail.ERROR)) { - mail.setState(Mail.ERROR); - DeliveryRetriesHelper.initRetries(mail); - mail.setLastUpdated(dateSupplier.get()); - } - int retries = DeliveryRetriesHelper.retrieveRetries(mail); - - if (retries < configuration.getMaxRetries()) { - reAttemptDelivery(mail, retries); - } else { - LOGGER.debug("Bouncing message {} after {} retries", mail.getName(), retries); - bouncer.bounce(mail, new Exception("Too many retries failure. Bouncing after " + retries + " retries.", executionResult.getException().orElse(null))); - } - } - - private void reAttemptDelivery(Mail mail, int retries) throws MailQueue.MailQueueException { - LOGGER.debug("Storing message {} into outgoing after {} retries", mail.getName(), retries); - DeliveryRetriesHelper.incrementRetries(mail); - mail.setLastUpdated(dateSupplier.get()); - // Something happened that will delay delivery. Store it back in the retry repository. - long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail)); - - if (configuration.isUsePriority()) { - // Use lowest priority for retries. See JAMES-1311 - mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, MailPrioritySupport.LOW_PRIORITY); - } - queue.enQueue(mail, delay, TimeUnit.MILLISECONDS); - } - - private long getNextDelay(int retry_count) { - if (retry_count > configuration.getDelayTimes().size()) { - return Delay.DEFAULT_DELAY_TIME; - } - return configuration.getDelayTimes().get(retry_count - 1); - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java deleted file mode 100644 index 18da3f8..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.Iterator; - -import org.apache.james.dnsservice.api.DNSService; -import org.apache.james.dnsservice.api.TemporaryResolutionException; -import org.apache.james.dnsservice.library.MXHostAddressIterator; -import org.apache.mailet.HostAddress; - -@SuppressWarnings("deprecation") -public class DnsHelper { - - public static final boolean USE_SEVERAL_IP = false; - private final DNSService dnsServer; - private final RemoteDeliveryConfiguration configuration; - - public DnsHelper(DNSService dnsServer, RemoteDeliveryConfiguration configuration) { - this.dnsServer = dnsServer; - this.configuration = configuration; - } - - public Iterator<HostAddress> retrieveHostAddressIterator(String host) throws TemporaryResolutionException { - if (configuration.getGatewayServer().isEmpty()) { - return new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, USE_SEVERAL_IP); - } else { - return new MXHostAddressIterator(configuration.getGatewayServer().iterator(), dnsServer, USE_SEVERAL_IP); - } - } - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java deleted file mode 100644 index 5eb3f33..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java +++ /dev/null @@ -1,167 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Optional; -import java.util.function.Function; -import javax.mail.MessagingException; -import javax.mail.internet.InternetAddress; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Preconditions; -import com.sun.mail.smtp.SMTPAddressFailedException; -import com.sun.mail.smtp.SMTPSendFailedException; -import com.sun.mail.smtp.SMTPSenderFailedException; - -public class EnhancedMessagingException { - - private static final Logger logger = LoggerFactory.getLogger(EnhancedMessagingException.class); - - private final MessagingException messagingException; - private final Optional<Integer> returnCode; - private final Optional<Integer> nestedReturnCode; - - public EnhancedMessagingException(MessagingException messagingException) { - this.messagingException = messagingException; - this.returnCode = computeReturnCode(); - this.nestedReturnCode = computeNestedReturnCode(); - } - - public boolean hasReturnCode() { - return returnCode.isPresent(); - } - - public boolean hasNestedReturnCode() { - return nestedReturnCode.isPresent(); - } - - public boolean isServerError() { - return isServerError(returnCode) || isServerError(nestedReturnCode); - } - - private boolean isServerError(Optional<Integer> returnCode) { - return (returnCode.isPresent() - && returnCode.get() >= 500 - && returnCode.get() <= 599) - || messageIndicatesServerException(); - } - - private boolean messageIndicatesServerException() { - return Optional.ofNullable(messagingException.getMessage()) - .map(startWith5()) - .orElse(false); - } - - private Function<String, Boolean> startWith5() { - return message -> message.startsWith("5"); - } - - private Optional<Integer> computeReturnCode() { - if (messagingException instanceof SMTPAddressFailedException) { - SMTPAddressFailedException addressFailedException = (SMTPAddressFailedException) this.messagingException; - return Optional.of(addressFailedException.getReturnCode()); - } - if (messagingException instanceof SMTPSendFailedException) { - SMTPSendFailedException sendFailedException = (SMTPSendFailedException) this.messagingException; - return Optional.of(sendFailedException.getReturnCode()); - } - if (messagingException instanceof SMTPSenderFailedException) { - SMTPSenderFailedException senderFailedException = (SMTPSenderFailedException) this.messagingException; - return Optional.of(senderFailedException.getReturnCode()); - } - if (messagingException.getClass().getName().endsWith(".SMTPSendFailedException") - || messagingException.getClass().getName().endsWith(".SMTPAddressSucceededException")) { - try { - return Optional.of((Integer)invokeGetter(messagingException, "getReturnCode")); - } catch (ClassCastException | IllegalArgumentException | IllegalStateException e) { - logger.error("unexpected exception", e); - } - } - return Optional.empty(); - } - - public Optional<String> computeCommand() { - if (hasReturnCode()) { - try { - return Optional.of((String) invokeGetter(messagingException, "getCommand")); - } catch (ClassCastException | IllegalArgumentException | IllegalStateException e) { - logger.error("unexpected exception", e); - } - } - return Optional.empty(); - } - - public Optional<InternetAddress> computeAddress() { - if (hasReturnCode()) { - try { - return Optional.of((InternetAddress) invokeGetter(messagingException, "getAddress")); - } catch (ClassCastException | IllegalArgumentException | IllegalStateException e) { - logger.error("unexpected exception", e); - } - } - return Optional.empty(); - } - - public String computeAction() { - return messagingException.getClass().getName().endsWith(".SMTPAddressFailedException") ? "FAILED" : "SUCCEEDED"; - } - - public Optional<Integer> getReturnCode() { - return returnCode; - } - - private Optional<Integer> computeNestedReturnCode() { - EnhancedMessagingException currentMessagingException = this; - while (true) { - Optional<Integer> returnCode = currentMessagingException.computeReturnCode(); - if (returnCode.isPresent()) { - return returnCode; - } - if (currentMessagingException.hasNestedMessagingException()) { - currentMessagingException = currentMessagingException.getNestedMessagingException(); - } else { - return Optional.empty(); - } - } - } - - private boolean hasNestedMessagingException() { - return messagingException.getNextException() != null - && messagingException.getNextException() instanceof MessagingException; - } - - private EnhancedMessagingException getNestedMessagingException() { - Preconditions.checkState(hasNestedMessagingException()); - return new EnhancedMessagingException((MessagingException) messagingException.getNextException()); - } - - private Object invokeGetter(Object target, String getter) { - try { - Method getAddress = target.getClass().getMethod(getter); - return getAddress.invoke(target); - } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - return new IllegalStateException("Exception invoking " + getter + " on a " + target.getClass() + " object"); - } - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java deleted file mode 100644 index fda36b2..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.Optional; - -import com.google.common.base.Objects; - -public class ExecutionResult { - - public enum ExecutionState { - SUCCESS, - PERMANENT_FAILURE, - TEMPORARY_FAILURE - } - - public static ExecutionResult success() { - return new ExecutionResult(ExecutionState.SUCCESS, Optional.empty()); - } - - public static ExecutionResult temporaryFailure(Exception e) { - return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.of(e)); - } - - public static ExecutionResult permanentFailure(Exception e) { - return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.of(e)); - } - - public static ExecutionResult temporaryFailure() { - return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.empty()); - } - - public static ExecutionResult onFailure(boolean permanent, Exception exeption) { - if (permanent) { - return permanentFailure(exeption); - } else { - return temporaryFailure(exeption); - } - } - - private final ExecutionState executionState; - private final Optional<Exception> exception; - - public ExecutionResult(ExecutionState executionState, Optional<Exception> exception) { - this.executionState = executionState; - this.exception = exception; - } - - public ExecutionState getExecutionState() { - return executionState; - } - - public Optional<Exception> getException() { - return exception; - } - - public boolean isPermanent() { - return executionState == ExecutionState.PERMANENT_FAILURE; - } - - @Override - public boolean equals(Object o) { - if (o instanceof ExecutionResult) { - ExecutionResult that = (ExecutionResult) o; - return Objects.equal(this.executionState, that.executionState) - && Objects.equal(this.exception, that.exception); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hashCode(executionState, exception); - } -} - http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java deleted file mode 100644 index b91c68e..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import org.apache.james.domainlist.api.DomainList; -import org.apache.james.domainlist.api.DomainListException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class HeloNameProvider { - - private static final Logger LOGGER = LoggerFactory.getLogger(HeloNameProvider.class); - public static final String LOCALHOST = "localhost"; - - private final String heloName; - private final DomainList domainList; - - public HeloNameProvider(String heloName, DomainList domainList) { - this.heloName = heloName; - this.domainList = domainList; - } - - public String getHeloName() { - if (heloName == null) { - // TODO: Maybe we should better just lookup the hostname via dns - try { - return domainList.getDefaultDomain(); - } catch (DomainListException e) { - LOGGER.warn("Unable to access DomainList", e); - return LOCALHOST; - } - } else { - return heloName; - } - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java deleted file mode 100644 index ca9037f..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.Collection; -import javax.mail.internet.InternetAddress; - -import org.apache.james.core.MailAddress; - -import com.google.common.base.Preconditions; - - -public class InternetAddressConverter { - - public static InternetAddress[] convert(Collection<MailAddress> recipients) { - Preconditions.checkNotNull(recipients); - return recipients.stream() - .map(MailAddress::toInternetAddress) - .toArray(InternetAddress[]::new); - } - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java deleted file mode 100644 index b6ce913..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java +++ /dev/null @@ -1,273 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.io.IOException; -import java.util.Iterator; -import java.util.List; - -import javax.mail.Address; -import javax.mail.MessagingException; -import javax.mail.SendFailedException; -import javax.mail.internet.InternetAddress; - -import org.apache.james.core.MailAddress; -import org.apache.james.dnsservice.api.DNSService; -import org.apache.james.dnsservice.api.TemporaryResolutionException; -import org.apache.mailet.HostAddress; -import org.apache.mailet.Mail; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.collect.Iterables; - -@SuppressWarnings("deprecation") -public class MailDelivrer { - private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrer.class); - - private final RemoteDeliveryConfiguration configuration; - private final MailDelivrerToHost mailDelivrerToHost; - private final DnsHelper dnsHelper; - private final MessageComposer messageComposer; - private final Bouncer bouncer; - - public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Bouncer bouncer) { - this(configuration, mailDelivrerToHost, new DnsHelper(dnsServer, configuration), bouncer); - } - - @VisibleForTesting - MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DnsHelper dnsHelper, Bouncer bouncer) { - this.configuration = configuration; - this.mailDelivrerToHost = mailDelivrerToHost; - this.dnsHelper = dnsHelper; - this.messageComposer = new MessageComposer(configuration); - this.bouncer = bouncer; - } - - /** - * 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. - * - * @param mail org.apache.james.core.MailImpl - * @return boolean Whether the delivery was successful and the message can be deleted - */ - public ExecutionResult deliver(Mail mail) { - try { - return tryDeliver(mail); - } catch (SendFailedException sfe) { - return handleSenderFailedException(mail, sfe); - } catch (MessagingException ex) { - // 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 - boolean isPermanent = new EnhancedMessagingException(ex).isServerError(); - return logAndReturn(mail, ExecutionResult.onFailure(isPermanent, ex)); - } catch (Exception ex) { - LOGGER.error("Generic exception = permanent failure: {}", ex.getMessage(), ex); - return logAndReturn(mail, ExecutionResult.permanentFailure(ex)); - } - } - - private ExecutionResult tryDeliver(Mail mail) throws MessagingException { - if (mail.getRecipients().isEmpty()) { - LOGGER.info("No recipients specified... not sure how this could have happened."); - return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender())); - } - if (configuration.isDebug()) { - LOGGER.debug("Attempting to deliver {}", mail.getName()); - } - - String host = retrieveTargetHostname(mail); - try { - // Figure out which servers to try to send to. This collection - // will hold all the possible target servers - Iterator<HostAddress> targetServers = dnsHelper.retrieveHostAddressIterator(host); - if (!targetServers.hasNext()) { - return handleNoTargetServer(mail, host); - } - return doDeliver(mail, InternetAddressConverter.convert(mail.getRecipients()), targetServers); - } catch (TemporaryResolutionException e) { - return logAndReturn(mail, ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " + - "up mail server for host: " + host + ". I cannot determine where to send this message."))); - } - } - - private String retrieveTargetHostname(Mail mail) { - Preconditions.checkArgument(!mail.getRecipients().isEmpty(), "Mail should have recipients to attempt delivery"); - MailAddress rcpt = Iterables.getFirst(mail.getRecipients(), null); - return rcpt.getDomain(); - } - - private ExecutionResult doDeliver(Mail mail, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException { - MessagingException lastError = null; - - while (targetServers.hasNext()) { - try { - return mailDelivrerToHost.tryDeliveryToHost(mail, addr, targetServers.next()); - } catch (SendFailedException sfe) { - lastError = handleSendFailExceptionOnMxIteration(mail, sfe); - } catch (MessagingException me) { - lastError = handleMessagingException(mail, me); - if (configuration.isDebug()) { - LOGGER.debug(me.getMessage(), me.getCause()); - } else { - LOGGER.info(me.getMessage()); - } - } - } - // 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; - } - return ExecutionResult.temporaryFailure(); - } - - private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException { - LOGGER.debug("Exception delivering message ({}) - {}", mail.getName(), me.getMessage()); - if ((me.getNextException() != null) && (me.getNextException() instanceof IOException)) { - // If it's an IO exception with no nested exception, it's probably - // some socket or weird I/O related problem. - return me; - } else { - // 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; - } - } - - @VisibleForTesting - ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) { - logSendFailedException(sfe); - EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe); - List<MailAddress> invalidAddresses = AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(sfe.getInvalidAddresses()); - List<MailAddress> validUnsentAddresses = AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(sfe.getValidUnsentAddresses()); - if (configuration.isDebug()) { - LOGGER.debug("Mail {} has initially recipients: {}", mail.getName(), mail.getRecipients()); - if (!invalidAddresses.isEmpty()) { - LOGGER.debug("Invalid recipients: {}", invalidAddresses); - } - if (!validUnsentAddresses.isEmpty()) { - LOGGER.debug("Unsent recipients: {}", validUnsentAddresses); - } - } - if (!validUnsentAddresses.isEmpty()) { - if (!invalidAddresses.isEmpty()) { - mail.setRecipients(invalidAddresses); - bouncer.bounce(mail, sfe); - } - mail.setRecipients(validUnsentAddresses); - if (enhancedMessagingException.hasReturnCode()) { - boolean isPermanent = enhancedMessagingException.isServerError(); - return logAndReturn(mail, ExecutionResult.onFailure(isPermanent, sfe)); - } else { - return logAndReturn(mail, ExecutionResult.temporaryFailure(sfe)); - } - } - if (!invalidAddresses.isEmpty()) { - mail.setRecipients(invalidAddresses); - return logAndReturn(mail, ExecutionResult.permanentFailure(sfe)); - } - - if (enhancedMessagingException.hasReturnCode() || enhancedMessagingException.hasNestedReturnCode()) { - if (enhancedMessagingException.isServerError()) { - return ExecutionResult.permanentFailure(sfe); - } - } - return ExecutionResult.temporaryFailure(sfe); - } - - private ExecutionResult logAndReturn(Mail mail, ExecutionResult executionResult) { - LOGGER.debug(messageComposer.composeFailLogMessage(mail, executionResult)); - return executionResult; - } - - private MessagingException handleSendFailExceptionOnMxIteration(Mail mail, SendFailedException sfe) throws SendFailedException { - logSendFailedException(sfe); - - if (sfe.getValidSentAddresses() != null) { - Address[] validSent = sfe.getValidSentAddresses(); - if (validSent.length > 0) { - LOGGER.debug("Mail ({}) sent successfully for {}", mail.getName(), validSent); - } - } - - EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe); - if (enhancedMessagingException.isServerError()) { - throw sfe; - } - - final Address[] validUnsentAddresses = sfe.getValidUnsentAddresses(); - if (validUnsentAddresses != null && validUnsentAddresses.length > 0) { - if (configuration.isDebug()) { - LOGGER.debug("Send failed, {} valid addresses remain, continuing with any other servers", (Object) validUnsentAddresses); - } - return sfe; - } else { - // There are no valid addresses left to send, so rethrow - throw sfe; - } - } - - private ExecutionResult handleNoTargetServer(Mail mail, String host) { - LOGGER.info("No mail server found for: {}", host); - MessagingException messagingException = new MessagingException("There are no DNS entries for the hostname " + host + ". I cannot determine where to send this message."); - int retry = DeliveryRetriesHelper.retrieveRetries(mail); - if (retry >= configuration.getDnsProblemRetry()) { - return logAndReturn(mail, ExecutionResult.permanentFailure(messagingException)); - } else { - return logAndReturn(mail, ExecutionResult.temporaryFailure(messagingException)); - } - } - - private void logSendFailedException(SendFailedException sfe) { - if (configuration.isDebug()) { - EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe); - if (enhancedMessagingException.hasReturnCode()) { - LOGGER.info("SMTP SEND FAILED: Command [{}] RetCode: [{}] Response[{}]", enhancedMessagingException.computeCommand(), - enhancedMessagingException.getReturnCode(), sfe.getMessage()); - } else { - LOGGER.info("Send failed", sfe); - } - logLevels(sfe); - } - } - - private void logLevels(MessagingException me) { - Exception ne; - while ((ne = me.getNextException()) != null && ne instanceof MessagingException) { - me = (MessagingException) ne; - EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me); - if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) { - LOGGER.debug("ADDRESS :[{}] Address:[{}] Command : [{}] RetCode[{}] Response [{}]", - enhancedMessagingException.computeAction(), me, enhancedMessagingException.computeAddress(), - enhancedMessagingException.computeCommand(), enhancedMessagingException.getReturnCode()); - } - } - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java deleted file mode 100644 index 903ea98..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.io.IOException; -import java.util.Properties; - -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; - -import org.apache.mailet.HostAddress; -import org.apache.mailet.Mail; -import org.apache.mailet.MailetContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.sun.mail.smtp.SMTPTransport; - -@SuppressWarnings("deprecation") -public class MailDelivrerToHost { - private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrerToHost.class); - public static final String BIT_MIME_8 = "8BITMIME"; - - private final RemoteDeliveryConfiguration configuration; - private final Converter7Bit converter7Bit; - private final Session session; - - public MailDelivrerToHost(RemoteDeliveryConfiguration remoteDeliveryConfiguration, MailetContext mailetContext) { - this.configuration = remoteDeliveryConfiguration; - this.converter7Bit = new Converter7Bit(mailetContext); - this.session = Session.getInstance(configuration.createFinalJavaxProperties()); - } - - public ExecutionResult tryDeliveryToHost(Mail mail, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException { - Properties props = getPropertiesForMail(mail); - LOGGER.debug("Attempting delivery of {} to host {} at {} from {}", - mail.getName(), outgoingMailServer.getHostName(), outgoingMailServer.getHost(), props.get("mail.smtp.from")); - - // 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. - - SMTPTransport transport = null; - try { - transport = (SMTPTransport) session.getTransport(outgoingMailServer); - transport.setLocalHost(props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName())); - connect(outgoingMailServer, transport); - transport.sendMessage(adaptToTransport(mail.getMessage(), transport), addr); - LOGGER.debug("Mail ({}) sent successfully to {} at {} from {} for {}", mail.getName(), outgoingMailServer.getHostName(), - outgoingMailServer.getHost(), props.get("mail.smtp.from"), mail.getRecipients()); - } finally { - closeTransport(mail, outgoingMailServer, transport); - } - return ExecutionResult.success(); - } - - private Properties getPropertiesForMail(Mail mail) { - 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); - } - return props; - } - - private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException { - if (configuration.getAuthUser() != null) { - transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass()); - } else { - transport.connect(); - } - } - - private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException { - if (shouldAdapt(transport)) { - try { - converter7Bit.convertTo7Bit(message); - } catch (IOException e) { - LOGGER.error("Error during the conversion to 7 bit.", e); - } - } - return message; - } - - private boolean shouldAdapt(SMTPTransport transport) { - // If the transport is a SMTPTransport (from sun) some performance enhancement can be done. - // 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. - return !transport.getClass().getName().endsWith(".SMTPTransport") - || !transport.supportsExtension(BIT_MIME_8); - // if the message is already 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. - } - - private void closeTransport(Mail mail, HostAddress outgoingMailServer, SMTPTransport transport) { - 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) { - LOGGER.error("Warning: could not close the SMTP transport after sending mail ({}) to {} at {} for {}; " + - "probably the server has already closed the connection. Message is considered to be delivered. Exception: {}", - mail.getName(), outgoingMailServer.getHostName(), outgoingMailServer.getHost(), mail.getRecipients(), e.getMessage()); - } - transport = null; - } - } - - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java deleted file mode 100644 index e04c2be..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import javax.mail.MessagingException; -import javax.mail.SendFailedException; -import javax.mail.internet.InternetAddress; - -import org.apache.mailet.Mail; - -public class MessageComposer { - - private final RemoteDeliveryConfiguration configuration; - - public MessageComposer(RemoteDeliveryConfiguration configuration) { - this.configuration = configuration; - } - - /** - * 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 - */ - public String fromException(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(" ( ").append(ia).append(" - [").append(ex.getMessage().replaceAll("\\n", "")).append("] )"); - 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(Arrays.toString(exception.getInvalidAddresses())); - invalidAddr = true; - } - if (exception.getValidUnsentAddresses().length > 0) { - if (invalidAddr) { - sb.append(" "); - } - sb.append(Arrays.toString(exception.getValidUnsentAddresses())); - } - sb.append(" - ["); - sb.append(exception.getMessage().replaceAll("\\n", "")); - sb.append("] )"); - } - return sb.toString(); - } - return null; - } - - public String composeFailLogMessage(Mail mail, ExecutionResult executionResult) { - StringWriter sout = new StringWriter(); - PrintWriter out = new PrintWriter(sout, true); - out.print(permanentAsString(executionResult.isPermanent()) + " exception delivering mail (" + mail.getName() - + ")" + retrieveExceptionLog(executionResult.getException().orElse(null)) + ": "); - if (configuration.isDebug()) { - if (executionResult.getException().isPresent()) { - executionResult.getException().get().printStackTrace(out); - } - } - return sout.toString(); - } - - private String permanentAsString(boolean permanent) { - if (permanent) { - return "Permanent"; - } - return "Temporary"; - } - - private String retrieveExceptionLog(Exception ex) { - String exceptionLog = fromException(ex); - if (exceptionLog != null) { - return ". " + exceptionLog; - } - return ""; - } - - private Object invokeGetter(Object target, String getter) { - try { - Method getAddress = target.getClass().getMethod(getter); - return getAddress.invoke(target); - } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - return new IllegalStateException("Exception invoking " + getter + " on a " + target.getClass() + " object"); - } - } - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java deleted file mode 100644 index f6596ff..0000000 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java +++ /dev/null @@ -1,320 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.transport.mailets.remoteDelivery; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Properties; - -import org.apache.commons.lang3.tuple.Pair; -import org.apache.james.domainlist.api.DomainList; -import org.apache.mailet.MailetConfig; -import org.apache.mailet.base.MailetUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.github.steveash.guavate.Guavate; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; - -public class RemoteDeliveryConfiguration { - - private static final Logger LOGGER = LoggerFactory.getLogger(RemoteDeliveryConfiguration.class); - - public static final String DELIVERY_THREADS = "deliveryThreads"; - public static final String USE_PRIORITY = "usePriority"; - public static final String MAX_DNS_PROBLEM_RETRIES = "maxDnsProblemRetries"; - public static final String HELO_NAME = "heloName"; - public static final String JAVAX_PREFIX = "mail."; - public static final String BIND = "bind"; - public static final String GATEWAY_PASSWORD = "gatewayPassword"; - public static final String GATEWAY_USERNAME_COMPATIBILITY = "gatewayusername"; - public static final String GATEWAY_USERNAME = "gatewayUsername"; - public static final String GATEWAY_PORT = "gatewayPort"; - public static final String GATEWAY = "gateway"; - public static final String SSL_ENABLE = "sslEnable"; - public static final String START_TLS = "startTLS"; - public static final String BOUNCE_PROCESSOR = "bounceProcessor"; - public static final String SENDPARTIAL = "sendpartial"; - public static final String TIMEOUT = "timeout"; - public static final String CONNECTIONTIMEOUT = "connectiontimeout"; - public static final String OUTGOING = "outgoing"; - public static final String MAX_RETRIES = "maxRetries"; - public static final String DELAY_TIME = "delayTime"; - public static final String DEBUG = "debug"; - public static final int DEFAULT_SMTP_TIMEOUT = 180000; - public static final String DEFAULT_OUTGOING_QUEUE_NAME = "outgoing"; - public static final int DEFAULT_CONNECTION_TIMEOUT = 60000; - public static final int DEFAULT_DNS_RETRY_PROBLEM = 0; - public static final int DEFAULT_MAX_RETRY = 5; - public static final String ADDRESS_PORT_SEPARATOR = ":"; - - private final boolean isDebug; - private final boolean usePriority; - private final boolean startTLS; - private final boolean isSSLEnable; - private final boolean isBindUsed; - private final boolean sendPartial; - private final int maxRetries; - private final long smtpTimeout; - private final int dnsProblemRetry; - private final int connectionTimeout; - private final int workersThreadCount; - private final List<Long> delayTimes; - private final HeloNameProvider heloNameProvider; - private final String outGoingQueueName; - private final String bindAddress; - private final String bounceProcessor; - private final Collection<String> gatewayServer; - private final String authUser; - private final String authPass; - private final Properties javaxAdditionalProperties; - - public RemoteDeliveryConfiguration(MailetConfig mailetConfig, DomainList domainList) { - isDebug = MailetUtil.getInitParameter(mailetConfig, DEBUG).orElse(false); - startTLS = MailetUtil.getInitParameter(mailetConfig, START_TLS).orElse(false); - isSSLEnable = MailetUtil.getInitParameter(mailetConfig, SSL_ENABLE).orElse(false); - usePriority = MailetUtil.getInitParameter(mailetConfig, USE_PRIORITY).orElse(false); - sendPartial = MailetUtil.getInitParameter(mailetConfig, SENDPARTIAL).orElse(false); - outGoingQueueName = Optional.ofNullable(mailetConfig.getInitParameter(OUTGOING)).orElse(DEFAULT_OUTGOING_QUEUE_NAME); - bounceProcessor = mailetConfig.getInitParameter(BOUNCE_PROCESSOR); - bindAddress = mailetConfig.getInitParameter(BIND); - - DelaysAndMaxRetry delaysAndMaxRetry = computeDelaysAndMaxRetry(mailetConfig); - maxRetries = delaysAndMaxRetry.getMaxRetries(); - delayTimes = delaysAndMaxRetry.getExpandedDelays(); - smtpTimeout = computeSmtpTimeout(mailetConfig); - connectionTimeout = computeConnectionTimeout(mailetConfig); - dnsProblemRetry = computeDnsProblemRetry(mailetConfig); - heloNameProvider = new HeloNameProvider(mailetConfig.getInitParameter(HELO_NAME), domainList); - workersThreadCount = Integer.valueOf(mailetConfig.getInitParameter(DELIVERY_THREADS)); - - String gatewayPort = mailetConfig.getInitParameter(GATEWAY_PORT); - String gateway = mailetConfig.getInitParameter(GATEWAY); - gatewayServer = computeGatewayServers(gatewayPort, gateway); - if (gateway != null) { - authUser = computeGatewayUser(mailetConfig); - authPass = mailetConfig.getInitParameter(GATEWAY_PASSWORD); - } else { - authUser = null; - authPass = null; - } - isBindUsed = bindAddress != null; - javaxAdditionalProperties = computeJavaxProperties(mailetConfig); - } - - private Properties computeJavaxProperties(MailetConfig mailetConfig) { - Properties result = new Properties(); - // deal with <mail.*> attributes, passing them to javamail - result.putAll( - ImmutableList.copyOf(mailetConfig.getInitParameterNames()) - .stream() - .filter(propertyName -> propertyName.startsWith(JAVAX_PREFIX)) - .map(propertyName -> Pair.of(propertyName, mailetConfig.getInitParameter(propertyName))) - .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue))); - return result; - } - - private int computeDnsProblemRetry(MailetConfig mailetConfig) { - String dnsRetry = mailetConfig.getInitParameter(MAX_DNS_PROBLEM_RETRIES); - if (!Strings.isNullOrEmpty(dnsRetry)) { - return Integer.valueOf(dnsRetry); - } else { - return DEFAULT_DNS_RETRY_PROBLEM; - } - } - - private int computeConnectionTimeout(MailetConfig mailetConfig) { - try { - return Integer.valueOf( - Optional.ofNullable(mailetConfig.getInitParameter(CONNECTIONTIMEOUT)) - .orElse(String.valueOf(DEFAULT_CONNECTION_TIMEOUT))); - } catch (Exception e) { - LOGGER.warn("Invalid timeout setting: {}", mailetConfig.getInitParameter(TIMEOUT)); - return DEFAULT_CONNECTION_TIMEOUT; - } - } - - private long computeSmtpTimeout(MailetConfig mailetConfig) { - try { - if (mailetConfig.getInitParameter(TIMEOUT) != null) { - return Integer.valueOf(mailetConfig.getInitParameter(TIMEOUT)); - } else { - return DEFAULT_SMTP_TIMEOUT; - } - } catch (Exception e) { - LOGGER.warn("Invalid timeout setting: {}", mailetConfig.getInitParameter(TIMEOUT)); - return DEFAULT_SMTP_TIMEOUT; - } - } - - private DelaysAndMaxRetry computeDelaysAndMaxRetry(MailetConfig mailetConfig) { - try { - int intendedMaxRetries = Integer.valueOf( - Optional.ofNullable(mailetConfig.getInitParameter(MAX_RETRIES)) - .orElse(String.valueOf(DEFAULT_MAX_RETRY))); - return DelaysAndMaxRetry.from(intendedMaxRetries, mailetConfig.getInitParameter(DELAY_TIME)); - } catch (Exception e) { - LOGGER.warn("Invalid maxRetries setting: {}", mailetConfig.getInitParameter(MAX_RETRIES)); - return DelaysAndMaxRetry.defaults(); - } - } - - private String computeGatewayUser(MailetConfig mailetConfig) { - // backward compatibility with 2.3.x - String user = mailetConfig.getInitParameter(GATEWAY_USERNAME); - if (user == null) { - return mailetConfig.getInitParameter(GATEWAY_USERNAME_COMPATIBILITY); - } - return user; - } - - private List<String> computeGatewayServers(String gatewayPort, String gateway) { - if (gateway != null) { - ImmutableList.Builder<String> builder = ImmutableList.builder(); - Iterable<String> gatewayParts = Splitter.on(',').split(gateway); - for (String gatewayPart : gatewayParts) { - builder.add(parsePart(gatewayPort, gatewayPart)); - } - return builder.build(); - } else { - return ImmutableList.of(); - } - } - - private String parsePart(String gatewayPort, String gatewayPart) { - String address = gatewayPart.trim(); - if (!address.contains(ADDRESS_PORT_SEPARATOR) && gatewayPort != null) { - return address + ADDRESS_PORT_SEPARATOR + gatewayPort; - } - return address; - } - - public Properties createFinalJavaxProperties() { - Properties props = new Properties(); - 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). - props.setProperty("mail.smtp.allow8bitmime", "true"); - props.put("mail.smtp.timeout", String.valueOf(smtpTimeout)); - props.put("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout)); - props.put("mail.smtp.sendpartial", String.valueOf(sendPartial)); - props.put("mail.smtp.localhost", heloNameProvider.getHeloName()); - props.put("mail.smtp.starttls.enable", String.valueOf(startTLS)); - props.put("mail.smtp.ssl.enable", String.valueOf(isSSLEnable)); - 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(javaxAdditionalProperties); - return props; - } - - public boolean isDebug() { - return isDebug; - } - - public List<Long> getDelayTimes() { - return delayTimes; - } - - public int getMaxRetries() { - return maxRetries; - } - - public long getSmtpTimeout() { - return smtpTimeout; - } - - public boolean isSendPartial() { - return sendPartial; - } - - public int getConnectionTimeout() { - return connectionTimeout; - } - - public int getWorkersThreadCount() { - return workersThreadCount; - } - - public Collection<String> getGatewayServer() { - return gatewayServer; - } - - public String getAuthUser() { - return authUser; - } - - public String getAuthPass() { - return authPass; - } - - public boolean isBindUsed() { - return isBindUsed; - } - - public String getBounceProcessor() { - return bounceProcessor; - } - - public boolean isUsePriority() { - return usePriority; - } - - public boolean isStartTLS() { - return startTLS; - } - - public boolean isSSLEnable() { - return isSSLEnable; - } - - public HeloNameProvider getHeloNameProvider() { - return heloNameProvider; - } - - public String getOutGoingQueueName() { - return outGoingQueueName; - } - - public Properties getJavaxAdditionalProperties() { - return javaxAdditionalProperties; - } - - public int getDnsProblemRetry() { - return dnsProblemRetry; - } - - public String getBindAddress() { - return bindAddress; - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
