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