PageRenderTime 50ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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