001 package org.codemonkey.simplejavamail;
002
003 import java.util.Properties;
004
005 import javax.mail.Authenticator;
006 import javax.mail.Message;
007 import javax.mail.MessagingException;
008 import javax.mail.PasswordAuthentication;
009 import javax.mail.Session;
010 import javax.mail.Transport;
011
012 /**
013 * Creates and initializes a {@link Session} instance based on provided host and credentials as well as a {@link TransportStrategy} instance
014 * to apply SMTP, SMTPS or SMTP + TLS protocol semantics.
015 * <p>
016 * The <code>Session</code> needs to be created before assembling the concerning e-mail message in the {@link Mailer} as the Sun JavaMail
017 * API directly wires the e-mail message into the <code>Session</code>. The session again is needed when actually sending the e-mail or when
018 * {@link #toString()} is called.
019 *
020 * @author Benny Bottema
021 */
022 class MailSession {
023
024 /**
025 * Used to actually send the email. This session can come from being passed in the default constructor, or made by this
026 * <code>MailSession</code> directly, when no <code>Session</code> instance was provided.
027 *
028 * @see #MailSession(Session)
029 * @see #MailSession(String, int, String, String, TransportStrategy)
030 */
031 private final Session session;
032
033 /**
034 * The transport protocol strategy enum that actually handles the session configuration. Session configuration meaning setting the right
035 * properties for the appropriate transport type (ie. <em>"mail.smtp.host"</em> for SMTP, <em>"mail.smtps.host"</em> for SMTPS).
036 */
037 private TransportStrategy transportStrategy;
038
039 /**
040 * Default constructor, stores the given mail session for later use. It is assumed all properties are set, such as host, credentials and
041 * transport properties.
042 *
043 * @param session A preconfigured mail {@link Session} object with which a {@link Message} can be produced.
044 */
045 public MailSession(final Session session) {
046 this.session = session;
047 }
048
049 /**
050 * Overloaded constructor which produces a new {@link Session} on the fly. Use this if you don't have a mail session configured in your
051 * web container, or Spring context etc.
052 *
053 * @param host The address URL of the SMTP server to be used.
054 * @param port The port of the SMTP server.
055 * @param username An optional username, may be <code>null</code>.
056 * @param password An optional password, may be <code>null</code>, but only if username is <code>null</code> as well.
057 */
058 public MailSession(final String host, final int port, final String username, final String password,
059 final TransportStrategy transportStrategy) {
060 // we're doing this manually instead of using Apache Commons, to avoid another dependency
061 if (host == null || "".equals(host.trim())) {
062 throw new RuntimeException("Can't send an email without host");
063 } else if ((password != null && !"".equals(password.trim())) && (username == null || "".equals(username.trim()))) {
064 throw new RuntimeException("Can't have a password without username");
065 }
066 this.transportStrategy = transportStrategy;
067 this.session = createMailSession(host, port, username, password);
068 }
069
070 /**
071 * Actually instantiates and configures the {@link Session} instance. Delegates resolving transport protocol specific properties to the
072 * {@link #transportStrategy} in two ways:
073 * <ol>
074 * <li>request an initial property list which the strategy may pre-populate</li>
075 * <li>by requesting the property names according to the respective transport protocol it handles (for the host property name it would
076 * be <em>"mail.smtp.host"</em> for SMTP and <em>"mail.smtps.host"</em> for SMTPS)</li>
077 * </ol>
078 *
079 * @param host The address URL of the SMTP server to be used.
080 * @param port The port of the SMTP server.
081 * @param username An optional username, may be <code>null</code>.
082 * @param password An optional password, may be <code>null</code>.
083 * @return A fully configured <code>Session</code> instance complete with transport protocol settings.
084 * @see TransportStrategy#generateProperties()
085 * @see TransportStrategy#propertyNameHost()
086 * @see TransportStrategy#propertyNamePort()
087 * @see TransportStrategy#propertyNameUsername()
088 * @see TransportStrategy#propertyNameAuthenticate()
089 */
090 public Session createMailSession(final String host, final int port, final String username, final String password) {
091 Properties props = transportStrategy.generateProperties();
092 props.put(transportStrategy.propertyNameHost(), host);
093 props.put(transportStrategy.propertyNamePort(), String.valueOf(port));
094
095 if (username != null) {
096 props.put(transportStrategy.propertyNameUsername(), username);
097 }
098
099 if (password != null) {
100 props.put(transportStrategy.propertyNameAuthenticate(), "true");
101 return Session.getInstance(props, new Authenticator() {
102 @Override
103 protected PasswordAuthentication getPasswordAuthentication() {
104 return new PasswordAuthentication(username, password);
105 }
106 });
107 } else {
108 return Session.getInstance(props);
109 }
110 }
111
112 /**
113 * Sends a Sun JavaMail {@link Message} object using the {@link Session#getTransport()}. It will call {@link Transport#connect()}
114 * assuming all connection details have been configured in the provided {@link Session} instance.
115 * <p>
116 * Performs a call to {@link Message#saveChanges()} as the Sun JavaMail API indicates it is needed to configure the message headers and
117 * providing a message id.
118 *
119 * @param message The message to send. Generate by the {@link Mailer}.
120 * @throws MessagingException Thrown by {@link Message#saveChanges()}, {@link Transport#connect()},
121 * {@link Transport#sendMessage(Message, javax.mail.Address[])}, {@link Transport#close()}
122 */
123 public void sendMessage(Message message)
124 throws MessagingException {
125 message.saveChanges(); // some headers and id's will be set for this specific message
126 Transport transport = session.getTransport();
127 transport.connect();
128 transport.sendMessage(message, message.getAllRecipients());
129 transport.close();
130 }
131
132 /**
133 * Returns host details, credentials used and whether authentication will take place and finally the transport protocol used.
134 */
135 @Override
136 public String toString() {
137 final String logmsg = "host: %s, port: %s, username: %s, authenticate: %s, transport: %s";
138 return String.format(logmsg, transportStrategy.propertyNameHost(), transportStrategy.propertyNamePort(),
139 transportStrategy.propertyNameUsername(), Boolean.parseBoolean(transportStrategy.propertyNameAuthenticate()),
140 transportStrategy);
141 }
142
143 /**
144 * Bean getter for {@link #session}.
145 */
146 public Session getSession() {
147 return session;
148 }
149 }