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: 958535 $ 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 }