| | package com.whitemagicsoftware.notify; |
| | |
| | -import static com.whitemagicsoftware.notify.TextHtmlMultipart.*; |
| | -import java.io.BufferedReader; |
| | -import java.io.IOException; |
| | -import java.io.InputStreamReader; |
| | -import java.io.OutputStream; |
| | -import java.io.OutputStreamWriter; |
| | -import java.io.UnsupportedEncodingException; |
| | -import java.net.HttpURLConnection; |
| | -import java.net.MalformedURLException; |
| | -import java.net.URL; |
| | -import java.net.URLConnection; |
| | -import java.net.URLEncoder; |
| | -import java.nio.charset.Charset; |
| | -import java.nio.charset.StandardCharsets; |
| | -import javax.mail.Address; |
| | -import javax.mail.BodyPart; |
| | -import javax.mail.Message; |
| | -import javax.mail.MessagingException; |
| | -import javax.mail.Multipart; |
| | -import javax.mail.PasswordAuthentication; |
| | -import javax.mail.Session; |
| | -import javax.mail.Transport; |
| | -import javax.mail.URLName; |
| | -import javax.mail.internet.AddressException; |
| | -import javax.mail.internet.InternetAddress; |
| | - |
| | -/** |
| | - * Responsible for sending messages over HTTP. Subclasses are responsible for |
| | - * providing the sendMessage method implementation. |
| | - * |
| | - * @author White Magic Software, Ltd. |
| | - */ |
| | -public abstract class HttpTransport extends Transport { |
| | - |
| | - /** |
| | - * Constructs a new HttpTransport capable of sending notifications via a |
| | - * RESTFUL API. |
| | - * |
| | - * @param session Session state. |
| | - * @param urlname Set value to null for HTTP. |
| | - */ |
| | - public HttpTransport( Session session, URLName urlname ) { |
| | - // This should set the protected instance variables of "this.session" |
| | - // and "this.urlname". |
| | - super( session, urlname ); |
| | - } |
| | - |
| | - /** |
| | - * Sets the credentials to use for permission to send messages. |
| | - * |
| | - * @param username The username authorised to send messages over HTTP. |
| | - * @param password The password (or API key) associated with the username. |
| | - */ |
| | - public void setAuthentication( String username, String password ) { |
| | - getSession().setPasswordAuthentication( |
| | - getURLName(), |
| | - new PasswordAuthentication( username, password ) |
| | - ); |
| | - } |
| | - |
| | - /** |
| | - * Sends the given data over the wire. |
| | - * |
| | - * @param data The URL-encoded parameters to send. |
| | - * |
| | - * @throws javax.mail.MessagingException |
| | - */ |
| | - protected void send( String data ) throws MessagingException { |
| | - HttpURLConnection connection = open(); |
| | - |
| | - write( data, connection ); |
| | - String response = read( connection ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the default encoding to use for converting plain text into safe URL |
| | - * parameters. |
| | - * |
| | - * @return StandardCharsets.UTF_8 by default. |
| | - */ |
| | - protected Charset getDefaultCharset() { |
| | - return StandardCharsets.UTF_8; |
| | - } |
| | - |
| | - /** |
| | - * Returns the given text string encoded for passing as a URL parameter. |
| | - * |
| | - * @param text The text to encode. |
| | - * |
| | - * @return The encoded text. |
| | - * |
| | - * @throws MessagingException The text could not be encoded. |
| | - */ |
| | - protected String encode( final String text ) throws MessagingException { |
| | - String charset = getDefaultCharset().displayName(); |
| | - |
| | - try { |
| | - return URLEncoder.encode( text, charset ); |
| | - } catch( UnsupportedEncodingException e ) { |
| | - throw new MessagingException( |
| | - "Invalid default encoding: '" + charset + "'", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Helper method to return a URL-encoded string. |
| | - * |
| | - * @param text Text to encode. |
| | - * |
| | - * @return The StringBuilder's text, encoded to be URL-friendly. |
| | - * |
| | - * @throws MessagingException |
| | - */ |
| | - protected String encode( final StringBuilder text ) |
| | - throws MessagingException { |
| | - return encode( text.toString() ); |
| | - } |
| | - |
| | - /** |
| | - * Answers whether the string contains any characters. |
| | - * |
| | - * @param string Can be null or empty. |
| | - * |
| | - * @return true The string is empty or zero length or contains only |
| | - * whitespace. |
| | - */ |
| | - private boolean isEmpty( final String string ) { |
| | - return string == null || string.trim().length() == 0; |
| | - } |
| | - |
| | - /** |
| | - * |
| | - * @param message Message that contains content to extract. |
| | - * |
| | - * @return The text and HTML portions of the message in array index 0 and 1, |
| | - * respectively. |
| | - * |
| | - * @throws javax.mail.MessagingException Message body not extracted. |
| | - */ |
| | - protected String[] extractMessageContents( Message message ) |
| | - throws MessagingException { |
| | - String result[] = new String[]{ "", "" }; |
| | - |
| | - try { |
| | - // Ensure the mime types are applied. |
| | - // |
| | - // See also: http://stackoverflow.com/a/5031057/59087 |
| | - message.saveChanges(); |
| | - Object content = message.getContent(); |
| | - |
| | - if( content instanceof Multipart ) { |
| | - Multipart multipart = (Multipart)content; |
| | - |
| | - result[ 0 ] = extractMultipart( multipart, MIME_TYPE_PLAIN ); |
| | - result[ 1 ] = extractMultipart( multipart, MIME_TYPE_HTML ); |
| | - } else if( message.isMimeType( MIME_TYPE_PLAIN ) ) { |
| | - result[ 0 ] = content.toString(); |
| | - } |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Could not extract message content.", e ); |
| | - } |
| | - |
| | - return result; |
| | - } |
| | - |
| | - /** |
| | - * Extracts the content from the multipart instance that matches the given |
| | - * mimeType. |
| | - * |
| | - * @param multipart The multipart instance with contents to extract. |
| | - * @param mimeType The mimeType to match against the multipart. |
| | - * |
| | - * @return The contents from the multipart that match the mimeType. |
| | - * |
| | - * @throws MessagingException Failed to extract the content. |
| | - */ |
| | - private String extractMultipart( Multipart multipart, String mimeType ) |
| | - throws MessagingException { |
| | - String result = ""; |
| | - int count = multipart.getCount(); |
| | - |
| | - for( int i = 0; i < count; i++ ) { |
| | - BodyPart bodyPart = multipart.getBodyPart( i ); |
| | - Object content = exhumeBodyPart( bodyPart ); |
| | - |
| | - if( content instanceof Multipart ) { |
| | - result += extractMultipart( (Multipart)content, mimeType ); |
| | - } else if( bodyPart.isMimeType( mimeType ) ) { |
| | - result += "\n" + content.toString(); |
| | - } |
| | - } |
| | - |
| | - return result; |
| | - } |
| | - |
| | - /** |
| | - * Returns the body part content. |
| | - * |
| | - * @param bodyPart The body part containing content to retrieve. |
| | - * |
| | - * @return The body part's contents. |
| | - * |
| | - * @throws MessagingException Error extracting the body part contents. |
| | - */ |
| | - private Object exhumeBodyPart( BodyPart bodyPart ) |
| | - throws MessagingException { |
| | - try { |
| | - return bodyPart.getContent(); |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Error opening body part contents.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Writes the given URL-encoded parameters to the given connection. |
| | - * |
| | - * @param urlParameters The URL-encoded parameters to write. |
| | - * @param connection The connection that can be written to. |
| | - * |
| | - * @throws MessagingException Error writing the parameters to the given |
| | - * connection. |
| | - */ |
| | - protected void write( String urlParameters, HttpURLConnection connection ) |
| | - throws MessagingException { |
| | - try { |
| | - write( urlParameters, connection.getOutputStream() ); |
| | - |
| | - int response = connection.getResponseCode(); |
| | - |
| | - // Ensure a 200 response code is returned. |
| | - if( response != HttpURLConnection.HTTP_OK ) { |
| | - throw new MessagingException( "Unexpected response code: " + response ); |
| | - } |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Could not stream output connection.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Writes a set of encoded URL parameters to the given stream. |
| | - * |
| | - * @param urlParameters The parameters to send. |
| | - * @param outputStream The stream to receive the parameters. |
| | - * |
| | - * @throws MessagingException Message could not be sent. |
| | - */ |
| | - protected void write( String urlParameters, OutputStream outputStream ) |
| | - throws MessagingException { |
| | - try( OutputStreamWriter writer = new OutputStreamWriter( outputStream ); ) { |
| | - writer.write( urlParameters ); |
| | - writer.flush(); |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Could not send message.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Reads the response code returned from a previous call to write. This must |
| | - * be called after write, not before. |
| | - * |
| | - * @param connection The connection that had data written to it. |
| | - * |
| | - * @return A non-null, possibly empty string. |
| | - * |
| | - * @throws MessagingException Response could not be read. |
| | - */ |
| | - protected String read( URLConnection connection ) throws MessagingException { |
| | - try { |
| | - return read( new InputStreamReader( connection.getInputStream() ) ); |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Could not stream input connection.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Helper method. |
| | - * |
| | - * @param in The stream from a connection that had data written to it. |
| | - * |
| | - * @return A non-null, possibly empty string. |
| | - * |
| | - * @throws MessagingException Response could not be read. |
| | - */ |
| | - protected String read( InputStreamReader in ) throws MessagingException { |
| | - try( BufferedReader rd = new BufferedReader( in ) ) { |
| | - return rd.readLine(); |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Could not read status.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Connects to the REST API over HTTP. |
| | - * |
| | - * @return An opened URLConnection instance. |
| | - * |
| | - * @throws javax.mail.MessagingException The REST API is unavailable. |
| | - */ |
| | - protected HttpURLConnection open() throws MessagingException { |
| | - HttpURLConnection connection = openConnection(); |
| | - connection.setDoOutput( true ); |
| | - |
| | - return connection; |
| | - } |
| | - |
| | - /** |
| | - * Helper method to return an opened URL connection. |
| | - * |
| | - * @return An opened URLConnection instance. |
| | - * |
| | - * @throws javax.mail.MessagingException Error connecting to the provider. |
| | - */ |
| | - public HttpURLConnection openConnection() throws MessagingException { |
| | - try { |
| | - return (HttpURLConnection)getURL().openConnection(); |
| | - } catch( IOException e ) { |
| | - throw new MessagingException( "Could not connect to provider.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Helper method to return the REST API URL for sending messages. |
| | - * |
| | - * @return An HTTP(S) URL, unopened. |
| | - * |
| | - * @return @throws javax.mail.MessagingException |
| | - */ |
| | - private URL getURL() { |
| | - try { |
| | - return new URL( getRestApiUrl() ); |
| | - } catch( MalformedURLException e ) { |
| | - throw new RuntimeException( "Provider URL not set.", e ); |
| | - } |
| | - } |
| | - |
| | - /** |
| | - * Returns the URLName by first creating a URL using the REST API URL. |
| | - * |
| | - * @return A non-null URLName. |
| | - */ |
| | - @Override |
| | - public URLName getURLName() { |
| | - return new URLName( getURL() ); |
| | - } |
| | - |
| | - /** |
| | - * Returns the fully qualified REST API URL, including host and path. The |
| | - * default port (e.g., https/443 or http/80) should be sufficient for most |
| | - * scenarios and therefore does not need to be specified. |
| | - * |
| | - * @return A non-null path to the mailer's REST API. |
| | - */ |
| | - protected abstract String getRestApiUrl(); |
| | - |
| | - /** |
| | - * Returns the wrapper for the user name and password. |
| | - * |
| | - * @return The session's credentials wrapper. |
| | - */ |
| | - private PasswordAuthentication getPasswordAuthentication() { |
| | - return getSession().getPasswordAuthentication( getURLName() ); |
| | - } |
| | - |
| | - /** |
| | - * Answers whether credentials have been set. |
| | - * |
| | - * @return true Messages will be subjected to authentication. |
| | - */ |
| | - public boolean hasPasswordAuthentication() { |
| | - return getPasswordAuthentication() != null; |
| | - } |
| | - |
| | - /** |
| | - * Returns the user name allowed to make HTTP requests for sending messages. |
| | - * |
| | - * @return The user name. |
| | - */ |
| | - protected String getUsername() { |
| | - return getPasswordAuthentication().getUserName(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the password associated with the username. This can, for example, |
| | - * be an API key. |
| | - * |
| | - * @return The password (or API key). |
| | - */ |
| | - protected String getPassword() { |
| | - return getPasswordAuthentication().getPassword(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the sender's e-mail address. If there are multiple senders, this |
| | - * will return the first. If no sender is assigned, this will return a new |
| | - * InternetAddress instance with a "no-reply@" prefix and host name of the |
| | - * transport agent for the suffix. |
| | - * |
| | - * @param message The message containing at least one sender. |
| | - * |
| | - * @return A non-null, possibly defaulted, InternetAddress instance. |
| | - * |
| | - * @throws MessagingException Error obtaining an address from the message. |
| | - */ |
| | - protected InternetAddress extractSender( Message message ) |
| | - throws MessagingException { |
| | - Address[] addresses = message.getFrom(); |
| | - |
| | - return (addresses.length > 0 && addresses[ 0 ] instanceof InternetAddress) |
| | - ? (InternetAddress)addresses[ 0 ] |
| | - : getDefaultSender(); |
| | - } |
| | - |
| | - /** |
| | - * Returns the default sender to use in the situation that a message has been |
| | - * created without at least one sender. This will return an address starting |
| | - * with "no-reply@" and ending with the domain of the transport agent that |
| | - * offers the HTTP REST API for sending messages. |
| | - * |
| | - * @return "no-reply@" + host name from URLName. |
| | - * |
| | - * @throws javax.mail.internet.AddressException Could not create a valid |
| | - * InternetAddress. |
| | - */ |
| | - protected InternetAddress getDefaultSender() throws AddressException { |
| | - return new InternetAddress( "no-reply@" + getURLName().getHost() ); |
| | - } |
| | - |
| | - /** |
| | - * Helper method. |
| | - * |
| | - * @return this.session |
| | - */ |
| | - private Session getSession() { |
| | - return this.session; |
| | +import static com.whitemagicsoftware.notify.TextHtmlMultipart.TEXT_HTML_TYPE; |
| | +import static com.whitemagicsoftware.notify.TextHtmlMultipart.TEXT_PLAIN_TYPE; |
| | +import java.io.BufferedReader; |
| | +import java.io.IOException; |
| | +import java.io.InputStreamReader; |
| | +import java.io.OutputStream; |
| | +import java.io.OutputStreamWriter; |
| | +import java.io.UnsupportedEncodingException; |
| | +import java.net.HttpURLConnection; |
| | +import java.net.MalformedURLException; |
| | +import java.net.URL; |
| | +import java.net.URLConnection; |
| | +import java.net.URLEncoder; |
| | +import java.nio.charset.Charset; |
| | +import java.nio.charset.StandardCharsets; |
| | +import javax.mail.Address; |
| | +import javax.mail.BodyPart; |
| | +import javax.mail.Message; |
| | +import javax.mail.MessagingException; |
| | +import javax.mail.Multipart; |
| | +import javax.mail.PasswordAuthentication; |
| | +import javax.mail.Session; |
| | +import javax.mail.Transport; |
| | +import javax.mail.URLName; |
| | +import static javax.mail.event.ConnectionEvent.CLOSED; |
| | +import static javax.mail.event.ConnectionEvent.DISCONNECTED; |
| | +import static javax.mail.event.ConnectionEvent.OPENED; |
| | +import javax.mail.internet.AddressException; |
| | +import javax.mail.internet.InternetAddress; |
| | + |
| | +/** |
| | + * Responsible for sending messages over HTTP. Subclasses are responsible for |
| | + * providing the sendMessage method implementation. |
| | + * |
| | + * @author White Magic Software, Ltd. |
| | + */ |
| | +public abstract class HttpTransport extends Transport { |
| | + |
| | + /** |
| | + * Optional token returned after sending a message. |
| | + */ |
| | + private Token token; |
| | + |
| | + /** |
| | + * Constructs a new HttpTransport capable of sending notifications via a |
| | + * RESTFUL API. |
| | + * |
| | + * @param session Session state. |
| | + * @param urlname Set value to null for HTTP. |
| | + */ |
| | + public HttpTransport( Session session, URLName urlname ) { |
| | + // This should set the protected instance variables of "this.session" |
| | + // and "this.urlname". |
| | + super( session, urlname ); |
| | + } |
| | + |
| | + /** |
| | + * Sets the credentials to use for permission to send messages. |
| | + * |
| | + * @param username The username authorised to send messages over HTTP. |
| | + * @param password The password (or API key) associated with the username. |
| | + */ |
| | + public void setAuthentication( String username, String password ) { |
| | + getSession().setPasswordAuthentication( |
| | + getURLName(), |
| | + new PasswordAuthentication( username, password ) |
| | + ); |
| | + } |
| | + |
| | + /** |
| | + * Sends the given data over the wire. |
| | + * |
| | + * @param data The URL-encoded parameters to send. |
| | + * |
| | + * @return A token on success. |
| | + * |
| | + * @throws javax.mail.MessagingException |
| | + */ |
| | + protected String send( String data ) throws MessagingException { |
| | + HttpURLConnection connection = open(); |
| | + |
| | + write( data, connection ); |
| | + return read( connection ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the default encoding to use for converting plain text into safe URL |
| | + * parameters. |
| | + * |
| | + * @return StandardCharsets.UTF_8 by default. |
| | + */ |
| | + protected Charset getDefaultCharset() { |
| | + return StandardCharsets.UTF_8; |
| | + } |
| | + |
| | + /** |
| | + * Returns the given text string encoded for passing as a URL parameter. |
| | + * |
| | + * @param text The text to encode. |
| | + * |
| | + * @return The encoded text. |
| | + * |
| | + * @throws MessagingException The text could not be encoded. |
| | + */ |
| | + protected String encode( final String text ) throws MessagingException { |
| | + String charset = getDefaultCharset().displayName(); |
| | + |
| | + try { |
| | + return URLEncoder.encode( text, charset ); |
| | + } catch( UnsupportedEncodingException e ) { |
| | + throw new MessagingException( |
| | + "Invalid default encoding: '" + charset + "'", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Helper method to return a URL-encoded string. |
| | + * |
| | + * @param text Text to encode. |
| | + * |
| | + * @return The StringBuilder's text, encoded to be URL-friendly. |
| | + * |
| | + * @throws MessagingException |
| | + */ |
| | + protected String encode( final StringBuilder text ) |
| | + throws MessagingException { |
| | + return encode( text.toString() ); |
| | + } |
| | + |
| | + /** |
| | + * Answers whether the string contains any characters. |
| | + * |
| | + * @param string Can be null or empty. |
| | + * |
| | + * @return true The string is empty or zero length or contains only |
| | + * whitespace. |
| | + */ |
| | + private boolean isEmpty( final String string ) { |
| | + return string == null || string.trim().length() == 0; |
| | + } |
| | + |
| | + /** |
| | + * |
| | + * @param message Message that contains content to extract. |
| | + * |
| | + * @return The text and HTML portions of the message in array index 0 and 1, |
| | + * respectively. |
| | + * |
| | + * @throws javax.mail.MessagingException Message body not extracted. |
| | + */ |
| | + protected String[] extractMessageContents( Message message ) |
| | + throws MessagingException { |
| | + String result[] = new String[]{ "", "" }; |
| | + |
| | + try { |
| | + // Ensure the mime types are applied. |
| | + // |
| | + // See also: http://stackoverflow.com/a/5031057/59087 |
| | + message.saveChanges(); |
| | + Object content = message.getContent(); |
| | + |
| | + if( content instanceof Multipart ) { |
| | + Multipart multipart = (Multipart)content; |
| | + |
| | + result[ 0 ] = extractMultipart( multipart, TEXT_PLAIN_TYPE ); |
| | + result[ 1 ] = extractMultipart( multipart, TEXT_HTML_TYPE ); |
| | + } else if( message.isMimeType( TEXT_PLAIN_TYPE ) ) { |
| | + result[ 0 ] = content.toString(); |
| | + } |
| | + } catch( IOException e ) { |
| | + throw new MessagingException( "Could not extract message content.", e ); |
| | + } |
| | + |
| | + return result; |
| | + } |
| | + |
| | + /** |
| | + * Extracts the content from the multipart instance that matches the given |
| | + * mimeType. |
| | + * |
| | + * @param multipart The multipart instance with contents to extract. |
| | + * @param mimeType The mimeType to match against the multipart. |
| | + * |
| | + * @return The contents from the multipart that match the mimeType. |
| | + * |
| | + * @throws MessagingException Failed to extract the content. |
| | + */ |
| | + private String extractMultipart( Multipart multipart, String mimeType ) |
| | + throws MessagingException { |
| | + String result = ""; |
| | + int count = multipart.getCount(); |
| | + |
| | + for( int i = 0; i < count; i++ ) { |
| | + BodyPart bodyPart = multipart.getBodyPart( i ); |
| | + Object content = exhumeBodyPart( bodyPart ); |
| | + |
| | + if( content instanceof Multipart ) { |
| | + result += extractMultipart( (Multipart)content, mimeType ); |
| | + } else if( bodyPart.isMimeType( mimeType ) ) { |
| | + result += "\n" + content.toString(); |
| | + } |
| | + } |
| | + |
| | + return result; |
| | + } |
| | + |
| | + /** |
| | + * Returns the body part content. |
| | + * |
| | + * @param bodyPart The body part containing content to retrieve. |
| | + * |
| | + * @return The body part's contents. |
| | + * |
| | + * @throws MessagingException Error extracting the body part contents. |
| | + */ |
| | + private Object exhumeBodyPart( BodyPart bodyPart ) |
| | + throws MessagingException { |
| | + try { |
| | + return bodyPart.getContent(); |
| | + } catch( IOException e ) { |
| | + throw new MessagingException( "Error opening body part contents.", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Writes the given URL-encoded parameters to the given connection. |
| | + * |
| | + * @param urlParameters The URL-encoded parameters to write. |
| | + * @param connection The connection that can be written to. |
| | + * |
| | + * @throws MessagingException Error writing the parameters to the given |
| | + * connection. |
| | + */ |
| | + protected void write( String urlParameters, HttpURLConnection connection ) |
| | + throws MessagingException { |
| | + try { |
| | + write( urlParameters, connection.getOutputStream() ); |
| | + |
| | + int response = connection.getResponseCode(); |
| | + |
| | + // Ensure a 200 response code is returned. |
| | + if( response != HttpURLConnection.HTTP_OK ) { |
| | + throw new MessagingException( "Unexpected response code: " + response ); |
| | + } |
| | + } catch( IOException e ) { |
| | + throw new MessagingException( "Could not stream output connection.", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Writes a set of encoded URL parameters to the given stream. |
| | + * |
| | + * @param urlParameters The parameters to send. |
| | + * @param outputStream The stream to receive the parameters. |
| | + * |
| | + * @throws MessagingException Message could not be sent. |
| | + */ |
| | + protected void write( String urlParameters, OutputStream outputStream ) |
| | + throws MessagingException { |
| | + try( OutputStreamWriter writer = new OutputStreamWriter( outputStream ); ) { |
| | + writer.write( urlParameters ); |
| | + writer.flush(); |
| | + } catch( IOException e ) { |
| | + disconnected(); |
| | + throw new MessagingException( "Could not send message.", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Reads the response code returned from a previous call to write. This must |
| | + * be called after write, not before. |
| | + * |
| | + * @param connection The connection that had data written to it. |
| | + * |
| | + * @return A non-null, possibly empty string. |
| | + * |
| | + * @throws MessagingException Response could not be read. |
| | + */ |
| | + protected String read( URLConnection connection ) throws MessagingException { |
| | + try { |
| | + return read( new InputStreamReader( connection.getInputStream() ) ); |
| | + } catch( IOException e ) { |
| | + disconnected(); |
| | + throw new MessagingException( "Could not stream input connection.", e ); |
| | + } finally { |
| | + closed(); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Notifies connection listeners that the connection to the transport agent |
| | + * has opened. |
| | + */ |
| | + protected void opened() { |
| | + notifyConnectionListeners( OPENED ); |
| | + } |
| | + |
| | + /** |
| | + * Notifies connection listeners that the connection to the transport agent |
| | + * has closed. |
| | + */ |
| | + protected void closed() { |
| | + notifyConnectionListeners( CLOSED ); |
| | + } |
| | + |
| | + /** |
| | + * Notifies connection listeners that the connection to the transport agent |
| | + * has disconnected. |
| | + */ |
| | + protected void disconnected() { |
| | + notifyConnectionListeners( DISCONNECTED ); |
| | + } |
| | + |
| | + /** |
| | + * Helper method. |
| | + * |
| | + * @param in The stream from a connection that had data written to it. |
| | + * |
| | + * @return A non-null, possibly empty string. |
| | + * |
| | + * @throws MessagingException Response could not be read. |
| | + */ |
| | + protected String read( InputStreamReader in ) throws MessagingException { |
| | + try( BufferedReader rd = new BufferedReader( in ) ) { |
| | + return rd.readLine(); |
| | + } catch( IOException e ) { |
| | + disconnected(); |
| | + throw new MessagingException( "Could not read status.", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Connects to the REST API over HTTP. |
| | + * |
| | + * @return An opened URLConnection instance. |
| | + * |
| | + * @throws javax.mail.MessagingException The REST API is unavailable. |
| | + */ |
| | + protected HttpURLConnection open() throws MessagingException { |
| | + HttpURLConnection connection = openConnection(); |
| | + connection.setDoOutput( true ); |
| | + opened(); |
| | + |
| | + return connection; |
| | + } |
| | + |
| | + /** |
| | + * Helper method to return an opened URL connection. |
| | + * |
| | + * @return An opened URLConnection instance. |
| | + * |
| | + * @throws javax.mail.MessagingException Error connecting to the provider. |
| | + */ |
| | + public HttpURLConnection openConnection() throws MessagingException { |
| | + try { |
| | + return (HttpURLConnection)getURL().openConnection(); |
| | + } catch( IOException e ) { |
| | + disconnected(); |
| | + throw new MessagingException( "Could not connect to provider.", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Helper method to return the REST API URL for sending messages. |
| | + * |
| | + * @return An HTTP(S) URL, unopened. |
| | + * |
| | + * @return @throws javax.mail.MessagingException |
| | + */ |
| | + private URL getURL() { |
| | + try { |
| | + return new URL( getRestApiUrl() ); |
| | + } catch( MalformedURLException e ) { |
| | + throw new RuntimeException( "Provider URL not set.", e ); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Returns the URLName by first creating a URL using the REST API URL. |
| | + * |
| | + * @return A non-null URLName. |
| | + */ |
| | + @Override |
| | + public URLName getURLName() { |
| | + try { |
| | + URL name = getURL(); |
| | + return new URLName( new URL( name.getProtocol(), name.getHost(), "/" ) ); |
| | + } catch( MalformedURLException e ) { |
| | + return super.getURLName(); |
| | + } |
| | + } |
| | + |
| | + /** |
| | + * Returns the fully qualified REST API URL, including host and path. The |
| | + * default port (e.g., https/443 or http/80) should be sufficient for most |
| | + * scenarios and therefore does not need to be specified. |
| | + * |
| | + * @return A non-null path to the mailer's REST API. |
| | + */ |
| | + protected abstract String getRestApiUrl(); |
| | + |
| | + /** |
| | + * Returns the wrapper for the user name and password. |
| | + * |
| | + * @return The session's credentials wrapper. |
| | + */ |
| | + private PasswordAuthentication getPasswordAuthentication() { |
| | + return getSession().getPasswordAuthentication( getURLName() ); |
| | + } |
| | + |
| | + /** |
| | + * Answers whether credentials have been set. |
| | + * |
| | + * @return true Messages will be subjected to authentication. |
| | + */ |
| | + public boolean hasPasswordAuthentication() { |
| | + return getPasswordAuthentication() != null; |
| | + } |
| | + |
| | + /** |
| | + * Returns the user name allowed to make HTTP requests for sending messages. |
| | + * |
| | + * @return The user name. |
| | + */ |
| | + protected String getUsername() { |
| | + return getPasswordAuthentication().getUserName(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the password associated with the username. This can, for example, |
| | + * be an API key. |
| | + * |
| | + * @return The password (or API key). |
| | + */ |
| | + protected String getPassword() { |
| | + return getPasswordAuthentication().getPassword(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the sender's e-mail address. If there are multiple senders, this |
| | + * will return the first. If no sender is assigned, this will return a new |
| | + * InternetAddress instance with a "no-reply@" prefix and host name of the |
| | + * transport agent for the suffix. |
| | + * |
| | + * @param message The message containing at least one sender. |
| | + * |
| | + * @return A non-null, possibly defaulted, InternetAddress instance. |
| | + * |
| | + * @throws MessagingException Error obtaining an address from the message. |
| | + */ |
| | + protected InternetAddress extractSender( Message message ) |
| | + throws MessagingException { |
| | + Address[] addresses = message.getFrom(); |
| | + |
| | + return (addresses.length > 0 && addresses[ 0 ] instanceof InternetAddress) |
| | + ? (InternetAddress)addresses[ 0 ] |
| | + : getDefaultSender(); |
| | + } |
| | + |
| | + /** |
| | + * Returns the default sender to use in the situation that a message has been |
| | + * created without at least one sender. This will return an address starting |
| | + * with "no-reply@" and ending with the domain of the transport agent that |
| | + * offers the HTTP REST API for sending messages. |
| | + * |
| | + * @return "no-reply@" + host name from URLName. |
| | + * |
| | + * @throws javax.mail.internet.AddressException Could not create a valid |
| | + * InternetAddress. |
| | + */ |
| | + protected InternetAddress getDefaultSender() throws AddressException { |
| | + return new InternetAddress( "no-reply@" + getURLName().getHost() ); |
| | + } |
| | + |
| | + /** |
| | + * Helper method. |
| | + * |
| | + * @return this.session |
| | + */ |
| | + private Session getSession() { |
| | + return this.session; |
| | + } |
| | + |
| | + protected void setToken( String token ) { |
| | + this.token = new Token( token ); |
| | + } |
| | + |
| | + /** |
| | + * Returns the token associated with the most recent call to sendMessage. |
| | + * |
| | + * @return A non-null, possibly empty Token instance. |
| | + */ |
| | + public Token getToken() { |
| | + return this.token == null ? Token.EMPTY : this.token; |
| | } |
| | } |