001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.mail;
018
019import java.io.PrintStream;
020import java.io.PrintWriter;
021import java.util.LinkedHashMap;
022import java.util.Map;
023
024import org.springframework.util.ObjectUtils;
025
026/**
027 * Exception thrown when a mail sending error is encountered.
028 * Can register failed messages with their exceptions.
029 *
030 * @author Dmitriy Kopylenko
031 * @author Juergen Hoeller
032 */
033@SuppressWarnings("serial")
034public class MailSendException extends MailException {
035
036        private transient final Map<Object, Exception> failedMessages;
037
038        private Exception[] messageExceptions;
039
040
041        /**
042         * Constructor for MailSendException.
043         * @param msg the detail message
044         */
045        public MailSendException(String msg) {
046                this(msg, null);
047        }
048
049        /**
050         * Constructor for MailSendException.
051         * @param msg the detail message
052         * @param cause the root cause from the mail API in use
053         */
054        public MailSendException(String msg, Throwable cause) {
055                super(msg, cause);
056                this.failedMessages = new LinkedHashMap<Object, Exception>();
057        }
058
059        /**
060         * Constructor for registration of failed messages, with the
061         * messages that failed as keys, and the thrown exceptions as values.
062         * <p>The messages should be the same that were originally passed
063         * to the invoked send method.
064         * @param msg the detail message
065         * @param cause the root cause from the mail API in use
066         * @param failedMessages Map of failed messages as keys and thrown
067         * exceptions as values
068         */
069        public MailSendException(String msg, Throwable cause, Map<Object, Exception> failedMessages) {
070                super(msg, cause);
071                this.failedMessages = new LinkedHashMap<Object, Exception>(failedMessages);
072                this.messageExceptions = failedMessages.values().toArray(new Exception[failedMessages.size()]);
073        }
074
075        /**
076         * Constructor for registration of failed messages, with the
077         * messages that failed as keys, and the thrown exceptions as values.
078         * <p>The messages should be the same that were originally passed
079         * to the invoked send method.
080         * @param failedMessages Map of failed messages as keys and thrown
081         * exceptions as values
082         */
083        public MailSendException(Map<Object, Exception> failedMessages) {
084                this(null, null, failedMessages);
085        }
086
087
088        /**
089         * Return a Map with the failed messages as keys, and the thrown exceptions
090         * as values.
091         * <p>Note that a general mail server connection failure will not result
092         * in failed messages being returned here: A message will only be
093         * contained here if actually sending it was attempted but failed.
094         * <p>The messages will be the same that were originally passed to the
095         * invoked send method, that is, SimpleMailMessages in case of using
096         * the generic MailSender interface.
097         * <p>In case of sending MimeMessage instances via JavaMailSender,
098         * the messages will be of type MimeMessage.
099         * <p><b>NOTE:</b> This Map will not be available after serialization.
100         * Use {@link #getMessageExceptions()} in such a scenario, which will
101         * be available after serialization as well.
102         * @return the Map of failed messages as keys and thrown exceptions as values
103         * @see SimpleMailMessage
104         * @see javax.mail.internet.MimeMessage
105         */
106        public final Map<Object, Exception> getFailedMessages() {
107                return this.failedMessages;
108        }
109
110        /**
111         * Return an array with thrown message exceptions.
112         * <p>Note that a general mail server connection failure will not result
113         * in failed messages being returned here: A message will only be
114         * contained here if actually sending it was attempted but failed.
115         * @return the array of thrown message exceptions,
116         * or an empty array if no failed messages
117         */
118        public final Exception[] getMessageExceptions() {
119                return (this.messageExceptions != null ? this.messageExceptions : new Exception[0]);
120        }
121
122
123        @Override
124        public String getMessage() {
125                if (ObjectUtils.isEmpty(this.messageExceptions)) {
126                        return super.getMessage();
127                }
128                else {
129                        StringBuilder sb = new StringBuilder();
130                        String baseMessage = super.getMessage();
131                        if (baseMessage != null) {
132                                sb.append(baseMessage).append(". ");
133                        }
134                        sb.append("Failed messages: ");
135                        for (int i = 0; i < this.messageExceptions.length; i++) {
136                                Exception subEx = this.messageExceptions[i];
137                                sb.append(subEx.toString());
138                                if (i < this.messageExceptions.length - 1) {
139                                        sb.append("; ");
140                                }
141                        }
142                        return sb.toString();
143                }
144        }
145
146        @Override
147        public String toString() {
148                if (ObjectUtils.isEmpty(this.messageExceptions)) {
149                        return super.toString();
150                }
151                else {
152                        StringBuilder sb = new StringBuilder(super.toString());
153                        sb.append("; message exceptions (").append(this.messageExceptions.length).append(") are:");
154                        for (int i = 0; i < this.messageExceptions.length; i++) {
155                                Exception subEx = this.messageExceptions[i];
156                                sb.append('\n').append("Failed message ").append(i + 1).append(": ");
157                                sb.append(subEx);
158                        }
159                        return sb.toString();
160                }
161        }
162
163        @Override
164        public void printStackTrace(PrintStream ps) {
165                if (ObjectUtils.isEmpty(this.messageExceptions)) {
166                        super.printStackTrace(ps);
167                }
168                else {
169                        ps.println(super.toString() + "; message exception details (" +
170                                        this.messageExceptions.length + ") are:");
171                        for (int i = 0; i < this.messageExceptions.length; i++) {
172                                Exception subEx = this.messageExceptions[i];
173                                ps.println("Failed message " + (i + 1) + ":");
174                                subEx.printStackTrace(ps);
175                        }
176                }
177        }
178
179        @Override
180        public void printStackTrace(PrintWriter pw) {
181                if (ObjectUtils.isEmpty(this.messageExceptions)) {
182                        super.printStackTrace(pw);
183                }
184                else {
185                        pw.println(super.toString() + "; message exception details (" +
186                                        this.messageExceptions.length + ") are:");
187                        for (int i = 0; i < this.messageExceptions.length; i++) {
188                                Exception subEx = this.messageExceptions[i];
189                                pw.println("Failed message " + (i + 1) + ":");
190                                subEx.printStackTrace(pw);
191                        }
192                }
193        }
194
195}