PageRenderTime 285ms CodeModel.GetById 32ms app.highlight 182ms RepoModel.GetById 19ms app.codeStats 2ms

/projects/springframework-3.0.5/projects/org.springframework.context.support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1100 lines | 476 code | 114 blank | 510 comment | 45 complexity | e38695981348dfd2379319a818370997 MD5 | raw file
   1/*
   2 * Copyright 2002-2010 the original author or authors.
   3 *
   4 * Licensed under the Apache License, Version 2.0 (the "License");
   5 * you may not use this file except in compliance with the License.
   6 * You may obtain a copy of the License at
   7 *
   8 *      http://www.apache.org/licenses/LICENSE-2.0
   9 *
  10 * Unless required by applicable law or agreed to in writing, software
  11 * distributed under the License is distributed on an "AS IS" BASIS,
  12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 * See the License for the specific language governing permissions and
  14 * limitations under the License.
  15 */
  16
  17package org.springframework.mail.javamail;
  18
  19import java.io.File;
  20import java.io.IOException;
  21import java.io.InputStream;
  22import java.io.OutputStream;
  23import java.io.UnsupportedEncodingException;
  24import java.util.Date;
  25import javax.activation.DataHandler;
  26import javax.activation.DataSource;
  27import javax.activation.FileDataSource;
  28import javax.activation.FileTypeMap;
  29import javax.mail.BodyPart;
  30import javax.mail.Message;
  31import javax.mail.MessagingException;
  32import javax.mail.internet.AddressException;
  33import javax.mail.internet.InternetAddress;
  34import javax.mail.internet.MimeBodyPart;
  35import javax.mail.internet.MimeMessage;
  36import javax.mail.internet.MimeMultipart;
  37import javax.mail.internet.MimePart;
  38
  39import org.springframework.core.io.InputStreamSource;
  40import org.springframework.core.io.Resource;
  41import org.springframework.util.Assert;
  42
  43/**
  44 * Helper class for populating a {@link javax.mail.internet.MimeMessage}.
  45 *
  46 * <p>Mirrors the simple setters of {@link org.springframework.mail.SimpleMailMessage},
  47 * directly applying the values to the underlying MimeMessage. Allows for defining
  48 * a character encoding for the entire message, automatically applied by all methods
  49 * of this helper class.
  50 *
  51 * <p>Offers support for HTML text content, inline elements such as images, and typical
  52 * mail attachments. Also supports personal names that accompany mail addresses. Note that
  53 * advanced settings can still be applied directly to the underlying MimeMessage object!
  54 *
  55 * <p>Typically used in {@link MimeMessagePreparator} implementations or
  56 * {@link JavaMailSender} client code: simply instantiating it as a MimeMessage wrapper,
  57 * invoking setters on the wrapper, using the underlying MimeMessage for mail sending.
  58 * Also used internally by {@link JavaMailSenderImpl}.
  59 *
  60 * <p>Sample code for an HTML mail with an inline image and a PDF attachment:
  61 *
  62 * <pre class="code">
  63 * mailSender.send(new MimeMessagePreparator() {
  64 *   public void prepare(MimeMessage mimeMessage) throws MessagingException {
  65 *     MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8");
  66 *     message.setFrom("me@mail.com");
  67 *     message.setTo("you@mail.com");
  68 *     message.setSubject("my subject");
  69 *     message.setText("my text &lt;img src='cid:myLogo'&gt;", true);
  70 *     message.addInline("myLogo", new ClassPathResource("img/mylogo.gif"));
  71 *     message.addAttachment("myDocument.pdf", new ClassPathResource("doc/myDocument.pdf"));
  72 *   }
  73 * });</pre>
  74 *
  75 * Consider using {@link MimeMailMessage} (which implements the common
  76 * {@link org.springframework.mail.MailMessage} interface, just like
  77 * {@link org.springframework.mail.SimpleMailMessage}) on top of this helper,
  78 * in order to let message population code interact with a simple message
  79 * or a MIME message through a common interface.
  80 *
  81 * <p><b>Warning regarding multipart mails:</b> Simple MIME messages that
  82 * just contain HTML text but no inline elements or attachments will work on
  83 * more or less any email client that is capable of HTML rendering. However,
  84 * inline elements and attachments are still a major compatibility issue
  85 * between email clients: It's virtually impossible to get inline elements
  86 * and attachments working across Microsoft Outlook, Lotus Notes and Mac Mail.
  87 * Consider choosing a specific multipart mode for your needs: The javadoc
  88 * on the MULTIPART_MODE constants contains more detailed information.
  89 *
  90 * @author Juergen Hoeller
  91 * @since 19.01.2004
  92 * @see #setText(String, boolean)
  93 * @see #setText(String, String)
  94 * @see #addInline(String, org.springframework.core.io.Resource)
  95 * @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
  96 * @see #MULTIPART_MODE_MIXED_RELATED
  97 * @see #MULTIPART_MODE_RELATED
  98 * @see #getMimeMessage()
  99 * @see JavaMailSender
 100 */
 101public class MimeMessageHelper {
 102
 103	/**
 104	 * Constant indicating a non-multipart message.
 105	 */
 106	public static final int MULTIPART_MODE_NO = 0;
 107
 108	/**
 109	 * Constant indicating a multipart message with a single root multipart
 110	 * element of type "mixed". Texts, inline elements and attachements
 111	 * will all get added to that root element.
 112	 * <p>This was Spring 1.0's default behavior. It is known to work properly
 113	 * on Outlook. However, other mail clients tend to misinterpret inline
 114	 * elements as attachments and/or show attachments inline as well.
 115	 */
 116	public static final int MULTIPART_MODE_MIXED = 1;
 117
 118	/**
 119	 * Constant indicating a multipart message with a single root multipart
 120	 * element of type "related". Texts, inline elements and attachements
 121	 * will all get added to that root element.
 122	 * <p>This was the default behavior from Spring 1.1 up to 1.2 final.
 123	 * This is the "Microsoft multipart mode", as natively sent by Outlook.
 124	 * It is known to work properly on Outlook, Outlook Express, Yahoo Mail, and
 125	 * to a large degree also on Mac Mail (with an additional attachment listed
 126	 * for an inline element, despite the inline element also shown inline).
 127	 * Does not work properly on Lotus Notes (attachments won't be shown there).
 128	 */
 129	public static final int MULTIPART_MODE_RELATED = 2;
 130
 131	/**
 132	 * Constant indicating a multipart message with a root multipart element
 133	 * "mixed" plus a nested multipart element of type "related". Texts and
 134	 * inline elements will get added to the nested "related" element,
 135	 * while attachments will get added to the "mixed" root element.
 136	 * <p>This is the default since Spring 1.2.1. This is arguably the most correct
 137	 * MIME structure, according to the MIME spec: It is known to work properly
 138	 * on Outlook, Outlook Express, Yahoo Mail, and Lotus Notes. Does not work
 139	 * properly on Mac Mail. If you target Mac Mail or experience issues with
 140	 * specific mails on Outlook, consider using MULTIPART_MODE_RELATED instead.
 141	 */
 142	public static final int MULTIPART_MODE_MIXED_RELATED = 3;
 143
 144
 145	private static final String MULTIPART_SUBTYPE_MIXED = "mixed";
 146
 147	private static final String MULTIPART_SUBTYPE_RELATED = "related";
 148
 149	private static final String MULTIPART_SUBTYPE_ALTERNATIVE = "alternative";
 150
 151	private static final String CONTENT_TYPE_ALTERNATIVE = "text/alternative";
 152
 153	private static final String CONTENT_TYPE_HTML = "text/html";
 154
 155	private static final String CONTENT_TYPE_CHARSET_SUFFIX = ";charset=";
 156
 157	private static final String HEADER_PRIORITY = "X-Priority";
 158
 159	private static final String HEADER_CONTENT_ID = "Content-ID";
 160
 161
 162	private final MimeMessage mimeMessage;
 163
 164	private MimeMultipart rootMimeMultipart;
 165
 166	private MimeMultipart mimeMultipart;
 167
 168	private final String encoding;
 169
 170	private FileTypeMap fileTypeMap;
 171
 172	private boolean validateAddresses = false;
 173
 174
 175	/**
 176	 * Create a new MimeMessageHelper for the given MimeMessage,
 177	 * assuming a simple text message (no multipart content,
 178	 * i.e. no alternative texts and no inline elements or attachments).
 179	 * <p>The character encoding for the message will be taken from
 180	 * the passed-in MimeMessage object, if carried there. Else,
 181	 * JavaMail's default encoding will be used.
 182	 * @param mimeMessage MimeMessage to work on
 183	 * @see #MimeMessageHelper(javax.mail.internet.MimeMessage, boolean)
 184	 * @see #getDefaultEncoding(javax.mail.internet.MimeMessage)
 185	 * @see JavaMailSenderImpl#setDefaultEncoding
 186	 */
 187	public MimeMessageHelper(MimeMessage mimeMessage) {
 188		this(mimeMessage, null);
 189	}
 190
 191	/**
 192	 * Create a new MimeMessageHelper for the given MimeMessage,
 193	 * assuming a simple text message (no multipart content,
 194	 * i.e. no alternative texts and no inline elements or attachments).
 195	 * @param mimeMessage MimeMessage to work on
 196	 * @param encoding the character encoding to use for the message
 197	 * @see #MimeMessageHelper(javax.mail.internet.MimeMessage, boolean)
 198	 */
 199	public MimeMessageHelper(MimeMessage mimeMessage, String encoding) {
 200		this.mimeMessage = mimeMessage;
 201		this.encoding = (encoding != null ? encoding : getDefaultEncoding(mimeMessage));
 202		this.fileTypeMap = getDefaultFileTypeMap(mimeMessage);
 203	}
 204
 205	/**
 206	 * Create a new MimeMessageHelper for the given MimeMessage,
 207	 * in multipart mode (supporting alternative texts, inline
 208	 * elements and attachments) if requested.
 209	 * <p>Consider using the MimeMessageHelper constructor that
 210	 * takes a multipartMode argument to choose a specific multipart
 211	 * mode other than MULTIPART_MODE_MIXED_RELATED.
 212	 * <p>The character encoding for the message will be taken from
 213	 * the passed-in MimeMessage object, if carried there. Else,
 214	 * JavaMail's default encoding will be used.
 215	 * @param mimeMessage MimeMessage to work on
 216	 * @param multipart whether to create a multipart message that
 217	 * supports alternative texts, inline elements and attachments
 218	 * (corresponds to MULTIPART_MODE_MIXED_RELATED)
 219	 * @throws MessagingException if multipart creation failed
 220	 * @see #MimeMessageHelper(javax.mail.internet.MimeMessage, int)
 221	 * @see #getDefaultEncoding(javax.mail.internet.MimeMessage)
 222	 * @see JavaMailSenderImpl#setDefaultEncoding
 223	 */
 224	public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart) throws MessagingException {
 225		this(mimeMessage, multipart, null);
 226	}
 227
 228	/**
 229	 * Create a new MimeMessageHelper for the given MimeMessage,
 230	 * in multipart mode (supporting alternative texts, inline
 231	 * elements and attachments) if requested.
 232	 * <p>Consider using the MimeMessageHelper constructor that
 233	 * takes a multipartMode argument to choose a specific multipart
 234	 * mode other than MULTIPART_MODE_MIXED_RELATED.
 235	 * @param mimeMessage MimeMessage to work on
 236	 * @param multipart whether to create a multipart message that
 237	 * supports alternative texts, inline elements and attachments
 238	 * (corresponds to MULTIPART_MODE_MIXED_RELATED)
 239	 * @param encoding the character encoding to use for the message
 240	 * @throws MessagingException if multipart creation failed
 241	 * @see #MimeMessageHelper(javax.mail.internet.MimeMessage, int, String)
 242	 */
 243	public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, String encoding)
 244	    throws MessagingException {
 245
 246		this(mimeMessage, (multipart ? MULTIPART_MODE_MIXED_RELATED : MULTIPART_MODE_NO), encoding);
 247	}
 248
 249	/**
 250	 * Create a new MimeMessageHelper for the given MimeMessage,
 251	 * in multipart mode (supporting alternative texts, inline
 252	 * elements and attachments) if requested.
 253	 * <p>The character encoding for the message will be taken from
 254	 * the passed-in MimeMessage object, if carried there. Else,
 255	 * JavaMail's default encoding will be used.
 256	 * @param mimeMessage MimeMessage to work on
 257	 * @param multipartMode which kind of multipart message to create
 258	 * (MIXED, RELATED, MIXED_RELATED, or NO)
 259	 * @throws MessagingException if multipart creation failed
 260	 * @see #MULTIPART_MODE_NO
 261	 * @see #MULTIPART_MODE_MIXED
 262	 * @see #MULTIPART_MODE_RELATED
 263	 * @see #MULTIPART_MODE_MIXED_RELATED
 264	 * @see #getDefaultEncoding(javax.mail.internet.MimeMessage)
 265	 * @see JavaMailSenderImpl#setDefaultEncoding
 266	 */
 267	public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode) throws MessagingException {
 268		this(mimeMessage, multipartMode, null);
 269	}
 270
 271	/**
 272	 * Create a new MimeMessageHelper for the given MimeMessage,
 273	 * in multipart mode (supporting alternative texts, inline
 274	 * elements and attachments) if requested.
 275	 * @param mimeMessage MimeMessage to work on
 276	 * @param multipartMode which kind of multipart message to create
 277	 * (MIXED, RELATED, MIXED_RELATED, or NO)
 278	 * @param encoding the character encoding to use for the message
 279	 * @throws MessagingException if multipart creation failed
 280	 * @see #MULTIPART_MODE_NO
 281	 * @see #MULTIPART_MODE_MIXED
 282	 * @see #MULTIPART_MODE_RELATED
 283	 * @see #MULTIPART_MODE_MIXED_RELATED
 284	 */
 285	public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode, String encoding)
 286	    throws MessagingException {
 287
 288		this.mimeMessage = mimeMessage;
 289		createMimeMultiparts(mimeMessage, multipartMode);
 290		this.encoding = (encoding != null ? encoding : getDefaultEncoding(mimeMessage));
 291		this.fileTypeMap = getDefaultFileTypeMap(mimeMessage);
 292	}
 293
 294
 295	/**
 296	 * Return the underlying MimeMessage object.
 297	 */
 298	public final MimeMessage getMimeMessage() {
 299		return this.mimeMessage;
 300	}
 301
 302
 303	/**
 304	 * Determine the MimeMultipart objects to use, which will be used
 305	 * to store attachments on the one hand and text(s) and inline elements
 306	 * on the other hand.
 307	 * <p>Texts and inline elements can either be stored in the root element
 308	 * itself (MULTIPART_MODE_MIXED, MULTIPART_MODE_RELATED) or in a nested element
 309	 * rather than the root element directly (MULTIPART_MODE_MIXED_RELATED).
 310	 * <p>By default, the root MimeMultipart element will be of type "mixed"
 311	 * (MULTIPART_MODE_MIXED) or "related" (MULTIPART_MODE_RELATED).
 312	 * The main multipart element will either be added as nested element of
 313	 * type "related" (MULTIPART_MODE_MIXED_RELATED) or be identical to the root
 314	 * element itself (MULTIPART_MODE_MIXED, MULTIPART_MODE_RELATED).
 315	 * @param mimeMessage the MimeMessage object to add the root MimeMultipart
 316	 * object to
 317	 * @param multipartMode the multipart mode, as passed into the constructor
 318	 * (MIXED, RELATED, MIXED_RELATED, or NO)
 319	 * @throws MessagingException if multipart creation failed
 320	 * @see #setMimeMultiparts
 321	 * @see #MULTIPART_MODE_NO
 322	 * @see #MULTIPART_MODE_MIXED
 323	 * @see #MULTIPART_MODE_RELATED
 324	 * @see #MULTIPART_MODE_MIXED_RELATED
 325	 */
 326	protected void createMimeMultiparts(MimeMessage mimeMessage, int multipartMode) throws MessagingException {
 327		switch (multipartMode) {
 328			case MULTIPART_MODE_NO:
 329				setMimeMultiparts(null, null);
 330				break;
 331			case MULTIPART_MODE_MIXED:
 332				MimeMultipart mixedMultipart = new MimeMultipart(MULTIPART_SUBTYPE_MIXED);
 333				mimeMessage.setContent(mixedMultipart);
 334				setMimeMultiparts(mixedMultipart, mixedMultipart);
 335				break;
 336			case MULTIPART_MODE_RELATED:
 337				MimeMultipart relatedMultipart = new MimeMultipart(MULTIPART_SUBTYPE_RELATED);
 338				mimeMessage.setContent(relatedMultipart);
 339				setMimeMultiparts(relatedMultipart, relatedMultipart);
 340				break;
 341			case MULTIPART_MODE_MIXED_RELATED:
 342				MimeMultipart rootMixedMultipart = new MimeMultipart(MULTIPART_SUBTYPE_MIXED);
 343				mimeMessage.setContent(rootMixedMultipart);
 344				MimeMultipart nestedRelatedMultipart = new MimeMultipart(MULTIPART_SUBTYPE_RELATED);
 345				MimeBodyPart relatedBodyPart = new MimeBodyPart();
 346				relatedBodyPart.setContent(nestedRelatedMultipart);
 347				rootMixedMultipart.addBodyPart(relatedBodyPart);
 348				setMimeMultiparts(rootMixedMultipart, nestedRelatedMultipart);
 349				break;
 350			default:
 351				throw new IllegalArgumentException("Only multipart modes MIXED_RELATED, RELATED and NO supported");
 352		}
 353	}
 354
 355	/**
 356	 * Set the given MimeMultipart objects for use by this MimeMessageHelper.
 357	 * @param root the root MimeMultipart object, which attachments will be added to;
 358	 * or <code>null</code> to indicate no multipart at all
 359	 * @param main the main MimeMultipart object, which text(s) and inline elements
 360	 * will be added to (can be the same as the root multipart object, or an element
 361	 * nested underneath the root multipart element)
 362	 */
 363	protected final void setMimeMultiparts(MimeMultipart root, MimeMultipart main) {
 364		this.rootMimeMultipart = root;
 365		this.mimeMultipart = main;
 366	}
 367
 368	/**
 369	 * Return whether this helper is in multipart mode,
 370	 * i.e. whether it holds a multipart message.
 371	 * @see #MimeMessageHelper(MimeMessage, boolean)
 372	 */
 373	public final boolean isMultipart() {
 374		return (this.rootMimeMultipart != null);
 375	}
 376
 377	/**
 378	 * Throw an IllegalStateException if this helper is not in multipart mode.
 379	 */
 380	private void checkMultipart() throws IllegalStateException {
 381		if (!isMultipart()) {
 382			throw new IllegalStateException("Not in multipart mode - " +
 383			    "create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " +
 384			    "if you need to set alternative texts or add inline elements or attachments.");
 385		}
 386	}
 387
 388	/**
 389	 * Return the root MIME "multipart/mixed" object, if any.
 390	 * Can be used to manually add attachments.
 391	 * <p>This will be the direct content of the MimeMessage,
 392	 * in case of a multipart mail.
 393	 * @throws IllegalStateException if this helper is not in multipart mode
 394	 * @see #isMultipart
 395	 * @see #getMimeMessage
 396	 * @see javax.mail.internet.MimeMultipart#addBodyPart
 397	 */
 398	public final MimeMultipart getRootMimeMultipart() throws IllegalStateException {
 399		checkMultipart();
 400		return this.rootMimeMultipart;
 401	}
 402
 403	/**
 404	 * Return the underlying MIME "multipart/related" object, if any.
 405	 * Can be used to manually add body parts, inline elements, etc.
 406	 * <p>This will be nested within the root MimeMultipart,
 407	 * in case of a multipart mail.
 408	 * @throws IllegalStateException if this helper is not in multipart mode
 409	 * @see #isMultipart
 410	 * @see #getRootMimeMultipart
 411	 * @see javax.mail.internet.MimeMultipart#addBodyPart
 412	 */
 413	public final MimeMultipart getMimeMultipart() throws IllegalStateException {
 414		checkMultipart();
 415		return this.mimeMultipart;
 416	}
 417
 418
 419	/**
 420	 * Determine the default encoding for the given MimeMessage.
 421	 * @param mimeMessage the passed-in MimeMessage
 422	 * @return the default encoding associated with the MimeMessage,
 423	 * or <code>null</code> if none found
 424	 */
 425	protected String getDefaultEncoding(MimeMessage mimeMessage) {
 426		if (mimeMessage instanceof SmartMimeMessage) {
 427			return ((SmartMimeMessage) mimeMessage).getDefaultEncoding();
 428		}
 429		return null;
 430	}
 431
 432	/**
 433	 * Return the specific character encoding used for this message, if any.
 434	 */
 435	public String getEncoding() {
 436		return this.encoding;
 437	}
 438
 439	/**
 440	 * Determine the default Java Activation FileTypeMap for the given MimeMessage.
 441	 * @param mimeMessage the passed-in MimeMessage
 442	 * @return the default FileTypeMap associated with the MimeMessage,
 443	 * or a default ConfigurableMimeFileTypeMap if none found for the message
 444	 * @see ConfigurableMimeFileTypeMap
 445	 */
 446	protected FileTypeMap getDefaultFileTypeMap(MimeMessage mimeMessage) {
 447		if (mimeMessage instanceof SmartMimeMessage) {
 448			FileTypeMap fileTypeMap = ((SmartMimeMessage) mimeMessage).getDefaultFileTypeMap();
 449			if (fileTypeMap != null) {
 450				return fileTypeMap;
 451			}
 452		}
 453		ConfigurableMimeFileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap();
 454		fileTypeMap.afterPropertiesSet();
 455		return fileTypeMap;
 456	}
 457
 458	/**
 459	 * Set the Java Activation Framework <code>FileTypeMap</code> to use
 460	 * for determining the content type of inline content and attachments
 461	 * that get added to the message.
 462	 * <p>Default is the <code>FileTypeMap</code> that the underlying
 463	 * MimeMessage carries, if any, or the Activation Framework's default
 464	 * <code>FileTypeMap</code> instance else.
 465	 * @see #addInline
 466	 * @see #addAttachment
 467	 * @see #getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
 468	 * @see JavaMailSenderImpl#setDefaultFileTypeMap
 469	 * @see javax.activation.FileTypeMap#getDefaultFileTypeMap
 470	 * @see ConfigurableMimeFileTypeMap
 471	 */
 472	public void setFileTypeMap(FileTypeMap fileTypeMap) {
 473		this.fileTypeMap = (fileTypeMap != null ? fileTypeMap : getDefaultFileTypeMap(getMimeMessage()));
 474	}
 475
 476	/**
 477	 * Return the <code>FileTypeMap</code> used by this MimeMessageHelper.
 478	 */
 479	public FileTypeMap getFileTypeMap() {
 480		return this.fileTypeMap;
 481	}
 482
 483
 484	/**
 485	 * Set whether to validate all addresses which get passed to this helper.
 486	 * Default is "false".
 487	 * <p>Note that this is by default just available for JavaMail >= 1.3.
 488	 * You can override the default <code>validateAddress method</code> for
 489	 * validation on older JavaMail versions (or for custom validation).
 490	 * @see #validateAddress
 491	 */
 492	public void setValidateAddresses(boolean validateAddresses) {
 493		this.validateAddresses = validateAddresses;
 494	}
 495
 496	/**
 497	 * Return whether this helper will validate all addresses passed to it.
 498	 */
 499	public boolean isValidateAddresses() {
 500		return this.validateAddresses;
 501	}
 502
 503	/**
 504	 * Validate the given mail address.
 505	 * Called by all of MimeMessageHelper's address setters and adders.
 506	 * <p>Default implementation invokes <code>InternetAddress.validate()</code>,
 507	 * provided that address validation is activated for the helper instance.
 508	 * <p>Note that this method will just work on JavaMail >= 1.3. You can override
 509	 * it for validation on older JavaMail versions or for custom validation.
 510	 * @param address the address to validate
 511	 * @throws AddressException if validation failed
 512	 * @see #isValidateAddresses()
 513	 * @see javax.mail.internet.InternetAddress#validate()
 514	 */
 515	protected void validateAddress(InternetAddress address) throws AddressException {
 516		if (isValidateAddresses()) {
 517			address.validate();
 518		}
 519	}
 520
 521	/**
 522	 * Validate all given mail addresses.
 523	 * Default implementation simply delegates to validateAddress for each address.
 524	 * @param addresses the addresses to validate
 525	 * @throws AddressException if validation failed
 526	 * @see #validateAddress(InternetAddress)
 527	 */
 528	protected void validateAddresses(InternetAddress[] addresses) throws AddressException {
 529		for (InternetAddress address : addresses) {
 530			validateAddress(address);
 531		}
 532	}
 533
 534
 535	public void setFrom(InternetAddress from) throws MessagingException {
 536		Assert.notNull(from, "From address must not be null");
 537		validateAddress(from);
 538		this.mimeMessage.setFrom(from);
 539	}
 540
 541	public void setFrom(String from) throws MessagingException {
 542		Assert.notNull(from, "From address must not be null");
 543		setFrom(parseAddress(from));
 544	}
 545
 546	public void setFrom(String from, String personal) throws MessagingException, UnsupportedEncodingException {
 547		Assert.notNull(from, "From address must not be null");
 548		setFrom(getEncoding() != null ?
 549		    new InternetAddress(from, personal, getEncoding()) : new InternetAddress(from, personal));
 550	}
 551
 552	public void setReplyTo(InternetAddress replyTo) throws MessagingException {
 553		Assert.notNull(replyTo, "Reply-to address must not be null");
 554		validateAddress(replyTo);
 555		this.mimeMessage.setReplyTo(new InternetAddress[] {replyTo});
 556	}
 557
 558	public void setReplyTo(String replyTo) throws MessagingException {
 559		Assert.notNull(replyTo, "Reply-to address must not be null");
 560		setReplyTo(parseAddress(replyTo));
 561	}
 562
 563	public void setReplyTo(String replyTo, String personal) throws MessagingException, UnsupportedEncodingException {
 564		Assert.notNull(replyTo, "Reply-to address must not be null");
 565		InternetAddress replyToAddress = (getEncoding() != null) ?
 566				new InternetAddress(replyTo, personal, getEncoding()) : new InternetAddress(replyTo, personal);
 567		setReplyTo(replyToAddress);
 568	}
 569
 570
 571	public void setTo(InternetAddress to) throws MessagingException {
 572		Assert.notNull(to, "To address must not be null");
 573		validateAddress(to);
 574		this.mimeMessage.setRecipient(Message.RecipientType.TO, to);
 575	}
 576
 577	public void setTo(InternetAddress[] to) throws MessagingException {
 578		Assert.notNull(to, "To address array must not be null");
 579		validateAddresses(to);
 580		this.mimeMessage.setRecipients(Message.RecipientType.TO, to);
 581	}
 582
 583	public void setTo(String to) throws MessagingException {
 584		Assert.notNull(to, "To address must not be null");
 585		setTo(parseAddress(to));
 586	}
 587
 588	public void setTo(String[] to) throws MessagingException {
 589		Assert.notNull(to, "To address array must not be null");
 590		InternetAddress[] addresses = new InternetAddress[to.length];
 591		for (int i = 0; i < to.length; i++) {
 592			addresses[i] = parseAddress(to[i]);
 593		}
 594		setTo(addresses);
 595	}
 596
 597	public void addTo(InternetAddress to) throws MessagingException {
 598		Assert.notNull(to, "To address must not be null");
 599		validateAddress(to);
 600		this.mimeMessage.addRecipient(Message.RecipientType.TO, to);
 601	}
 602
 603	public void addTo(String to) throws MessagingException {
 604		Assert.notNull(to, "To address must not be null");
 605		addTo(parseAddress(to));
 606	}
 607
 608	public void addTo(String to, String personal) throws MessagingException, UnsupportedEncodingException {
 609		Assert.notNull(to, "To address must not be null");
 610		addTo(getEncoding() != null ?
 611		    new InternetAddress(to, personal, getEncoding()) :
 612		    new InternetAddress(to, personal));
 613	}
 614
 615
 616	public void setCc(InternetAddress cc) throws MessagingException {
 617		Assert.notNull(cc, "Cc address must not be null");
 618		validateAddress(cc);
 619		this.mimeMessage.setRecipient(Message.RecipientType.CC, cc);
 620	}
 621
 622	public void setCc(InternetAddress[] cc) throws MessagingException {
 623		Assert.notNull(cc, "Cc address array must not be null");
 624		validateAddresses(cc);
 625		this.mimeMessage.setRecipients(Message.RecipientType.CC, cc);
 626	}
 627
 628	public void setCc(String cc) throws MessagingException {
 629		Assert.notNull(cc, "Cc address must not be null");
 630		setCc(parseAddress(cc));
 631	}
 632
 633	public void setCc(String[] cc) throws MessagingException {
 634		Assert.notNull(cc, "Cc address array must not be null");
 635		InternetAddress[] addresses = new InternetAddress[cc.length];
 636		for (int i = 0; i < cc.length; i++) {
 637			addresses[i] = parseAddress(cc[i]);
 638		}
 639		setCc(addresses);
 640	}
 641
 642	public void addCc(InternetAddress cc) throws MessagingException {
 643		Assert.notNull(cc, "Cc address must not be null");
 644		validateAddress(cc);
 645		this.mimeMessage.addRecipient(Message.RecipientType.CC, cc);
 646	}
 647
 648	public void addCc(String cc) throws MessagingException {
 649		Assert.notNull(cc, "Cc address must not be null");
 650		addCc(parseAddress(cc));
 651	}
 652
 653	public void addCc(String cc, String personal) throws MessagingException, UnsupportedEncodingException {
 654		Assert.notNull(cc, "Cc address must not be null");
 655		addCc(getEncoding() != null ?
 656		    new InternetAddress(cc, personal, getEncoding()) :
 657		    new InternetAddress(cc, personal));
 658	}
 659
 660
 661	public void setBcc(InternetAddress bcc) throws MessagingException {
 662		Assert.notNull(bcc, "Bcc address must not be null");
 663		validateAddress(bcc);
 664		this.mimeMessage.setRecipient(Message.RecipientType.BCC, bcc);
 665	}
 666
 667	public void setBcc(InternetAddress[] bcc) throws MessagingException {
 668		Assert.notNull(bcc, "Bcc address array must not be null");
 669		validateAddresses(bcc);
 670		this.mimeMessage.setRecipients(Message.RecipientType.BCC, bcc);
 671	}
 672
 673	public void setBcc(String bcc) throws MessagingException {
 674		Assert.notNull(bcc, "Bcc address must not be null");
 675		setBcc(parseAddress(bcc));
 676	}
 677
 678	public void setBcc(String[] bcc) throws MessagingException {
 679		Assert.notNull(bcc, "Bcc address array must not be null");
 680		InternetAddress[] addresses = new InternetAddress[bcc.length];
 681		for (int i = 0; i < bcc.length; i++) {
 682			addresses[i] = parseAddress(bcc[i]);
 683		}
 684		setBcc(addresses);
 685	}
 686
 687	public void addBcc(InternetAddress bcc) throws MessagingException {
 688		Assert.notNull(bcc, "Bcc address must not be null");
 689		validateAddress(bcc);
 690		this.mimeMessage.addRecipient(Message.RecipientType.BCC, bcc);
 691	}
 692
 693	public void addBcc(String bcc) throws MessagingException {
 694		Assert.notNull(bcc, "Bcc address must not be null");
 695		addBcc(parseAddress(bcc));
 696	}
 697
 698	public void addBcc(String bcc, String personal) throws MessagingException, UnsupportedEncodingException {
 699		Assert.notNull(bcc, "Bcc address must not be null");
 700		addBcc(getEncoding() != null ?
 701		    new InternetAddress(bcc, personal, getEncoding()) :
 702		    new InternetAddress(bcc, personal));
 703	}
 704
 705	private InternetAddress parseAddress(String address) throws MessagingException {
 706		InternetAddress[] parsed = InternetAddress.parse(address);
 707		if (parsed.length != 1) {
 708			throw new AddressException("Illegal address", address);
 709		}
 710		InternetAddress raw = parsed[0];
 711		try {
 712			return (getEncoding() != null ?
 713					new InternetAddress(raw.getAddress(), raw.getPersonal(), getEncoding()) : raw);
 714		}
 715		catch (UnsupportedEncodingException ex) {
 716			throw new MessagingException("Failed to parse embedded personal name to correct encoding", ex);
 717		}
 718	}
 719
 720
 721	/**
 722	 * Set the priority ("X-Priority" header) of the message.
 723	 * @param priority the priority value;
 724	 * typically between 1 (highest) and 5 (lowest)
 725	 * @throws MessagingException in case of errors
 726	 */
 727	public void setPriority(int priority) throws MessagingException {
 728		this.mimeMessage.setHeader(HEADER_PRIORITY, Integer.toString(priority));
 729	}
 730
 731	/**
 732	 * Set the sent-date of the message.
 733	 * @param sentDate the date to set (never <code>null</code>)
 734	 * @throws MessagingException in case of errors
 735	 */
 736	public void setSentDate(Date sentDate) throws MessagingException {
 737		Assert.notNull(sentDate, "Sent date must not be null");
 738		this.mimeMessage.setSentDate(sentDate);
 739	}
 740
 741	/**
 742	 * Set the subject of the message, using the correct encoding.
 743	 * @param subject the subject text
 744	 * @throws MessagingException in case of errors
 745	 */
 746	public void setSubject(String subject) throws MessagingException {
 747		Assert.notNull(subject, "Subject must not be null");
 748		if (getEncoding() != null) {
 749			this.mimeMessage.setSubject(subject, getEncoding());
 750		}
 751		else {
 752			this.mimeMessage.setSubject(subject);
 753		}
 754	}
 755
 756
 757	/**
 758	 * Set the given text directly as content in non-multipart mode
 759	 * or as default body part in multipart mode.
 760	 * Always applies the default content type "text/plain".
 761	 * <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
 762	 * else, mail readers might not be able to resolve inline references correctly.
 763	 * @param text the text for the message
 764	 * @throws MessagingException in case of errors
 765	 */
 766	public void setText(String text) throws MessagingException {
 767		setText(text, false);
 768	}
 769
 770	/**
 771	 * Set the given text directly as content in non-multipart mode
 772	 * or as default body part in multipart mode.
 773	 * The "html" flag determines the content type to apply.
 774	 * <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
 775	 * else, mail readers might not be able to resolve inline references correctly.
 776	 * @param text the text for the message
 777	 * @param html whether to apply content type "text/html" for an
 778	 * HTML mail, using default content type ("text/plain") else
 779	 * @throws MessagingException in case of errors
 780	 */
 781	public void setText(String text, boolean html) throws MessagingException {
 782		Assert.notNull(text, "Text must not be null");
 783		MimePart partToUse;
 784		if (isMultipart()) {
 785			partToUse = getMainPart();
 786		}
 787		else {
 788			partToUse = this.mimeMessage;
 789		}
 790		if (html) {
 791			setHtmlTextToMimePart(partToUse, text);
 792		}
 793		else {
 794			setPlainTextToMimePart(partToUse, text);
 795		}
 796	}
 797
 798	/**
 799	 * Set the given plain text and HTML text as alternatives, offering
 800	 * both options to the email client. Requires multipart mode.
 801	 * <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
 802	 * else, mail readers might not be able to resolve inline references correctly.
 803	 * @param plainText the plain text for the message
 804	 * @param htmlText the HTML text for the message
 805	 * @throws MessagingException in case of errors
 806	 */
 807	public void setText(String plainText, String htmlText) throws MessagingException {
 808		Assert.notNull(plainText, "Plain text must not be null");
 809		Assert.notNull(htmlText, "HTML text must not be null");
 810
 811		MimeMultipart messageBody = new MimeMultipart(MULTIPART_SUBTYPE_ALTERNATIVE);
 812		getMainPart().setContent(messageBody, CONTENT_TYPE_ALTERNATIVE);
 813
 814		// Create the plain text part of the message.
 815		MimeBodyPart plainTextPart = new MimeBodyPart();
 816		setPlainTextToMimePart(plainTextPart, plainText);
 817		messageBody.addBodyPart(plainTextPart);
 818
 819		// Create the HTML text part of the message.
 820		MimeBodyPart htmlTextPart = new MimeBodyPart();
 821		setHtmlTextToMimePart(htmlTextPart, htmlText);
 822		messageBody.addBodyPart(htmlTextPart);
 823	}
 824
 825	private MimeBodyPart getMainPart() throws MessagingException {
 826		MimeMultipart mimeMultipart = getMimeMultipart();
 827		MimeBodyPart bodyPart = null;
 828		for (int i = 0; i < mimeMultipart.getCount(); i++) {
 829			BodyPart bp = mimeMultipart.getBodyPart(i);
 830			if (bp.getFileName() == null) {
 831				bodyPart = (MimeBodyPart) bp;
 832			}
 833		}
 834		if (bodyPart == null) {
 835			MimeBodyPart mimeBodyPart = new MimeBodyPart();
 836			mimeMultipart.addBodyPart(mimeBodyPart);
 837			bodyPart = mimeBodyPart;
 838		}
 839		return bodyPart;
 840	}
 841
 842	private void setPlainTextToMimePart(MimePart mimePart, String text) throws MessagingException {
 843		if (getEncoding() != null) {
 844			mimePart.setText(text, getEncoding());
 845		}
 846		else {
 847			mimePart.setText(text);
 848		}
 849	}
 850
 851	private void setHtmlTextToMimePart(MimePart mimePart, String text) throws MessagingException {
 852		if (getEncoding() != null) {
 853			mimePart.setContent(text, CONTENT_TYPE_HTML + CONTENT_TYPE_CHARSET_SUFFIX + getEncoding());
 854		}
 855		else {
 856			mimePart.setContent(text, CONTENT_TYPE_HTML);
 857		}
 858	}
 859
 860
 861	/**
 862	 * Add an inline element to the MimeMessage, taking the content from a
 863	 * <code>javax.activation.DataSource</code>.
 864	 * <p>Note that the InputStream returned by the DataSource implementation
 865	 * needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
 866	 * <code>getInputStream()</code> multiple times.
 867	 * <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
 868	 * else, mail readers might not be able to resolve inline references correctly.
 869	 * @param contentId the content ID to use. Will end up as "Content-ID" header
 870	 * in the body part, surrounded by angle brackets: e.g. "myId" -> "&lt;myId&gt;".
 871	 * Can be referenced in HTML source via src="cid:myId" expressions.
 872	 * @param dataSource the <code>javax.activation.DataSource</code> to take
 873	 * the content from, determining the InputStream and the content type
 874	 * @throws MessagingException in case of errors
 875	 * @see #addInline(String, java.io.File)
 876	 * @see #addInline(String, org.springframework.core.io.Resource)
 877	 */
 878	public void addInline(String contentId, DataSource dataSource) throws MessagingException {
 879		Assert.notNull(contentId, "Content ID must not be null");
 880		Assert.notNull(dataSource, "DataSource must not be null");
 881		MimeBodyPart mimeBodyPart = new MimeBodyPart();
 882		mimeBodyPart.setDisposition(MimeBodyPart.INLINE);
 883		// We're using setHeader here to remain compatible with JavaMail 1.2,
 884		// rather than JavaMail 1.3's setContentID.
 885		mimeBodyPart.setHeader(HEADER_CONTENT_ID, "<" + contentId + ">");
 886		mimeBodyPart.setDataHandler(new DataHandler(dataSource));
 887		getMimeMultipart().addBodyPart(mimeBodyPart);
 888	}
 889
 890	/**
 891	 * Add an inline element to the MimeMessage, taking the content from a
 892	 * <code>java.io.File</code>.
 893	 * <p>The content type will be determined by the name of the given
 894	 * content file. Do not use this for temporary files with arbitrary
 895	 * filenames (possibly ending in ".tmp" or the like)!
 896	 * <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
 897	 * else, mail readers might not be able to resolve inline references correctly.
 898	 * @param contentId the content ID to use. Will end up as "Content-ID" header
 899	 * in the body part, surrounded by angle brackets: e.g. "myId" -> "&lt;myId&gt;".
 900	 * Can be referenced in HTML source via src="cid:myId" expressions.
 901	 * @param file the File resource to take the content from
 902	 * @throws MessagingException in case of errors
 903	 * @see #setText
 904	 * @see #addInline(String, org.springframework.core.io.Resource)
 905	 * @see #addInline(String, javax.activation.DataSource)
 906	 */
 907	public void addInline(String contentId, File file) throws MessagingException {
 908		Assert.notNull(file, "File must not be null");
 909		FileDataSource dataSource = new FileDataSource(file);
 910		dataSource.setFileTypeMap(getFileTypeMap());
 911		addInline(contentId, dataSource);
 912	}
 913
 914	/**
 915	 * Add an inline element to the MimeMessage, taking the content from a
 916	 * <code>org.springframework.core.io.Resource</code>.
 917	 * <p>The content type will be determined by the name of the given
 918	 * content file. Do not use this for temporary files with arbitrary
 919	 * filenames (possibly ending in ".tmp" or the like)!
 920	 * <p>Note that the InputStream returned by the Resource implementation
 921	 * needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
 922	 * <code>getInputStream()</code> multiple times.
 923	 * <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
 924	 * else, mail readers might not be able to resolve inline references correctly.
 925	 * @param contentId the content ID to use. Will end up as "Content-ID" header
 926	 * in the body part, surrounded by angle brackets: e.g. "myId" -> "&lt;myId&gt;".
 927	 * Can be referenced in HTML source via src="cid:myId" expressions.
 928	 * @param resource the resource to take the content from
 929	 * @throws MessagingException in case of errors
 930	 * @see #setText
 931	 * @see #addInline(String, java.io.File)
 932	 * @see #addInline(String, javax.activation.DataSource)
 933	 */
 934	public void addInline(String contentId, Resource resource) throws MessagingException {
 935		Assert.notNull(resource, "Resource must not be null");
 936		String contentType = getFileTypeMap().getContentType(resource.getFilename());
 937		addInline(contentId, resource, contentType);
 938	}
 939
 940	/**
 941	 * Add an inline element to the MimeMessage, taking the content from an
 942	 * <code>org.springframework.core.InputStreamResource</code>, and
 943	 * specifying the content type explicitly.
 944	 * <p>You can determine the content type for any given filename via a Java
 945	 * Activation Framework's FileTypeMap, for example the one held by this helper.
 946	 * <p>Note that the InputStream returned by the InputStreamSource implementation
 947	 * needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
 948	 * <code>getInputStream()</code> multiple times.
 949	 * <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> <code>setText</code>;
 950	 * else, mail readers might not be able to resolve inline references correctly.
 951	 * @param contentId the content ID to use. Will end up as "Content-ID" header
 952	 * in the body part, surrounded by angle brackets: e.g. "myId" -> "&lt;myId&gt;".
 953	 * Can be referenced in HTML source via src="cid:myId" expressions.
 954	 * @param inputStreamSource the resource to take the content from
 955	 * @param contentType the content type to use for the element
 956	 * @throws MessagingException in case of errors
 957	 * @see #setText
 958	 * @see #getFileTypeMap
 959	 * @see #addInline(String, org.springframework.core.io.Resource)
 960	 * @see #addInline(String, javax.activation.DataSource)
 961	 */
 962	public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType)
 963	    throws MessagingException {
 964
 965		Assert.notNull(inputStreamSource, "InputStreamSource must not be null");
 966		if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) {
 967			throw new IllegalArgumentException(
 968					"Passed-in Resource contains an open stream: invalid argument. " +
 969					"JavaMail requires an InputStreamSource that creates a fresh stream for every call.");
 970		}
 971		DataSource dataSource = createDataSource(inputStreamSource, contentType, "inline");
 972		addInline(contentId, dataSource);
 973	}
 974
 975	/**
 976	 * Add an attachment to the MimeMessage, taking the content from a
 977	 * <code>javax.activation.DataSource</code>.
 978	 * <p>Note that the InputStream returned by the DataSource implementation
 979	 * needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
 980	 * <code>getInputStream()</code> multiple times.
 981	 * @param attachmentFilename the name of the attachment as it will
 982	 * appear in the mail (the content type will be determined by this)
 983	 * @param dataSource the <code>javax.activation.DataSource</code> to take
 984	 * the content from, determining the InputStream and the content type
 985	 * @throws MessagingException in case of errors
 986	 * @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
 987	 * @see #addAttachment(String, java.io.File)
 988	 */
 989	public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException {
 990		Assert.notNull(attachmentFilename, "Attachment filename must not be null");
 991		Assert.notNull(dataSource, "DataSource must not be null");
 992		MimeBodyPart mimeBodyPart = new MimeBodyPart();
 993		mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT);
 994		mimeBodyPart.setFileName(attachmentFilename);
 995		mimeBodyPart.setDataHandler(new DataHandler(dataSource));
 996		getRootMimeMultipart().addBodyPart(mimeBodyPart);
 997	}
 998
 999	/**
1000	 * Add an attachment to the MimeMessage, taking the content from a
1001	 * <code>java.io.File</code>.
1002	 * <p>The content type will be determined by the name of the given
1003	 * content file. Do not use this for temporary files with arbitrary
1004	 * filenames (possibly ending in ".tmp" or the like)!
1005	 * @param attachmentFilename the name of the attachment as it will
1006	 * appear in the mail
1007	 * @param file the File resource to take the content from
1008	 * @throws MessagingException in case of errors
1009	 * @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
1010	 * @see #addAttachment(String, javax.activation.DataSource)
1011	 */
1012	public void addAttachment(String attachmentFilename, File file) throws MessagingException {
1013		Assert.notNull(file, "File must not be null");
1014		FileDataSource dataSource = new FileDataSource(file);
1015		dataSource.setFileTypeMap(getFileTypeMap());
1016		addAttachment(attachmentFilename, dataSource);
1017	}
1018
1019	/**
1020	 * Add an attachment to the MimeMessage, taking the content from an
1021	 * <code>org.springframework.core.io.InputStreamResource</code>.
1022	 * <p>The content type will be determined by the given filename for
1023	 * the attachment. Thus, any content source will be fine, including
1024	 * temporary files with arbitrary filenames.
1025	 * <p>Note that the InputStream returned by the InputStreamSource
1026	 * implementation needs to be a <i>fresh one on each call</i>, as
1027	 * JavaMail will invoke <code>getInputStream()</code> multiple times.
1028	 * @param attachmentFilename the name of the attachment as it will
1029	 * appear in the mail
1030	 * @param inputStreamSource the resource to take the content from
1031	 * (all of Spring's Resource implementations can be passed in here)
1032	 * @throws MessagingException in case of errors
1033	 * @see #addAttachment(String, java.io.File)
1034	 * @see #addAttachment(String, javax.activation.DataSource)
1035	 * @see org.springframework.core.io.Resource
1036	 */
1037	public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource)
1038	    throws MessagingException {
1039
1040		String contentType = getFileTypeMap().getContentType(attachmentFilename);
1041		addAttachment(attachmentFilename, inputStreamSource, contentType);
1042	}
1043
1044	/**
1045	 * Add an attachment to the MimeMessage, taking the content from an
1046	 * <code>org.springframework.core.io.InputStreamResource</code>.
1047	 * <p>Note that the InputStream returned by the InputStreamSource
1048	 * implementation needs to be a <i>fresh one on each call</i>, as
1049	 * JavaMail will invoke <code>getInputStream()</code> multiple times.
1050	 * @param attachmentFilename the name of the attachment as it will
1051	 * appear in the mail
1052	 * @param inputStreamSource the resource to take the content from
1053	 * (all of Spring's Resource implementations can be passed in here)
1054	 * @param contentType the content type to use for the element
1055	 * @throws MessagingException in case of errors
1056	 * @see #addAttachment(String, java.io.File)
1057	 * @see #addAttachment(String, javax.activation.DataSource)
1058	 * @see org.springframework.core.io.Resource
1059	 */
1060	public void addAttachment(
1061			String attachmentFilename, InputStreamSource inputStreamSource, String contentType)
1062	    throws MessagingException {
1063
1064		Assert.notNull(inputStreamSource, "InputStreamSource must not be null");
1065		if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) {
1066			throw new IllegalArgumentException(
1067					"Passed-in Resource contains an open stream: invalid argument. " +
1068					"JavaMail requires an InputStreamSource that creates a fresh stream for every call.");
1069		}
1070		DataSource dataSource = createDataSource(inputStreamSource, contentType, attachmentFilename);
1071		addAttachment(attachmentFilename, dataSource);
1072	}
1073
1074	/**
1075	 * Create an Activation Framework DataSource for the given InputStreamSource.
1076	 * @param inputStreamSource the InputStreamSource (typically a Spring Resource)
1077	 * @param contentType the content type
1078	 * @param name the name of the DataSource
1079	 * @return the Activation Framework DataSource
1080	 */
1081	protected DataSource createDataSource(
1082	    final InputStreamSource inputStreamSource, final String contentType, final String name) {
1083
1084		return new DataSource() {
1085			public InputStream getInputStream() throws IOException {
1086				return inputStreamSource.getInputStream();
1087			}
1088			public OutputStream getOutputStream() {
1089				throw new UnsupportedOperationException("Read-only javax.activation.DataSource");
1090			}
1091			public String getContentType() {
1092				return contentType;
1093			}
1094			public String getName() {
1095				return name;
1096			}
1097		};
1098	}
1099
1100}