001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.mail;
018
019 import java.io.IOException;
020 import java.io.UnsupportedEncodingException;
021 import java.nio.charset.Charset;
022 import java.nio.charset.IllegalCharsetNameException;
023 import java.util.Enumeration;
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.util.Map;
027
028 import javax.activation.DataHandler;
029 import javax.activation.DataSource;
030 import javax.mail.Address;
031 import javax.mail.BodyPart;
032 import javax.mail.Header;
033 import javax.mail.Message;
034 import javax.mail.MessagingException;
035 import javax.mail.Multipart;
036 import javax.mail.Part;
037 import javax.mail.internet.InternetAddress;
038 import javax.mail.internet.MimeBodyPart;
039 import javax.mail.internet.MimeMessage;
040 import javax.mail.internet.MimeMultipart;
041 import javax.mail.util.ByteArrayDataSource;
042
043 import org.apache.camel.Exchange;
044 import org.apache.camel.RuntimeCamelException;
045 import org.apache.camel.converter.IOConverter;
046 import org.apache.camel.converter.ObjectConverter;
047 import org.apache.camel.impl.DefaultHeaderFilterStrategy;
048 import org.apache.camel.spi.HeaderFilterStrategy;
049 import org.apache.camel.util.CollectionHelper;
050 import org.apache.camel.util.ObjectHelper;
051 import org.apache.commons.logging.Log;
052 import org.apache.commons.logging.LogFactory;
053
054 /**
055 * A Strategy used to convert between a Camel {@link Exchange} and {@link Message} to and
056 * from a Mail {@link MimeMessage}
057 *
058 * @version $Revision: 20052 $
059 */
060 public class MailBinding {
061
062 private static final transient Log LOG = LogFactory.getLog(MailBinding.class);
063 private HeaderFilterStrategy headerFilterStrategy;
064 private ContentTypeResolver contentTypeResolver;
065
066 public MailBinding() {
067 headerFilterStrategy = new DefaultHeaderFilterStrategy();
068 }
069
070 public MailBinding(HeaderFilterStrategy headerFilterStrategy, ContentTypeResolver contentTypeResolver) {
071 this.headerFilterStrategy = headerFilterStrategy;
072 this.contentTypeResolver = contentTypeResolver;
073 }
074
075 public void populateMailMessage(MailEndpoint endpoint, MimeMessage mimeMessage, Exchange exchange)
076 throws MessagingException, IOException {
077
078 // camel message headers takes precedence over endpoint configuration
079 if (hasRecipientHeaders(exchange)) {
080 setRecipientFromCamelMessage(mimeMessage, exchange);
081 } else {
082 // fallback to endpoint configuration
083 setRecipientFromEndpointConfiguration(mimeMessage, endpoint);
084 }
085
086 // must have at least one recipients otherwise we do not know where to send the mail
087 if (mimeMessage.getAllRecipients() == null) {
088 throw new IllegalArgumentException("The mail message does not have any recipients set.");
089 }
090
091 // set the subject if it was passed in as an option in the uri. Note: if it is in both the URI
092 // and headers the headers win.
093 String subject = endpoint.getConfiguration().getSubject();
094 if (subject != null) {
095 mimeMessage.setSubject(subject, IOConverter.getCharsetName(exchange, false));
096 }
097
098 // append the rest of the headers (no recipients) that could be subject, reply-to etc.
099 appendHeadersFromCamelMessage(mimeMessage, endpoint.getConfiguration(), exchange);
100
101 if (empty(mimeMessage.getFrom())) {
102 // lets default the address to the endpoint destination
103 String from = endpoint.getConfiguration().getFrom();
104 mimeMessage.setFrom(new InternetAddress(from));
105 }
106
107 // if there is an alternative body provided, set up a mime multipart alternative message
108 if (hasAlternativeBody(endpoint.getConfiguration(), exchange)) {
109 createMultipartAlternativeMessage(mimeMessage, endpoint.getConfiguration(), exchange);
110 } else {
111 if (exchange.getIn().hasAttachments()) {
112 appendAttachmentsFromCamel(mimeMessage, endpoint.getConfiguration(), exchange);
113 } else {
114 populateContentOnMimeMessage(mimeMessage, endpoint.getConfiguration(), exchange);
115 }
116 }
117 }
118
119 protected String determineContentType(MailConfiguration configuration, Exchange exchange) {
120 // see if we got any content type set
121 String contentType = configuration.getContentType();
122 if (exchange.getIn().getHeader("contentType") != null) {
123 contentType = exchange.getIn().getHeader("contentType", String.class);
124 } else if (exchange.getIn().getHeader(Exchange.CONTENT_TYPE) != null) {
125 contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
126 }
127
128 // fix content type to include a space after semi colon if missing
129 if (contentType != null && contentType.contains(";")) {
130 String before = ObjectHelper.before(contentType, ";");
131 String charset = determineCharSet(configuration, exchange);
132
133 if (before != null && charset == null) {
134 contentType = before.trim();
135 } else if (before != null && charset != null) {
136 contentType = before.trim() + "; charset=" + charset;
137 }
138 }
139
140 // set the charset if exchange has the charset name as fall back
141 if (contentType != null && !contentType.contains(";") && IOConverter.getCharsetName(exchange, false) != null) {
142 contentType = contentType.trim() + "; charset=" + IOConverter.getCharsetName(exchange, false);
143 }
144
145 if (LOG.isTraceEnabled()) {
146 LOG.trace("Determined Content-Type: " + contentType);
147 }
148
149 return contentType;
150 }
151
152 protected String determineCharSet(MailConfiguration configuration, Exchange exchange) {
153
154 // see if we got any content type set
155 String contentType = configuration.getContentType();
156 if (exchange.getIn().getHeader("contentType") != null) {
157 contentType = exchange.getIn().getHeader("contentType", String.class);
158 } else if (exchange.getIn().getHeader(Exchange.CONTENT_TYPE) != null) {
159 contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
160 }
161
162 // fix content type to include a space after semi colon if missing
163 if (contentType != null && contentType.contains(";")) {
164 String after = ObjectHelper.after(contentType, ";");
165
166 // after is the charset lets see if its given and a valid charset
167 if (after != null) {
168 String charset = ObjectHelper.after(after, "=");
169 if (charset != null) {
170 boolean supported;
171 try {
172 supported = Charset.isSupported(charset);
173 } catch (IllegalCharsetNameException e) {
174 supported = false;
175 }
176 if (supported) {
177 return charset;
178 } else if (!configuration.isIgnoreUnsupportedCharset()) {
179 return charset;
180 } else if (configuration.isIgnoreUnsupportedCharset()) {
181 LOG.warn("Charset: " + charset + " is not supported, will fallback to use platform default instead.");
182 return null;
183 }
184 }
185 }
186 }
187
188 // Using the charset header of exchange as a fall back
189 return IOConverter.getCharsetName(exchange, false);
190
191
192
193 }
194
195 protected String populateContentOnMimeMessage(MimeMessage part, MailConfiguration configuration, Exchange exchange)
196 throws MessagingException, IOException {
197
198 String contentType = determineContentType(configuration, exchange);
199
200 if (LOG.isTraceEnabled()) {
201 LOG.trace("Using Content-Type " + contentType + " for MimeMessage: " + part);
202 }
203
204 // always store content in a byte array data store to avoid various content type and charset issues
205 DataSource ds = new ByteArrayDataSource(exchange.getIn().getBody(String.class), contentType);
206 part.setDataHandler(new DataHandler(ds));
207
208 // set the content type header afterwards
209 part.setHeader("Content-Type", contentType);
210
211 return contentType;
212 }
213
214 protected String populateContentOnBodyPart(BodyPart part, MailConfiguration configuration, Exchange exchange)
215 throws MessagingException, IOException {
216
217 String contentType = determineContentType(configuration, exchange);
218
219 if (LOG.isTraceEnabled()) {
220 LOG.trace("Using Content-Type " + contentType + " for BodyPart: " + part);
221 }
222
223 // always store content in a byte array data store to avoid various content type and charset issues
224 DataSource ds = new ByteArrayDataSource(exchange.getIn().getBody(String.class), contentType);
225 part.setDataHandler(new DataHandler(ds));
226
227 // set the content type header afterwards
228 part.setHeader("Content-Type", contentType);
229
230 return contentType;
231 }
232
233 /**
234 * Extracts the body from the Mail message
235 */
236 public Object extractBodyFromMail(Exchange exchange, MailMessage mailMessage) {
237 Message message = mailMessage.getMessage();
238 try {
239 return message.getContent();
240 } catch (Exception e) {
241 // try to fix message in case it has an unsupported encoding in the Content-Type header
242 UnsupportedEncodingException uee = ObjectHelper.getException(UnsupportedEncodingException.class, e);
243 if (uee != null) {
244 LOG.debug("Unsupported encoding detected: " + uee.getMessage());
245 try {
246 String contentType = message.getContentType();
247 String type = ObjectHelper.before(contentType, "charset=");
248 if (type != null) {
249 // try again with fixed content type
250 LOG.debug("Trying to extract mail message again with fixed Content-Type: " + type);
251 // Since message is read-only, we need to use a copy
252 MimeMessage messageCopy = new MimeMessage((MimeMessage)message);
253 messageCopy.setHeader("Content-Type", type);
254 Object body = messageCopy.getContent();
255 // If we got this far, our fix worked...
256 // Replace the MailMessage's Message with the copy
257 mailMessage.setMessage(messageCopy);
258 return body;
259 }
260 } catch (Exception e2) {
261 // fall through and let original exception be thrown
262 }
263 }
264
265 throw new RuntimeCamelException("Failed to extract body due to: " + e.getMessage()
266 + ". Exchange: " + exchange + ". Message: " + message, e);
267 }
268 }
269
270 /**
271 * Parses the attachments of the given mail message and adds them to the map
272 *
273 * @param message the mail message with attachments
274 * @param map the map to add found attachments (attachmentFilename is the key)
275 */
276 public void extractAttachmentsFromMail(Message message, Map<String, DataHandler> map)
277 throws javax.mail.MessagingException, IOException {
278
279 LOG.trace("Extracting attachments +++ start +++");
280
281 Object content = message.getContent();
282 if (content instanceof Multipart) {
283 extractAttachmentsFromMultipart((Multipart)content, map);
284 } else if (content != null) {
285 LOG.trace("No attachments to extract as content is not Multipart: " + content.getClass().getName());
286 }
287
288 LOG.trace("Extracting attachments +++ done +++");
289 }
290
291 protected void extractAttachmentsFromMultipart(Multipart mp, Map<String, DataHandler> map)
292 throws javax.mail.MessagingException, IOException {
293
294 for (int i = 0; i < mp.getCount(); i++) {
295 Part part = mp.getBodyPart(i);
296 LOG.trace("Part #" + i + ": " + part);
297
298 if (part.isMimeType("multipart/*")) {
299 LOG.trace("Part #" + i + ": is mimetype: multipart/*");
300 extractAttachmentsFromMultipart((Multipart)part.getContent(), map);
301 } else {
302 String disposition = part.getDisposition();
303 if (LOG.isTraceEnabled()) {
304 LOG.trace("Part #" + i + ": Disposition: " + part.getDisposition());
305 LOG.trace("Part #" + i + ": Description: " + part.getDescription());
306 LOG.trace("Part #" + i + ": ContentType: " + part.getContentType());
307 LOG.trace("Part #" + i + ": FileName: " + part.getFileName());
308 LOG.trace("Part #" + i + ": Size: " + part.getSize());
309 LOG.trace("Part #" + i + ": LineCount: " + part.getLineCount());
310 }
311
312 if (disposition != null && (disposition.equalsIgnoreCase(Part.ATTACHMENT) || disposition.equalsIgnoreCase(Part.INLINE))) {
313 // only add named attachments
314 String fileName = part.getFileName();
315 if (fileName != null) {
316 LOG.debug("Mail contains file attachment: " + fileName);
317 // Parts marked with a disposition of Part.ATTACHMENT are clearly attachments
318 CollectionHelper.appendValue(map, fileName, part.getDataHandler());
319 }
320 }
321 }
322 }
323 }
324
325 /**
326 * Appends the Mail headers from the Camel {@link MailMessage}
327 */
328 protected void appendHeadersFromCamelMessage(MimeMessage mimeMessage, MailConfiguration configuration, Exchange exchange)
329 throws MessagingException {
330
331 for (Map.Entry<String, Object> entry : exchange.getIn().getHeaders().entrySet()) {
332 String headerName = entry.getKey();
333 Object headerValue = entry.getValue();
334 if (headerValue != null) {
335 if (headerFilterStrategy != null
336 && !headerFilterStrategy.applyFilterToCamelHeaders(headerName, headerValue, exchange)) {
337 if (headerName.equalsIgnoreCase("subject")) {
338 mimeMessage.setSubject(asString(exchange, headerValue), IOConverter.getCharsetName(exchange, false));
339 continue;
340 }
341 if (isRecipientHeader(headerName)) {
342 // skip any recipients as they are handled specially
343 continue;
344 }
345
346 // alternative body should also be skipped
347 if (headerName.equalsIgnoreCase(configuration.getAlternativeBodyHeader())) {
348 // skip alternative body
349 continue;
350 }
351
352 // Mail messages can repeat the same header...
353 if (ObjectConverter.isCollection(headerValue)) {
354 Iterator iter = ObjectHelper.createIterator(headerValue);
355 while (iter.hasNext()) {
356 Object value = iter.next();
357 mimeMessage.addHeader(headerName, asString(exchange, value));
358 }
359 } else {
360 mimeMessage.setHeader(headerName, asString(exchange, headerValue));
361 }
362 }
363 }
364 }
365 }
366
367 private void setRecipientFromCamelMessage(MimeMessage mimeMessage, Exchange exchange) throws MessagingException {
368 for (Map.Entry<String, Object> entry : exchange.getIn().getHeaders().entrySet()) {
369 String headerName = entry.getKey();
370 Object headerValue = entry.getValue();
371 if (headerValue != null && isRecipientHeader(headerName)) {
372 // special handling of recipients
373 if (ObjectConverter.isCollection(headerValue)) {
374 Iterator iter = ObjectHelper.createIterator(headerValue);
375 while (iter.hasNext()) {
376 Object recipient = iter.next();
377 appendRecipientToMimeMessage(mimeMessage, headerName, asString(exchange, recipient));
378 }
379 } else {
380 appendRecipientToMimeMessage(mimeMessage, headerName, asString(exchange, headerValue));
381 }
382 }
383 }
384 }
385
386 /**
387 * Appends the Mail headers from the endpoint configuration.
388 */
389 protected void setRecipientFromEndpointConfiguration(MimeMessage mimeMessage, MailEndpoint endpoint)
390 throws MessagingException {
391
392 Map<Message.RecipientType, String> recipients = endpoint.getConfiguration().getRecipients();
393 if (recipients.containsKey(Message.RecipientType.TO)) {
394 appendRecipientToMimeMessage(mimeMessage, Message.RecipientType.TO.toString(), recipients.get(Message.RecipientType.TO));
395 }
396 if (recipients.containsKey(Message.RecipientType.CC)) {
397 appendRecipientToMimeMessage(mimeMessage, Message.RecipientType.CC.toString(), recipients.get(Message.RecipientType.CC));
398 }
399 if (recipients.containsKey(Message.RecipientType.BCC)) {
400 appendRecipientToMimeMessage(mimeMessage, Message.RecipientType.BCC.toString(), recipients.get(Message.RecipientType.BCC));
401 }
402 }
403
404 /**
405 * Appends the Mail attachments from the Camel {@link MailMessage}
406 */
407 protected void appendAttachmentsFromCamel(MimeMessage mimeMessage, MailConfiguration configuration, Exchange exchange)
408 throws MessagingException, IOException {
409
410 // Put parts in message
411 mimeMessage.setContent(createMixedMultipartAttachments(configuration, exchange));
412 }
413
414 private MimeMultipart createMixedMultipartAttachments(MailConfiguration configuration, Exchange exchange)
415 throws MessagingException, IOException {
416
417 // fill the body with text
418 MimeMultipart multipart = new MimeMultipart();
419 multipart.setSubType("mixed");
420 addBodyToMultipart(configuration, multipart, exchange);
421 String partDisposition = configuration.isUseInlineAttachments() ? Part.INLINE : Part.ATTACHMENT;
422 if (exchange.getIn().hasAttachments()) {
423 addAttachmentsToMultipart(multipart, partDisposition, exchange);
424 }
425 return multipart;
426 }
427
428 protected void addAttachmentsToMultipart(MimeMultipart multipart, String partDisposition, Exchange exchange) throws MessagingException {
429 LOG.trace("Adding attachments +++ start +++");
430 int i = 0;
431 for (Map.Entry<String, DataHandler> entry : exchange.getIn().getAttachments().entrySet()) {
432 String attachmentFilename = entry.getKey();
433 DataHandler handler = entry.getValue();
434
435 if (LOG.isTraceEnabled()) {
436 LOG.trace("Attachment #" + i + ": Disposition: " + partDisposition);
437 LOG.trace("Attachment #" + i + ": DataHandler: " + handler);
438 LOG.trace("Attachment #" + i + ": FileName: " + attachmentFilename);
439 }
440 if (handler != null) {
441 if (shouldAddAttachment(exchange, attachmentFilename, handler)) {
442 // Create another body part
443 BodyPart messageBodyPart = new MimeBodyPart();
444 // Set the data handler to the attachment
445 messageBodyPart.setDataHandler(handler);
446
447 if (attachmentFilename.toLowerCase().startsWith("cid:")) {
448 // add a Content-ID header to the attachment
449 // must use angle brackets according to RFC: http://www.ietf.org/rfc/rfc2392.txt
450 messageBodyPart.addHeader("Content-ID", "<" + attachmentFilename.substring(4) + ">");
451 // Set the filename without the cid
452 messageBodyPart.setFileName(attachmentFilename.substring(4));
453 } else {
454 // Set the filename
455 messageBodyPart.setFileName(attachmentFilename);
456 }
457
458 LOG.trace("Attachment #" + i + ": ContentType: " + messageBodyPart.getContentType());
459
460 if (contentTypeResolver != null) {
461 String contentType = contentTypeResolver.resolveContentType(attachmentFilename);
462 LOG.trace("Attachment #" + i + ": Using content type resolver: " + contentTypeResolver + " resolved content type as: " + contentType);
463 if (contentType != null) {
464 String value = contentType + "; name=" + attachmentFilename;
465 messageBodyPart.setHeader("Content-Type", value);
466 LOG.trace("Attachment #" + i + ": ContentType: " + messageBodyPart.getContentType());
467 }
468 }
469
470 // Set Disposition
471 messageBodyPart.setDisposition(partDisposition);
472 // Add part to multipart
473 multipart.addBodyPart(messageBodyPart);
474 } else {
475 LOG.trace("shouldAddAttachment: false");
476 }
477 } else {
478 LOG.warn("Cannot add attachment: " + attachmentFilename + " as DataHandler is null");
479 }
480 i++;
481 }
482 LOG.trace("Adding attachments +++ done +++");
483 }
484
485 protected void createMultipartAlternativeMessage(MimeMessage mimeMessage, MailConfiguration configuration, Exchange exchange)
486 throws MessagingException, IOException {
487
488 MimeMultipart multipartAlternative = new MimeMultipart("alternative");
489 mimeMessage.setContent(multipartAlternative);
490
491 MimeBodyPart plainText = new MimeBodyPart();
492 plainText.setText(getAlternativeBody(configuration, exchange), determineCharSet(configuration, exchange));
493 // remove the header with the alternative mail now that we got it
494 // otherwise it might end up twice in the mail reader
495 exchange.getIn().removeHeader(configuration.getAlternativeBodyHeader());
496 multipartAlternative.addBodyPart(plainText);
497
498 // if there are no attachments, add the body to the same mulitpart message
499 if (!exchange.getIn().hasAttachments()) {
500 addBodyToMultipart(configuration, multipartAlternative, exchange);
501 } else {
502 // if there are attachments, but they aren't set to be inline, add them to
503 // treat them as normal. It will append a multipart-mixed with the attachments and the body text
504 if (!configuration.isUseInlineAttachments()) {
505 BodyPart mixedAttachments = new MimeBodyPart();
506 mixedAttachments.setContent(createMixedMultipartAttachments(configuration, exchange));
507 multipartAlternative.addBodyPart(mixedAttachments);
508 } else {
509 // if the attachments are set to be inline, attach them as inline attachments
510 MimeMultipart multipartRelated = new MimeMultipart("related");
511 BodyPart related = new MimeBodyPart();
512
513 related.setContent(multipartRelated);
514 multipartAlternative.addBodyPart(related);
515
516 addBodyToMultipart(configuration, multipartRelated, exchange);
517
518 addAttachmentsToMultipart(multipartRelated, Part.INLINE, exchange);
519 }
520 }
521 }
522
523 protected void addBodyToMultipart(MailConfiguration configuration, MimeMultipart activeMultipart, Exchange exchange)
524 throws MessagingException, IOException {
525
526 BodyPart bodyMessage = new MimeBodyPart();
527 populateContentOnBodyPart(bodyMessage, configuration, exchange);
528 activeMultipart.addBodyPart(bodyMessage);
529 }
530
531 /**
532 * Strategy to allow filtering of attachments which are added on the Mail message
533 */
534 protected boolean shouldAddAttachment(Exchange exchange, String attachmentFilename, DataHandler handler) {
535 return true;
536 }
537
538 protected Map<String, Object> extractHeadersFromMail(Message mailMessage, Exchange exchange) throws MessagingException {
539 Map<String, Object> answer = new HashMap<String, Object>();
540 Enumeration names = mailMessage.getAllHeaders();
541
542 while (names.hasMoreElements()) {
543 Header header = (Header) names.nextElement();
544 String value = header.getValue();
545 if (headerFilterStrategy != null && !headerFilterStrategy.applyFilterToExternalHeaders(header.getName(), value, exchange)) {
546 CollectionHelper.appendValue(answer, header.getName(), value);
547 }
548 }
549
550 return answer;
551 }
552
553 private static void appendRecipientToMimeMessage(MimeMessage mimeMessage, String type, String recipient)
554 throws MessagingException {
555
556 // we support that multi recipient can be given as a string separated by comma or semicolon
557 String[] lines = recipient.split("[,;]");
558 for (String line : lines) {
559 line = line.trim();
560 mimeMessage.addRecipients(asRecipientType(type), line);
561 }
562 }
563
564 /**
565 * Does the given camel message contain any To, CC or BCC header names?
566 */
567 private static boolean hasRecipientHeaders(Exchange exchange) {
568 for (String key : exchange.getIn().getHeaders().keySet()) {
569 if (isRecipientHeader(key)) {
570 return true;
571 }
572 }
573 return false;
574 }
575
576 protected static boolean hasAlternativeBody(MailConfiguration configuration, Exchange exchange) {
577 return getAlternativeBody(configuration, exchange) != null;
578 }
579
580 protected static String getAlternativeBody(MailConfiguration configuration, Exchange exchange) {
581 String alternativeBodyHeader = configuration.getAlternativeBodyHeader();
582 return exchange.getIn().getHeader(alternativeBodyHeader, java.lang.String.class);
583 }
584
585 /**
586 * Is the given key a mime message recipient header (To, CC or BCC)
587 */
588 private static boolean isRecipientHeader(String key) {
589 if (Message.RecipientType.TO.toString().equalsIgnoreCase(key)) {
590 return true;
591 } else if (Message.RecipientType.CC.toString().equalsIgnoreCase(key)) {
592 return true;
593 } else if (Message.RecipientType.BCC.toString().equalsIgnoreCase(key)) {
594 return true;
595 }
596 return false;
597 }
598
599 /**
600 * Returns the RecipientType object.
601 */
602 private static Message.RecipientType asRecipientType(String type) {
603 if (Message.RecipientType.TO.toString().equalsIgnoreCase(type)) {
604 return Message.RecipientType.TO;
605 } else if (Message.RecipientType.CC.toString().equalsIgnoreCase(type)) {
606 return Message.RecipientType.CC;
607 } else if (Message.RecipientType.BCC.toString().equalsIgnoreCase(type)) {
608 return Message.RecipientType.BCC;
609 }
610 throw new IllegalArgumentException("Unknown recipient type: " + type);
611 }
612
613
614 private static boolean empty(Address[] addresses) {
615 return addresses == null || addresses.length == 0;
616 }
617
618 private static String asString(Exchange exchange, Object value) {
619 return exchange.getContext().getTypeConverter().convertTo(String.class, exchange, value);
620 }
621
622 }