001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.synapse.transport.mail;
021:
022: import org.apache.synapse.transport.base.AbstractTransportSender;
023: import org.apache.synapse.transport.base.BaseConstants;
024: import org.apache.synapse.transport.base.BaseUtils;
025: import org.apache.synapse.transport.base.BaseTransportException;
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.axis2.context.ConfigurationContext;
028: import org.apache.axis2.context.MessageContext;
029: import org.apache.axis2.description.TransportOutDescription;
030: import org.apache.axis2.description.Parameter;
031: import org.apache.axis2.AxisFault;
032: import org.apache.axis2.addressing.AddressingConstants;
033: import org.apache.axis2.transport.OutTransportInfo;
034: import org.apache.axis2.transport.MessageFormatter;
035: import org.apache.axis2.transport.TransportUtils;
036: import org.apache.axiom.om.OMElement;
037: import org.apache.axiom.om.OMOutputFormat;
038: import org.apache.axiom.om.OMNode;
039: import org.apache.axiom.om.OMText;
040: import org.apache.axiom.om.impl.llom.OMSourcedElementImpl;
041:
042: import javax.mail.*;
043: import javax.mail.util.ByteArrayDataSource;
044: import javax.mail.internet.*;
045: import javax.activation.DataHandler;
046: import javax.activation.DataSource;
047: import javax.activation.MailcapCommandMap;
048: import javax.activation.CommandMap;
049: import javax.xml.stream.XMLStreamException;
050: import javax.xml.stream.XMLStreamReader;
051:
052: import java.util.*;
053: import java.io.ByteArrayOutputStream;
054: import java.io.IOException;
055:
056: /**
057: * The mail transport sender sends mail using an SMTP server configuration defined
058: * in the axis2.xml's transport sender definition
059: */
060: public class MailTransportSender extends AbstractTransportSender {
061:
062: private String smtpUsername = null;
063: private String smtpPassword = null;
064: /** Default from address for outgoing messages */
065: private InternetAddress smtpFromAddress = null;
066: /** A set of custom Bcc address for all outgoing messages */
067: private InternetAddress[] smtpBccAddresses = null;
068: /** Default mail format */
069: private String defaultMailFormat = "Text";
070: /** The default Session which can be safely shared */
071: private Session session = null;
072:
073: /**
074: * The public constructor
075: */
076: public MailTransportSender() {
077: log = LogFactory.getLog(MailTransportSender.class);
078: }
079:
080: /**
081: * Initialize the Mail sender and be ready to send messages
082: * @param cfgCtx the axis2 configuration context
083: * @param transportOut the transport-out description
084: * @throws org.apache.axis2.AxisFault on error
085: */
086: public void init(ConfigurationContext cfgCtx,
087: TransportOutDescription transportOut) throws AxisFault {
088: setTransportName(MailConstants.TRANSPORT_NAME);
089: super .init(cfgCtx, transportOut);
090:
091: // initialize SMTP session
092: Properties props = new Properties();
093: List<Parameter> params = transportOut.getParameters();
094: for (Parameter p : params) {
095: props.put(p.getName(), p.getValue());
096: }
097:
098: if (props.containsKey(MailConstants.MAIL_SMTP_FROM)) {
099: try {
100: smtpFromAddress = new InternetAddress((String) props
101: .get(MailConstants.MAIL_SMTP_FROM));
102: } catch (AddressException e) {
103: handleException("Invalid default 'From' address : "
104: + props.get(MailConstants.MAIL_SMTP_FROM), e);
105: }
106: }
107:
108: if (props.containsKey(MailConstants.MAIL_SMTP_BCC)) {
109: try {
110: smtpBccAddresses = InternetAddress.parse((String) props
111: .get(MailConstants.MAIL_SMTP_BCC));
112: } catch (AddressException e) {
113: handleException("Invalid default 'Bcc' address : "
114: + props.get(MailConstants.MAIL_SMTP_BCC), e);
115: }
116: }
117:
118: if (props.containsKey(MailConstants.TRANSPORT_MAIL_FORMAT)) {
119: defaultMailFormat = (String) props
120: .get(MailConstants.TRANSPORT_MAIL_FORMAT);
121: }
122:
123: smtpUsername = (String) props
124: .get(MailConstants.MAIL_SMTP_USERNAME);
125: smtpPassword = (String) props
126: .get(MailConstants.MAIL_SMTP_PASSWORD);
127:
128: if (smtpUsername != null && smtpPassword != null) {
129: session = Session.getInstance(props, new Authenticator() {
130: public PasswordAuthentication getPasswordAuthentication() {
131: return new PasswordAuthentication(smtpUsername,
132: smtpPassword);
133: }
134: });
135: } else {
136: session = Session.getInstance(props, null);
137: }
138:
139: // add handlers for main MIME types
140: MailcapCommandMap mc = (MailcapCommandMap) CommandMap
141: .getDefaultCommandMap();
142: mc
143: .addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
144: mc
145: .addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
146: mc
147: .addMailcap("application/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
148: mc
149: .addMailcap("application/soap+xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
150: mc
151: .addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
152: mc
153: .addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
154: mc
155: .addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
156: CommandMap.setDefaultCommandMap(mc);
157:
158: session.setDebug(log.isTraceEnabled());
159: }
160:
161: /**
162: * Send the given message over the Mail transport
163: *
164: * @param msgCtx the axis2 message context
165: * @throws AxisFault on error
166: */
167: public void sendMessage(MessageContext msgCtx,
168: String targetAddress, OutTransportInfo outTransportInfo)
169: throws AxisFault {
170:
171: MailOutTransportInfo mailOutInfo = null;
172:
173: if (targetAddress != null) {
174: if (targetAddress.startsWith(MailConstants.TRANSPORT_NAME)) {
175: targetAddress = targetAddress
176: .substring(MailConstants.TRANSPORT_NAME
177: .length() + 1);
178: }
179:
180: if (msgCtx.getReplyTo() != null
181: && !AddressingConstants.Final.WSA_NONE_URI
182: .equals(msgCtx.getReplyTo().getAddress())
183: && !AddressingConstants.Final.WSA_ANONYMOUS_URL
184: .equals(msgCtx.getReplyTo().getAddress())) {
185:
186: String replyTo = msgCtx.getReplyTo().getAddress();
187: if (replyTo.startsWith(MailConstants.TRANSPORT_NAME)) {
188: replyTo = replyTo
189: .substring(MailConstants.TRANSPORT_NAME
190: .length() + 1);
191: }
192: try {
193: mailOutInfo = new MailOutTransportInfo(
194: new InternetAddress(replyTo));
195: } catch (AddressException e) {
196: handleException("Invalid reply address/es : "
197: + replyTo, e);
198: }
199: } else {
200: mailOutInfo = new MailOutTransportInfo(smtpFromAddress);
201: }
202:
203: try {
204: mailOutInfo.setTargetAddresses(InternetAddress
205: .parse(targetAddress));
206: } catch (AddressException e) {
207: handleException("Invalid target address/es : "
208: + targetAddress, e);
209: }
210: } else if (outTransportInfo != null
211: && outTransportInfo instanceof MailOutTransportInfo) {
212: mailOutInfo = (MailOutTransportInfo) outTransportInfo;
213: }
214:
215: if (mailOutInfo != null) {
216: try {
217: sendMail(mailOutInfo, msgCtx);
218: } catch (MessagingException e) {
219: handleException("Error generating mail message", e);
220: } catch (IOException e) {
221: handleException("Error generating mail message", e);
222: }
223: } else {
224: handleException("Unable to determine out transport information to send message");
225: }
226: }
227:
228: /**
229: * Populate email with a SOAP formatted message
230: * @param outInfo the out transport information holder
231: * @param msgContext the message context that holds the message to be written
232: * @throws AxisFault on error
233: */
234: private void sendMail(MailOutTransportInfo outInfo,
235: MessageContext msgContext) throws AxisFault,
236: MessagingException, IOException {
237:
238: OMOutputFormat format = BaseUtils.getOMOutputFormat(msgContext);
239: MessageFormatter messageFormatter = null;
240:
241: try {
242: messageFormatter = TransportUtils
243: .getMessageFormatter(msgContext);
244: } catch (AxisFault axisFault) {
245: throw new BaseTransportException(
246: "Unable to get the message formatter to use");
247: }
248:
249: WSMimeMessage message = new WSMimeMessage(session);
250: Map trpHeaders = (Map) msgContext
251: .getProperty(MessageContext.TRANSPORT_HEADERS);
252:
253: // set From address - first check if this is a reply, then use from address from the
254: // transport out, else if any custom transport headers set on this message, or default
255: // to the transport senders default From address
256: if (outInfo.getTargetAddresses() != null
257: && outInfo.getFromAddress() != null) {
258: message.setFrom(outInfo.getFromAddress());
259: message.setReplyTo((new Address[] { outInfo
260: .getFromAddress() }));
261: } else if (trpHeaders != null
262: && trpHeaders
263: .containsKey(MailConstants.MAIL_HEADER_FROM)) {
264: message.setFrom(new InternetAddress((String) trpHeaders
265: .get(MailConstants.MAIL_HEADER_FROM)));
266: message.setReplyTo(InternetAddress
267: .parse((String) trpHeaders
268: .get(MailConstants.MAIL_HEADER_FROM)));
269: } else {
270: if (smtpFromAddress != null) {
271: message.setFrom(smtpFromAddress);
272: message.setReplyTo(new Address[] { smtpFromAddress });
273: } else {
274: handleException("From address for outgoing message cannot be determined");
275: }
276: }
277:
278: // set To address/es to any custom transport header set on the message, else use the reply
279: // address from the out transport information
280: if (trpHeaders != null
281: && trpHeaders.containsKey(MailConstants.MAIL_HEADER_TO)) {
282: message.setRecipients(Message.RecipientType.TO,
283: InternetAddress.parse((String) trpHeaders
284: .get(MailConstants.MAIL_HEADER_TO)));
285: } else if (outInfo.getTargetAddresses() != null) {
286: message.setRecipients(Message.RecipientType.TO, outInfo
287: .getTargetAddresses());
288: } else {
289: handleException("To address for outgoing message cannot be determined");
290: }
291:
292: // set Cc address/es to any custom transport header set on the message, else use the
293: // Cc list from original request message
294: if (trpHeaders != null
295: && trpHeaders.containsKey(MailConstants.MAIL_HEADER_CC)) {
296: message.setRecipients(Message.RecipientType.CC,
297: InternetAddress.parse((String) trpHeaders
298: .get(MailConstants.MAIL_HEADER_CC)));
299: } else if (outInfo.getTargetAddresses() != null) {
300: message.setRecipients(Message.RecipientType.CC, outInfo
301: .getCcAddresses());
302: }
303:
304: // set Bcc address/es to any custom addresses set at the transport sender level + any
305: // custom transport header
306: InternetAddress[] trpBccArr = null;
307: if (trpHeaders != null
308: && trpHeaders
309: .containsKey(MailConstants.MAIL_HEADER_BCC)) {
310: trpBccArr = InternetAddress.parse((String) trpHeaders
311: .get(MailConstants.MAIL_HEADER_BCC));
312: }
313:
314: InternetAddress[] mergedBcc = new InternetAddress[(trpBccArr != null ? trpBccArr.length
315: : 0)
316: + (smtpBccAddresses != null ? smtpBccAddresses.length
317: : 0)];
318: if (trpBccArr != null) {
319: System.arraycopy(trpBccArr, 0, mergedBcc, 0,
320: trpBccArr.length);
321: }
322: if (smtpBccAddresses != null) {
323: System.arraycopy(smtpBccAddresses, 0, mergedBcc,
324: mergedBcc.length, smtpBccAddresses.length);
325: }
326: if (mergedBcc != null) {
327: message.setRecipients(Message.RecipientType.BCC, mergedBcc);
328: }
329:
330: // set subject
331: if (trpHeaders != null
332: && trpHeaders
333: .containsKey(MailConstants.MAIL_HEADER_SUBJECT)) {
334: message.setSubject((String) trpHeaders
335: .get(MailConstants.MAIL_HEADER_SUBJECT));
336: } else if (outInfo.getSubject() != null) {
337: message.setSubject(outInfo.getSubject());
338: } else {
339: message.setSubject(BaseConstants.SOAPACTION + ": "
340: + msgContext.getSoapAction());
341: }
342:
343: // if a custom message id is set, use it
344: if (msgContext.getMessageID() != null) {
345: message.setHeader(MailConstants.MAIL_HEADER_MESSAGE_ID,
346: msgContext.getMessageID());
347: message.setHeader(MailConstants.MAIL_HEADER_X_MESSAGE_ID,
348: msgContext.getMessageID());
349: }
350:
351: // if this is a reply, set reference to original message
352: if (outInfo.getRequestMessageID() != null) {
353: message.setHeader(MailConstants.MAIL_HEADER_IN_REPLY_TO,
354: outInfo.getRequestMessageID());
355: message.setHeader(MailConstants.MAIL_HEADER_REFERENCES,
356: outInfo.getRequestMessageID());
357:
358: } else {
359: if (trpHeaders != null
360: && trpHeaders
361: .containsKey(MailConstants.MAIL_HEADER_IN_REPLY_TO)) {
362: message
363: .setHeader(
364: MailConstants.MAIL_HEADER_IN_REPLY_TO,
365: (String) trpHeaders
366: .get(MailConstants.MAIL_HEADER_IN_REPLY_TO));
367: }
368: if (trpHeaders != null
369: && trpHeaders
370: .containsKey(MailConstants.MAIL_HEADER_REFERENCES)) {
371: message
372: .setHeader(
373: MailConstants.MAIL_HEADER_REFERENCES,
374: (String) trpHeaders
375: .get(MailConstants.MAIL_HEADER_REFERENCES));
376: }
377: }
378:
379: // set Date
380: message.setSentDate(new Date());
381:
382: // set SOAPAction header
383: message.setHeader(BaseConstants.SOAPACTION, msgContext
384: .getSoapAction());
385:
386: // write body
387: ByteArrayOutputStream baos = null;
388: String contentType = messageFormatter.getContentType(
389: msgContext, format, msgContext.getSoapAction());
390: DataHandler dataHandler = null;
391: MimeMultipart mimeMultiPart = null;
392:
393: OMElement firstChild = msgContext.getEnvelope().getBody()
394: .getFirstElement();
395: if (firstChild != null) {
396:
397: if (BaseConstants.DEFAULT_BINARY_WRAPPER.equals(firstChild
398: .getQName())) {
399: baos = new ByteArrayOutputStream();
400: OMNode omNode = firstChild.getFirstOMChild();
401:
402: if (omNode != null && omNode instanceof OMText) {
403: Object dh = ((OMText) omNode).getDataHandler();
404: if (dh != null && dh instanceof DataHandler) {
405: dataHandler = (DataHandler) dh;
406: }
407: }
408: } else if (BaseConstants.DEFAULT_TEXT_WRAPPER
409: .equals(firstChild.getQName())) {
410: if (firstChild instanceof OMSourcedElementImpl) {
411: // Note: this code will be replaced by something more efficient later
412: baos = new ByteArrayOutputStream();
413: try {
414: XMLStreamReader reader = firstChild
415: .getXMLStreamReader();
416: while (reader.hasNext()) {
417: if (reader.next() == XMLStreamReader.CHARACTERS) {
418: baos.write(reader.getText().getBytes());
419: }
420: }
421: } catch (XMLStreamException e) {
422: handleException(
423: "Error serializing 'text' payload from OMSourcedElement",
424: e);
425: }
426: dataHandler = new DataHandler(
427: new String(baos.toByteArray(), format
428: .getCharSetEncoding()),
429: MailConstants.TEXT_PLAIN);
430: } else {
431: dataHandler = new DataHandler(firstChild.getText(),
432: MailConstants.TEXT_PLAIN);
433: }
434: } else {
435:
436: baos = new ByteArrayOutputStream();
437: messageFormatter
438: .writeTo(msgContext, format, baos, true);
439:
440: // create the data handler
441: dataHandler = new DataHandler(new String(baos
442: .toByteArray(), format.getCharSetEncoding()),
443: contentType);
444:
445: String mFormat = (String) msgContext
446: .getProperty(MailConstants.TRANSPORT_MAIL_FORMAT);
447: if (mFormat == null) {
448: mFormat = defaultMailFormat;
449: }
450:
451: if (MailConstants.TRANSPORT_FORMAT_MP.equals(mFormat)) {
452: mimeMultiPart = new MimeMultipart();
453: MimeBodyPart mimeBodyPart1 = new MimeBodyPart();
454: mimeBodyPart1.setContent(
455: "Web Service Message Attached",
456: "text/plain");
457: MimeBodyPart mimeBodyPart2 = new MimeBodyPart();
458: mimeBodyPart2.setDataHandler(dataHandler);
459: mimeBodyPart2.setHeader(BaseConstants.SOAPACTION,
460: msgContext.getSoapAction());
461: mimeMultiPart.addBodyPart(mimeBodyPart1);
462: mimeMultiPart.addBodyPart(mimeBodyPart2);
463:
464: } else {
465: message.setHeader(BaseConstants.SOAPACTION,
466: msgContext.getSoapAction());
467: }
468: }
469: }
470:
471: try {
472: if (mimeMultiPart == null) {
473: message.setDataHandler(dataHandler);
474: } else {
475: message.setContent(mimeMultiPart);
476: }
477: Transport.send(message);
478:
479: } catch (MessagingException e) {
480: handleException(
481: "Error creating mail message or sending it to the configured server",
482: e);
483:
484: } finally {
485: try {
486: if (baos != null) {
487: baos.close();
488: }
489: } catch (IOException ignore) {
490: }
491: }
492: }
493: }
|