精简邮件通知组件

This commit is contained in:
椰子 2022-08-22 10:24:50 +08:00
parent 3600c0980e
commit e0162e100e
21 changed files with 3286 additions and 9 deletions

View File

@ -23,12 +23,6 @@
<artifactId>notify-core</artifactId> <artifactId>notify-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>

View File

@ -5,6 +5,9 @@ import com.simaek.notify.*;
import com.simaek.notify.email.EmailProvider; import com.simaek.notify.email.EmailProvider;
import com.simaek.notify.email.EmailTemplate; import com.simaek.notify.email.EmailTemplate;
import com.simaek.notify.email.EmailTemplateParsed; import com.simaek.notify.email.EmailTemplateParsed;
import com.simaek.notify.email.embedded.mail.javamail.JavaMailSender;
import com.simaek.notify.email.embedded.mail.javamail.JavaMailSenderImpl;
import com.simaek.notify.email.embedded.mail.javamail.MimeMessageHelper;
import com.simaek.notify.util.ExpressionUtils; import com.simaek.notify.util.ExpressionUtils;
import com.simaek.notify.util.StringUtils; import com.simaek.notify.util.StringUtils;
import okhttp3.Call; import okhttp3.Call;
@ -16,9 +19,6 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.InputStreamSource; import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;

View File

@ -0,0 +1,39 @@
package com.simaek.notify.email.embedded.mail;
/**
* Exception thrown on failed authentication.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
*/
public class MailAuthenticationException extends MailException {
/**
* Constructor for MailAuthenticationException.
*
* @param msg message
*/
public MailAuthenticationException(String msg) {
super(msg);
}
/**
* Constructor for MailAuthenticationException.
*
* @param msg the detail message
* @param cause the root cause from the mail API in use
*/
public MailAuthenticationException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructor for MailAuthenticationException.
*
* @param cause the root cause from the mail API in use
*/
public MailAuthenticationException(Throwable cause) {
super("Authentication failed", cause);
}
}

View File

@ -0,0 +1,32 @@
package com.simaek.notify.email.embedded.mail;
import org.springframework.core.NestedRuntimeException;
import org.springframework.lang.Nullable;
/**
* Base class for all mail exceptions.
*
* @author Dmitriy Kopylenko
*/
public abstract class MailException extends NestedRuntimeException {
/**
* Constructor for MailException.
*
* @param msg the detail message
*/
public MailException(String msg) {
super(msg);
}
/**
* Constructor for MailException.
*
* @param msg the detail message
* @param cause the root cause from the mail API in use
*/
public MailException(@Nullable String msg, @Nullable Throwable cause) {
super(msg, cause);
}
}

View File

@ -0,0 +1,46 @@
package com.simaek.notify.email.embedded.mail;
import com.simaek.notify.email.embedded.mail.javamail.MimeMessageHelper;
import java.util.Date;
/**
* This is a common interface for mail messages, allowing a user to set key
* values required in assembling a mail message, without needing to know if
* the underlying message is a simple text message or a more sophisticated
* MIME message.
*
* <p>Implemented by both SimpleMailMessage and MimeMessageHelper,
* to let message population code interact with a simple message or a
* MIME message through a common interface.
*
* @author Juergen Hoeller
* @see SimpleMailMessage
* @see MimeMessageHelper
* @since 1.1.5
*/
public interface MailMessage {
void setFrom(String from) throws MailParseException;
void setReplyTo(String replyTo) throws MailParseException;
void setTo(String to) throws MailParseException;
void setTo(String... to) throws MailParseException;
void setCc(String cc) throws MailParseException;
void setCc(String... cc) throws MailParseException;
void setBcc(String bcc) throws MailParseException;
void setBcc(String... bcc) throws MailParseException;
void setSentDate(Date sentDate) throws MailParseException;
void setSubject(String subject) throws MailParseException;
void setText(String text) throws MailParseException;
}

View File

@ -0,0 +1,39 @@
package com.simaek.notify.email.embedded.mail;
/**
* Exception thrown if illegal message properties are encountered.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
*/
public class MailParseException extends MailException {
/**
* Constructor for MailParseException.
*
* @param msg the detail message
*/
public MailParseException(String msg) {
super(msg);
}
/**
* Constructor for MailParseException.
*
* @param msg the detail message
* @param cause the root cause from the mail API in use
*/
public MailParseException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructor for MailParseException.
*
* @param cause the root cause from the mail API in use
*/
public MailParseException(Throwable cause) {
super("Could not parse mail", cause);
}
}

View File

@ -0,0 +1,35 @@
package com.simaek.notify.email.embedded.mail;
/**
* Exception to be thrown by user code if a mail cannot be prepared properly,
* for example when a FreeMarker template cannot be rendered for the mail text.
*
* @author Juergen Hoeller
* @since 1.1
*/
public class MailPreparationException extends MailException {
/**
* Constructor for MailPreparationException.
*
* @param msg the detail message
*/
public MailPreparationException(String msg) {
super(msg);
}
/**
* Constructor for MailPreparationException.
*
* @param msg the detail message
* @param cause the root cause from the mail API in use
*/
public MailPreparationException(String msg, Throwable cause) {
super(msg, cause);
}
public MailPreparationException(Throwable cause) {
super("Could not prepare mail", cause);
}
}

View File

@ -0,0 +1,184 @@
package com.simaek.notify.email.embedded.mail;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Exception thrown when a mail sending error is encountered.
* Can register failed messages with their exceptions.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
*/
public class MailSendException extends MailException {
private final transient Map<Object, Exception> failedMessages;
@Nullable
private final Exception[] messageExceptions;
/**
* Constructor for MailSendException.
*
* @param msg the detail message
*/
public MailSendException(String msg) {
this(msg, null);
}
/**
* Constructor for MailSendException.
*
* @param msg the detail message
* @param cause the root cause from the mail API in use
*/
public MailSendException(String msg, @Nullable Throwable cause) {
super(msg, cause);
this.failedMessages = new LinkedHashMap<>();
this.messageExceptions = null;
}
/**
* Constructor for registration of failed messages, with the
* messages that failed as keys, and the thrown exceptions as values.
* <p>The messages should be the same that were originally passed
* to the invoked send method.
*
* @param msg the detail message
* @param cause the root cause from the mail API in use
* @param failedMessages a Map of failed messages as keys and thrown
* exceptions as values
*/
public MailSendException(@Nullable String msg, @Nullable Throwable cause, Map<Object, Exception> failedMessages) {
super(msg, cause);
this.failedMessages = new LinkedHashMap<>(failedMessages);
this.messageExceptions = failedMessages.values().toArray(new Exception[0]);
}
/**
* Constructor for registration of failed messages, with the
* messages that failed as keys, and the thrown exceptions as values.
* <p>The messages should be the same that were originally passed
* to the invoked send method.
*
* @param failedMessages a Map of failed messages as keys and thrown
* exceptions as values
*/
public MailSendException(Map<Object, Exception> failedMessages) {
this(null, null, failedMessages);
}
/**
* Return a Map with the failed messages as keys, and the thrown exceptions
* as values.
* <p>Note that a general mail server connection failure will not result
* in failed messages being returned here: A message will only be
* contained here if actually sending it was attempted but failed.
* <p>The messages will be the same that were originally passed to the
* invoked send method, that is, SimpleMailMessages in case of using
* the generic MailSender interface.
* <p>In case of sending MimeMessage instances via JavaMailSender,
* the messages will be of type MimeMessage.
* <p><b>NOTE:</b> This Map will not be available after serialization.
* Use {@link #getMessageExceptions()} in such a scenario, which will
* be available after serialization as well.
*
* @return the Map of failed messages as keys and thrown exceptions as values
* @see SimpleMailMessage
* @see javax.mail.internet.MimeMessage
*/
public final Map<Object, Exception> getFailedMessages() {
return this.failedMessages;
}
/**
* Return an array with thrown message exceptions.
* <p>Note that a general mail server connection failure will not result
* in failed messages being returned here: A message will only be
* contained here if actually sending it was attempted but failed.
*
* @return the array of thrown message exceptions,
* or an empty array if no failed messages
*/
public final Exception[] getMessageExceptions() {
return (this.messageExceptions != null ? this.messageExceptions : new Exception[0]);
}
@Override
@Nullable
public String getMessage() {
if (ObjectUtils.isEmpty(this.messageExceptions)) {
return super.getMessage();
} else {
StringBuilder sb = new StringBuilder();
String baseMessage = super.getMessage();
if (baseMessage != null) {
sb.append(baseMessage).append(". ");
}
sb.append("Failed messages: ");
for (int i = 0; i < this.messageExceptions.length; i++) {
Exception subEx = this.messageExceptions[i];
sb.append(subEx.toString());
if (i < this.messageExceptions.length - 1) {
sb.append("; ");
}
}
return sb.toString();
}
}
@Override
public String toString() {
if (ObjectUtils.isEmpty(this.messageExceptions)) {
return super.toString();
} else {
StringBuilder sb = new StringBuilder(super.toString());
sb.append("; message exceptions (").append(this.messageExceptions.length).append(") are:");
for (int i = 0; i < this.messageExceptions.length; i++) {
Exception subEx = this.messageExceptions[i];
sb.append('\n').append("Failed message ").append(i + 1).append(": ");
sb.append(subEx);
}
return sb.toString();
}
}
@Override
public void printStackTrace(PrintStream ps) {
if (ObjectUtils.isEmpty(this.messageExceptions)) {
super.printStackTrace(ps);
} else {
ps.println(super.toString() + "; message exception details (" +
this.messageExceptions.length + ") are:");
for (int i = 0; i < this.messageExceptions.length; i++) {
Exception subEx = this.messageExceptions[i];
ps.println("Failed message " + (i + 1) + ":");
subEx.printStackTrace(ps);
}
}
}
@Override
public void printStackTrace(PrintWriter pw) {
if (ObjectUtils.isEmpty(this.messageExceptions)) {
super.printStackTrace(pw);
} else {
pw.println(super.toString() + "; message exception details (" +
this.messageExceptions.length + ") are:");
for (int i = 0; i < this.messageExceptions.length; i++) {
Exception subEx = this.messageExceptions[i];
pw.println("Failed message " + (i + 1) + ":");
subEx.printStackTrace(pw);
}
}
}
}

View File

@ -0,0 +1,40 @@
package com.simaek.notify.email.embedded.mail;
import com.simaek.notify.email.embedded.mail.javamail.JavaMailSender;
/**
* This interface defines a strategy for sending simple mails. Can be
* implemented for a variety of mailing systems due to the simple requirements.
* For richer functionality like MIME messages, consider JavaMailSender.
*
* <p>Allows for easy testing of clients, as it does not depend on JavaMail's
* infrastructure classes: no mocking of JavaMail Session or Transport necessary.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @see JavaMailSender
* @since 10.09.2003
*/
public interface MailSender {
/**
* Send the given simple mail message.
*
* @param simpleMessage the message to send
* @throws MailParseException in case of failure when parsing the message
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending the message
*/
void send(SimpleMailMessage simpleMessage) throws MailException;
/**
* Send the given array of simple mail messages in batch.
*
* @param simpleMessages the messages to send
* @throws MailParseException in case of failure when parsing a message
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending a message
*/
void send(SimpleMailMessage... simpleMessages) throws MailException;
}

View File

@ -0,0 +1,270 @@
package com.simaek.notify.email.embedded.mail;
import com.simaek.notify.email.embedded.mail.javamail.JavaMailSender;
import com.simaek.notify.email.embedded.mail.javamail.MimeMailMessage;
import com.simaek.notify.email.embedded.mail.javamail.MimeMessageHelper;
import com.simaek.notify.email.embedded.mail.javamail.MimeMessagePreparator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.util.Date;
/**
* Models a simple mail message, including data such as the from, to, cc, subject,
* and text fields.
*
* <p>Consider {@code JavaMailSender} and JavaMail {@code MimeMessages} for creating
* more sophisticated messages, for example messages with attachments, special
* character encodings, or personal names that accompany mail addresses.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @see MailSender
* @see JavaMailSender
* @see MimeMessagePreparator
* @see MimeMessageHelper
* @see MimeMailMessage
* @since 10.09.2003
*/
public class SimpleMailMessage implements MailMessage, Serializable {
@Nullable
private String from;
@Nullable
private String replyTo;
@Nullable
private String[] to;
@Nullable
private String[] cc;
@Nullable
private String[] bcc;
@Nullable
private Date sentDate;
@Nullable
private String subject;
@Nullable
private String text;
/**
* Create a new {@code SimpleMailMessage}.
*/
public SimpleMailMessage() {
}
/**
* Copy constructor for creating a new {@code SimpleMailMessage} from the state
* of an existing {@code SimpleMailMessage} instance.
*/
public SimpleMailMessage(SimpleMailMessage original) {
Assert.notNull(original, "'original' message argument must not be null");
this.from = original.getFrom();
this.replyTo = original.getReplyTo();
this.to = copyOrNull(original.getTo());
this.cc = copyOrNull(original.getCc());
this.bcc = copyOrNull(original.getBcc());
this.sentDate = original.getSentDate();
this.subject = original.getSubject();
this.text = original.getText();
}
@Override
public void setFrom(String from) {
this.from = from;
}
@Nullable
public String getFrom() {
return this.from;
}
@Override
public void setReplyTo(String replyTo) {
this.replyTo = replyTo;
}
@Nullable
public String getReplyTo() {
return this.replyTo;
}
@Override
public void setTo(String to) {
this.to = new String[]{to};
}
@Override
public void setTo(String... to) {
this.to = to;
}
@Nullable
public String[] getTo() {
return this.to;
}
@Override
public void setCc(String cc) {
this.cc = new String[]{cc};
}
@Override
public void setCc(String... cc) {
this.cc = cc;
}
@Nullable
public String[] getCc() {
return this.cc;
}
@Override
public void setBcc(String bcc) {
this.bcc = new String[]{bcc};
}
@Override
public void setBcc(String... bcc) {
this.bcc = bcc;
}
@Nullable
public String[] getBcc() {
return this.bcc;
}
@Override
public void setSentDate(Date sentDate) {
this.sentDate = sentDate;
}
@Nullable
public Date getSentDate() {
return this.sentDate;
}
@Override
public void setSubject(String subject) {
this.subject = subject;
}
@Nullable
public String getSubject() {
return this.subject;
}
@Override
public void setText(String text) {
this.text = text;
}
@Nullable
public String getText() {
return this.text;
}
/**
* Copy the contents of this message to the given target message.
*
* @param target the {@code MailMessage} to copy to
*/
public void copyTo(MailMessage target) {
Assert.notNull(target, "'target' MailMessage must not be null");
if (getFrom() != null) {
target.setFrom(getFrom());
}
if (getReplyTo() != null) {
target.setReplyTo(getReplyTo());
}
if (getTo() != null) {
target.setTo(copy(getTo()));
}
if (getCc() != null) {
target.setCc(copy(getCc()));
}
if (getBcc() != null) {
target.setBcc(copy(getBcc()));
}
if (getSentDate() != null) {
target.setSentDate(getSentDate());
}
if (getSubject() != null) {
target.setSubject(getSubject());
}
if (getText() != null) {
target.setText(getText());
}
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof SimpleMailMessage)) {
return false;
}
SimpleMailMessage otherMessage = (SimpleMailMessage) other;
return (ObjectUtils.nullSafeEquals(this.from, otherMessage.from) &&
ObjectUtils.nullSafeEquals(this.replyTo, otherMessage.replyTo) &&
ObjectUtils.nullSafeEquals(this.to, otherMessage.to) &&
ObjectUtils.nullSafeEquals(this.cc, otherMessage.cc) &&
ObjectUtils.nullSafeEquals(this.bcc, otherMessage.bcc) &&
ObjectUtils.nullSafeEquals(this.sentDate, otherMessage.sentDate) &&
ObjectUtils.nullSafeEquals(this.subject, otherMessage.subject) &&
ObjectUtils.nullSafeEquals(this.text, otherMessage.text));
}
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(this.from);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.replyTo);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.to);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.cc);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.bcc);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.sentDate);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.subject);
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("SimpleMailMessage: ");
sb.append("from=").append(this.from).append("; ");
sb.append("replyTo=").append(this.replyTo).append("; ");
sb.append("to=").append(StringUtils.arrayToCommaDelimitedString(this.to)).append("; ");
sb.append("cc=").append(StringUtils.arrayToCommaDelimitedString(this.cc)).append("; ");
sb.append("bcc=").append(StringUtils.arrayToCommaDelimitedString(this.bcc)).append("; ");
sb.append("sentDate=").append(this.sentDate).append("; ");
sb.append("subject=").append(this.subject).append("; ");
sb.append("text=").append(this.text);
return sb.toString();
}
@Nullable
private static String[] copyOrNull(@Nullable String[] state) {
if (state == null) {
return null;
}
return copy(state);
}
private static String[] copy(String[] state) {
return state.clone();
}
}

View File

@ -0,0 +1,159 @@
package com.simaek.notify.email.embedded.mail.javamail;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* Spring-configurable {@code FileTypeMap} implementation that will read
* MIME type to file extension mappings from a standard JavaMail MIME type
* mapping file, using a standard {@code MimetypesFileTypeMap} underneath.
*
* <p>The mapping file should be in the following format, as specified by the
* Java Activation Framework:
*
* <pre class="code">
* # map text/html to .htm and .html files
* text/html html htm HTML HTM</pre>
* <p>
* Lines starting with {@code #} are treated as comments and are ignored. All
* other lines are treated as mappings. Each mapping line should contain the MIME
* type as the first entry and then each file extension to map to that MIME type
* as subsequent entries. Each entry is separated by spaces or tabs.
*
* <p>By default, the mappings in the {@code mime.types} file located in the
* same package as this class are used, which cover many common file extensions
* (in contrast to the out-of-the-box mappings in {@code activation.jar}).
* This can be overridden using the {@code mappingLocation} property.
*
* <p>Additional mappings can be added via the {@code mappings} bean property,
* as lines that follow the {@code mime.types} file format.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @see #setMappingLocation
* @see #setMappings
* @see MimetypesFileTypeMap
* @since 1.2
*/
public class ConfigurableMimeFileTypeMap extends FileTypeMap {
/**
* The {@code Resource} to load the mapping file from.
*/
private Resource mappingLocation = new ClassPathResource("mime.types", getClass());
/**
* Used to configure additional mappings.
*/
@Nullable
private String[] mappings;
/**
* The delegate FileTypeMap, compiled from the mappings in the mapping file
* and the entries in the {@code mappings} property.
*/
@Nullable
private FileTypeMap fileTypeMap;
/**
* Specify the {@code Resource} from which mappings are loaded.
* <p>Needs to follow the {@code mime.types} file format, as specified
* by the Java Activation Framework, containing lines such as:<br>
* {@code text/html html htm HTML HTM}
*/
public void setMappingLocation(Resource mappingLocation) {
this.mappingLocation = mappingLocation;
}
/**
* Specify additional MIME type mappings as lines that follow the
* {@code mime.types} file format, as specified by the
* Java Activation Framework. For example:<br>
* {@code text/html html htm HTML HTM}
*/
public void setMappings(String... mappings) {
this.mappings = mappings;
}
/**
* Return the delegate FileTypeMap, compiled from the mappings in the mapping file
* and the entries in the {@code mappings} property.
*
* @see #setMappingLocation
* @see #setMappings
* @see #createFileTypeMap
*/
protected final FileTypeMap getFileTypeMap() {
if (this.fileTypeMap == null) {
try {
this.fileTypeMap = createFileTypeMap(this.mappingLocation, this.mappings);
} catch (IOException ex) {
throw new IllegalStateException(
"Could not load specified MIME type mapping file: " + this.mappingLocation, ex);
}
}
return this.fileTypeMap;
}
/**
* Compile a {@link FileTypeMap} from the mappings in the given mapping file
* and the given mapping entries.
* <p>The default implementation creates an Activation Framework {@link MimetypesFileTypeMap},
* passing in an InputStream from the mapping resource (if any) and registering
* the mapping lines programmatically.
*
* @param mappingLocation a {@code mime.types} mapping resource (can be {@code null})
* @param mappings an array of MIME type mapping lines (can be {@code null})
* @return the compiled FileTypeMap
* @throws IOException if resource access failed
* @see MimetypesFileTypeMap#MimetypesFileTypeMap(InputStream)
* @see MimetypesFileTypeMap#addMimeTypes(String)
*/
protected FileTypeMap createFileTypeMap(@Nullable Resource mappingLocation, @Nullable String[] mappings) throws IOException {
MimetypesFileTypeMap fileTypeMap = null;
if (mappingLocation != null) {
try (InputStream is = mappingLocation.getInputStream()) {
fileTypeMap = new MimetypesFileTypeMap(is);
}
} else {
fileTypeMap = new MimetypesFileTypeMap();
}
if (mappings != null) {
for (String mapping : mappings) {
fileTypeMap.addMimeTypes(mapping);
}
}
return fileTypeMap;
}
/**
* Delegates to the underlying FileTypeMap.
*
* @see #getFileTypeMap()
*/
@Override
public String getContentType(File file) {
return getFileTypeMap().getContentType(file);
}
/**
* Delegates to the underlying FileTypeMap.
*
* @see #getFileTypeMap()
*/
@Override
public String getContentType(String fileName) {
return getFileTypeMap().getContentType(fileName);
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.simaek.notify.email.embedded.mail.javamail;
import org.springframework.util.StringUtils;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import java.beans.PropertyEditorSupport;
/**
* Editor for {@code java.mail.internet.InternetAddress},
* to directly populate an InternetAddress property.
*
* <p>Expects the same syntax as InternetAddress's constructor with
* a String argument. Converts empty Strings into null values.
*
* @author Juergen Hoeller
* @since 1.2.3
* @see InternetAddress
*/
public class InternetAddressEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.hasText(text)) {
try {
setValue(new InternetAddress(text));
}
catch (AddressException ex) {
throw new IllegalArgumentException("Could not parse mail address: " + ex.getMessage());
}
}
else {
setValue(null);
}
}
@Override
public String getAsText() {
InternetAddress value = (InternetAddress) getValue();
return (value != null ? value.toUnicodeString() : "");
}
}

View File

@ -0,0 +1,118 @@
package com.simaek.notify.email.embedded.mail.javamail;
import com.simaek.notify.email.embedded.mail.*;
import javax.mail.internet.MimeMessage;
import java.io.InputStream;
/**
* Extended {@link MailSender} interface for JavaMail,
* supporting MIME messages both as direct arguments and through preparation
* callbacks. Typically used in conjunction with the {@link MimeMessageHelper}
* class for convenient creation of JavaMail {@link MimeMessage MimeMessages},
* including attachments etc.
*
* <p>Clients should talk to the mail sender through this interface if they need
* mail functionality beyond {@link SimpleMailMessage}.
* The production implementation is {@link JavaMailSenderImpl}; for testing,
* mocks can be created based on this interface. Clients will typically receive
* the JavaMailSender reference through dependency injection.
*
* <p>The recommended way of using this interface is the {@link MimeMessagePreparator}
* mechanism, possibly using a {@link MimeMessageHelper} for populating the message.
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
*
* <p>The entire JavaMail {@link javax.mail.Session} management is abstracted
* by the JavaMailSender. Client code should not deal with a Session in any way,
* rather leave the entire JavaMail configuration and resource handling to the
* JavaMailSender implementation. This also increases testability.
*
* <p>A JavaMailSender client is not as easy to test as a plain
* {@link MailSender} client, but still straightforward
* compared to traditional JavaMail code: Just let {@link #createMimeMessage()}
* return a plain {@link MimeMessage} created with a
* {@code Session.getInstance(new Properties())} call, and check the passed-in
* messages in your mock implementations of the various {@code send} methods.
*
* @author Juergen Hoeller
* @see MimeMessage
* @see javax.mail.Session
* @see JavaMailSenderImpl
* @see MimeMessagePreparator
* @see MimeMessageHelper
* @since 07.10.2003
*/
public interface JavaMailSender extends MailSender {
/**
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
* of this sender. Needs to be called to create MimeMessage instances
* that can be prepared by the client and passed to send(MimeMessage).
*
* @return the new MimeMessage instance
* @see #send(MimeMessage)
* @see #send(MimeMessage[])
*/
MimeMessage createMimeMessage();
/**
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
* of this sender, using the given input stream as the message source.
*
* @param contentStream the raw MIME input stream for the message
* @return the new MimeMessage instance
* @throws MailParseException in case of message creation failure
*/
MimeMessage createMimeMessage(InputStream contentStream) throws MailException;
/**
* Send the given JavaMail MIME message.
* The message needs to have been created with {@link #createMimeMessage()}.
*
* @param mimeMessage message to send
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending the message
* @see #createMimeMessage
*/
void send(MimeMessage mimeMessage) throws MailException;
/**
* Send the given array of JavaMail MIME messages in batch.
* The messages need to have been created with {@link #createMimeMessage()}.
*
* @param mimeMessages messages to send
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending a message
* @see #createMimeMessage
*/
void send(MimeMessage... mimeMessages) throws MailException;
/**
* Send the JavaMail MIME message prepared by the given MimeMessagePreparator.
* <p>Alternative way to prepare MimeMessage instances, instead of
* {@link #createMimeMessage()} and {@link #send(MimeMessage)} calls.
* Takes care of proper exception conversion.
*
* @param mimeMessagePreparator the preparator to use
* @throws MailPreparationException in case of failure when preparing the message
* @throws MailParseException in case of failure when parsing the message
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending the message
*/
void send(MimeMessagePreparator mimeMessagePreparator) throws MailException;
/**
* Send the JavaMail MIME messages prepared by the given MimeMessagePreparators.
* <p>Alternative way to prepare MimeMessage instances, instead of
* {@link #createMimeMessage()} and {@link #send(MimeMessage[])} calls.
* Takes care of proper exception conversion.
*
* @param mimeMessagePreparators the preparator to use
* @throws MailPreparationException in case of failure when preparing a message
* @throws MailParseException in case of failure when parsing a message
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending a message
*/
void send(MimeMessagePreparator... mimeMessagePreparators) throws MailException;
}

View File

@ -0,0 +1,507 @@
package com.simaek.notify.email.embedded.mail.javamail;
import com.simaek.notify.email.embedded.mail.*;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import javax.activation.FileTypeMap;
import javax.mail.*;
import javax.mail.internet.MimeMessage;
import java.io.InputStream;
import java.util.*;
/**
* Production implementation of the {@link JavaMailSender} interface,
* supporting both JavaMail {@link MimeMessage MimeMessages} and Spring
* {@link SimpleMailMessage SimpleMailMessages}. Can also be used as a
* plain {@link MailSender} implementation.
*
* <p>Allows for defining all settings locally as bean properties.
* Alternatively, a pre-configured JavaMail {@link Session} can be
* specified, possibly pulled from an application server's JNDI environment.
*
* <p>Non-default properties in this object will always override the settings
* in the JavaMail {@code Session}. Note that if overriding all values locally,
* there is no added value in setting a pre-configured {@code Session}.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @see MimeMessage
* @see Session
* @see #setSession
* @see #setJavaMailProperties
* @see #setHost
* @see #setPort
* @see #setUsername
* @see #setPassword
* @since 10.09.2003
*/
public class JavaMailSenderImpl implements JavaMailSender {
/**
* The default protocol: 'smtp'.
*/
public static final String DEFAULT_PROTOCOL = "smtp";
/**
* The default port: -1.
*/
public static final int DEFAULT_PORT = -1;
private static final String HEADER_MESSAGE_ID = "Message-ID";
private Properties javaMailProperties = new Properties();
@Nullable
private Session session;
@Nullable
private String protocol;
@Nullable
private String host;
private int port = DEFAULT_PORT;
@Nullable
private String username;
@Nullable
private String password;
@Nullable
private String defaultEncoding;
@Nullable
private FileTypeMap defaultFileTypeMap;
/**
* Create a new instance of the {@code JavaMailSenderImpl} class.
* <p>Initializes the {@link #setDefaultFileTypeMap "defaultFileTypeMap"}
* property with a default {@link ConfigurableMimeFileTypeMap}.
*/
public JavaMailSenderImpl() {
ConfigurableMimeFileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap();
// fileTypeMap.afterPropertiesSet();
this.defaultFileTypeMap = fileTypeMap;
}
/**
* Set JavaMail properties for the {@code Session}.
* <p>A new {@code Session} will be created with those properties.
* Use either this method or {@link #setSession}, but not both.
* <p>Non-default properties in this instance will override given
* JavaMail properties.
*/
public void setJavaMailProperties(Properties javaMailProperties) {
this.javaMailProperties = javaMailProperties;
synchronized (this) {
this.session = null;
}
}
/**
* Allow {code Map} access to the JavaMail properties of this sender,
* with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via
* {code javaMailProperties[mail.smtp.auth]}.
*/
public Properties getJavaMailProperties() {
return this.javaMailProperties;
}
/**
* Set the JavaMail {@code Session}, possibly pulled from JNDI.
* <p>Default is a new {@code Session} without defaults, that is
* completely configured via this instance's properties.
* <p>If using a pre-configured {@code Session}, non-default properties
* in this instance will override the settings in the {@code Session}.
*
* @see #setJavaMailProperties
*/
public synchronized void setSession(Session session) {
Assert.notNull(session, "Session must not be null");
this.session = session;
}
/**
* Return the JavaMail {@code Session},
* lazily initializing it if it hasn't been specified explicitly.
*/
public synchronized Session getSession() {
if (this.session == null) {
this.session = Session.getInstance(this.javaMailProperties);
}
return this.session;
}
/**
* Set the mail protocol. Default is "smtp".
*/
public void setProtocol(@Nullable String protocol) {
this.protocol = protocol;
}
/**
* Return the mail protocol.
*/
@Nullable
public String getProtocol() {
return this.protocol;
}
/**
* Set the mail server host, typically an SMTP host.
* <p>Default is the default host of the underlying JavaMail Session.
*/
public void setHost(@Nullable String host) {
this.host = host;
}
/**
* Return the mail server host.
*/
@Nullable
public String getHost() {
return this.host;
}
/**
* Set the mail server port.
* <p>Default is {@link #DEFAULT_PORT}, letting JavaMail use the default
* SMTP port (25).
*/
public void setPort(int port) {
this.port = port;
}
/**
* Return the mail server port.
*/
public int getPort() {
return this.port;
}
/**
* Set the username for the account at the mail host, if any.
* <p>Note that the underlying JavaMail {@code Session} has to be
* configured with the property {@code "mail.smtp.auth"} set to
* {@code true}, else the specified username will not be sent to the
* mail server by the JavaMail runtime. If you are not explicitly passing
* in a {@code Session} to use, simply specify this setting via
* {@link #setJavaMailProperties}.
*
* @see #setSession
* @see #setPassword
*/
public void setUsername(@Nullable String username) {
this.username = username;
}
/**
* Return the username for the account at the mail host.
*/
@Nullable
public String getUsername() {
return this.username;
}
/**
* Set the password for the account at the mail host, if any.
* <p>Note that the underlying JavaMail {@code Session} has to be
* configured with the property {@code "mail.smtp.auth"} set to
* {@code true}, else the specified password will not be sent to the
* mail server by the JavaMail runtime. If you are not explicitly passing
* in a {@code Session} to use, simply specify this setting via
* {@link #setJavaMailProperties}.
*
* @see #setSession
* @see #setUsername
*/
public void setPassword(@Nullable String password) {
this.password = password;
}
/**
* Return the password for the account at the mail host.
*/
@Nullable
public String getPassword() {
return this.password;
}
/**
* Set the default encoding to use for {@link MimeMessage MimeMessages}
* created by this instance.
* <p>Such an encoding will be auto-detected by {@link MimeMessageHelper}.
*/
public void setDefaultEncoding(@Nullable String defaultEncoding) {
this.defaultEncoding = defaultEncoding;
}
/**
* Return the default encoding for {@link MimeMessage MimeMessages},
* or {@code null} if none.
*/
@Nullable
public String getDefaultEncoding() {
return this.defaultEncoding;
}
/**
* Set the default Java Activation {@link FileTypeMap} to use for
* {@link MimeMessage MimeMessages} created by this instance.
* <p>A {@code FileTypeMap} specified here will be autodetected by
* {@link MimeMessageHelper}, avoiding the need to specify the
* {@code FileTypeMap} for each {@code MimeMessageHelper} instance.
* <p>For example, you can specify a custom instance of Spring's
* {@link ConfigurableMimeFileTypeMap} here. If not explicitly specified,
* a default {@code ConfigurableMimeFileTypeMap} will be used, containing
* an extended set of MIME type mappings (as defined by the
* {@code mime.types} file contained in the Spring jar).
*
* @see MimeMessageHelper#setFileTypeMap
*/
public void setDefaultFileTypeMap(@Nullable FileTypeMap defaultFileTypeMap) {
this.defaultFileTypeMap = defaultFileTypeMap;
}
/**
* Return the default Java Activation {@link FileTypeMap} for
* {@link MimeMessage MimeMessages}, or {@code null} if none.
*/
@Nullable
public FileTypeMap getDefaultFileTypeMap() {
return this.defaultFileTypeMap;
}
//---------------------------------------------------------------------
// Implementation of MailSender
//---------------------------------------------------------------------
@Override
public void send(SimpleMailMessage simpleMessage) throws MailException {
send(new SimpleMailMessage[]{simpleMessage});
}
@Override
public void send(SimpleMailMessage... simpleMessages) throws MailException {
List<MimeMessage> mimeMessages = new ArrayList<>(simpleMessages.length);
for (SimpleMailMessage simpleMessage : simpleMessages) {
MimeMailMessage message = new MimeMailMessage(createMimeMessage());
simpleMessage.copyTo(message);
mimeMessages.add(message.getMimeMessage());
}
doSend(mimeMessages.toArray(new MimeMessage[0]), simpleMessages);
}
//---------------------------------------------------------------------
// Implementation of JavaMailSender
//---------------------------------------------------------------------
/**
* This implementation creates a SmartMimeMessage, holding the specified
* default encoding and default FileTypeMap. This special defaults-carrying
* message will be autodetected by {@link MimeMessageHelper}, which will use
* the carried encoding and FileTypeMap unless explicitly overridden.
*
* @see #setDefaultEncoding
* @see #setDefaultFileTypeMap
*/
@Override
public MimeMessage createMimeMessage() {
return new SmartMimeMessage(getSession(), getDefaultEncoding(), getDefaultFileTypeMap());
}
@Override
public MimeMessage createMimeMessage(InputStream contentStream) throws MailException {
try {
return new MimeMessage(getSession(), contentStream);
} catch (Exception ex) {
throw new MailParseException("Could not parse raw MIME content", ex);
}
}
@Override
public void send(MimeMessage mimeMessage) throws MailException {
send(new MimeMessage[]{mimeMessage});
}
@Override
public void send(MimeMessage... mimeMessages) throws MailException {
doSend(mimeMessages, null);
}
@Override
public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException {
send(new MimeMessagePreparator[]{mimeMessagePreparator});
}
@Override
public void send(MimeMessagePreparator... mimeMessagePreparators) throws MailException {
try {
List<MimeMessage> mimeMessages = new ArrayList<>(mimeMessagePreparators.length);
for (MimeMessagePreparator preparator : mimeMessagePreparators) {
MimeMessage mimeMessage = createMimeMessage();
preparator.prepare(mimeMessage);
mimeMessages.add(mimeMessage);
}
send(mimeMessages.toArray(new MimeMessage[0]));
} catch (MailException ex) {
throw ex;
} catch (MessagingException ex) {
throw new MailParseException(ex);
} catch (Exception ex) {
throw new MailPreparationException(ex);
}
}
/**
* Validate that this instance can connect to the server that it is configured
* for. Throws a {@link MessagingException} if the connection attempt failed.
*/
public void testConnection() throws MessagingException {
Transport transport = null;
try {
transport = connectTransport();
} finally {
if (transport != null) {
transport.close();
}
}
}
/**
* Actually send the given array of MimeMessages via JavaMail.
*
* @param mimeMessages the MimeMessage objects to send
* @param originalMessages corresponding original message objects
* that the MimeMessages have been created from (with same array
* length and indices as the "mimeMessages" array), if any
* @throws MailAuthenticationException in case of authentication failure
* @throws MailSendException in case of failure when sending a message
*/
protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
Map<Object, Exception> failedMessages = new LinkedHashMap<>();
Transport transport = null;
try {
for (int i = 0; i < mimeMessages.length; i++) {
// Check transport connection first...
if (transport == null || !transport.isConnected()) {
if (transport != null) {
try {
transport.close();
} catch (Exception ex) {
// Ignore - we're reconnecting anyway
}
transport = null;
}
try {
transport = connectTransport();
} catch (AuthenticationFailedException ex) {
throw new MailAuthenticationException(ex);
} catch (Exception ex) {
// Effectively, all remaining messages failed...
for (int j = i; j < mimeMessages.length; j++) {
Object original = (originalMessages != null ? originalMessages[j] : mimeMessages[j]);
failedMessages.put(original, ex);
}
throw new MailSendException("Mail server connection failed", ex, failedMessages);
}
}
// Send message via current transport...
MimeMessage mimeMessage = mimeMessages[i];
try {
if (mimeMessage.getSentDate() == null) {
mimeMessage.setSentDate(new Date());
}
String messageId = mimeMessage.getMessageID();
mimeMessage.saveChanges();
if (messageId != null) {
// Preserve explicitly specified message id...
mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId);
}
Address[] addresses = mimeMessage.getAllRecipients();
transport.sendMessage(mimeMessage, (addresses != null ? addresses : new Address[0]));
} catch (Exception ex) {
Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
failedMessages.put(original, ex);
}
}
} finally {
try {
if (transport != null) {
transport.close();
}
} catch (Exception ex) {
if (!failedMessages.isEmpty()) {
throw new MailSendException("Failed to close server connection after message failures", ex,
failedMessages);
} else {
throw new MailSendException("Failed to close server connection after message sending", ex);
}
}
}
if (!failedMessages.isEmpty()) {
throw new MailSendException(failedMessages);
}
}
/**
* Obtain and connect a Transport from the underlying JavaMail Session,
* passing in the specified host, port, username, and password.
*
* @return the connected Transport object
* @throws MessagingException if the connect attempt failed
* @see #getTransport
* @see #getHost()
* @see #getPort()
* @see #getUsername()
* @see #getPassword()
* @since 4.1.2
*/
protected Transport connectTransport() throws MessagingException {
String username = getUsername();
String password = getPassword();
if ("".equals(username)) { // probably from a placeholder
username = null;
if ("".equals(password)) { // in conjunction with "" username, this means no password to use
password = null;
}
}
Transport transport = getTransport(getSession());
transport.connect(getHost(), getPort(), username, password);
return transport;
}
/**
* Obtain a Transport object from the given JavaMail Session,
* using the configured protocol.
* <p>Can be overridden in subclasses, e.g. to return a mock Transport object.
*
* @see Session#getTransport(String)
* @see #getSession()
* @see #getProtocol()
*/
protected Transport getTransport(Session session) throws NoSuchProviderException {
String protocol = getProtocol();
if (protocol == null) {
protocol = session.getProperty("mail.transport.protocol");
if (protocol == null) {
protocol = DEFAULT_PROTOCOL;
}
}
return session.getTransport(protocol);
}
}

View File

@ -0,0 +1,160 @@
package com.simaek.notify.email.embedded.mail.javamail;
import com.simaek.notify.email.embedded.mail.MailMessage;
import com.simaek.notify.email.embedded.mail.MailParseException;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Date;
/**
* Implementation of the MailMessage interface for a JavaMail MIME message,
* to let message population code interact with a simple message or a MIME
* message through a common interface.
*
* <p>Uses a MimeMessageHelper underneath. Can either be created with a
* MimeMessageHelper instance or with a JavaMail MimeMessage instance.
*
* @author Juergen Hoeller
* @see MimeMessageHelper
* @see MimeMessage
* @since 1.1.5
*/
public class MimeMailMessage implements MailMessage {
private final MimeMessageHelper helper;
/**
* Create a new MimeMailMessage based on the given MimeMessageHelper.
*
* @param mimeMessageHelper the MimeMessageHelper
*/
public MimeMailMessage(MimeMessageHelper mimeMessageHelper) {
this.helper = mimeMessageHelper;
}
/**
* Create a new MimeMailMessage based on the given JavaMail MimeMessage.
*
* @param mimeMessage the JavaMail MimeMessage
*/
public MimeMailMessage(MimeMessage mimeMessage) {
this.helper = new MimeMessageHelper(mimeMessage);
}
/**
* Return the MimeMessageHelper that this MimeMailMessage is based on.
*/
public final MimeMessageHelper getMimeMessageHelper() {
return this.helper;
}
/**
* Return the JavaMail MimeMessage that this MimeMailMessage is based on.
*/
public final MimeMessage getMimeMessage() {
return this.helper.getMimeMessage();
}
@Override
public void setFrom(String from) throws MailParseException {
try {
this.helper.setFrom(from);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setReplyTo(String replyTo) throws MailParseException {
try {
this.helper.setReplyTo(replyTo);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setTo(String to) throws MailParseException {
try {
this.helper.setTo(to);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setTo(String... to) throws MailParseException {
try {
this.helper.setTo(to);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setCc(String cc) throws MailParseException {
try {
this.helper.setCc(cc);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setCc(String... cc) throws MailParseException {
try {
this.helper.setCc(cc);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setBcc(String bcc) throws MailParseException {
try {
this.helper.setBcc(bcc);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setBcc(String... bcc) throws MailParseException {
try {
this.helper.setBcc(bcc);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setSentDate(Date sentDate) throws MailParseException {
try {
this.helper.setSentDate(sentDate);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setSubject(String subject) throws MailParseException {
try {
this.helper.setSubject(subject);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
@Override
public void setText(String text) throws MailParseException {
try {
this.helper.setText(text);
} catch (MessagingException ex) {
throw new MailParseException(ex);
}
}
}

View File

@ -0,0 +1,39 @@
package com.simaek.notify.email.embedded.mail.javamail;
import javax.mail.internet.MimeMessage;
/**
* Callback interface for the preparation of JavaMail MIME messages.
*
* <p>The corresponding {@code send} methods of {@link JavaMailSender}
* will take care of the actual creation of a {@link MimeMessage} instance,
* and of proper exception conversion.
*
* <p>It is often convenient to use a {@link MimeMessageHelper} for populating
* the passed-in MimeMessage, in particular when working with attachments or
* special character encodings.
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
*
* @author Juergen Hoeller
* @see JavaMailSender#send(MimeMessagePreparator)
* @see JavaMailSender#send(MimeMessagePreparator[])
* @see MimeMessageHelper
* @since 07.10.2003
*/
@FunctionalInterface
public interface MimeMessagePreparator {
/**
* Prepare the given new MimeMessage instance.
*
* @param mimeMessage the message to prepare
* @throws javax.mail.MessagingException passing any exceptions thrown by MimeMessage
* methods through for automatic conversion to the MailException hierarchy
* @throws java.io.IOException passing any exceptions thrown by MimeMessage methods
* through for automatic conversion to the MailException hierarchy
* @throws Exception if mail preparation failed, for example when a
* FreeMarker template cannot be rendered for the mail text
*/
void prepare(MimeMessage mimeMessage) throws Exception;
}

View File

@ -0,0 +1,65 @@
package com.simaek.notify.email.embedded.mail.javamail;
import org.springframework.lang.Nullable;
import javax.activation.FileTypeMap;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
/**
* Special subclass of the standard JavaMail {@link MimeMessage}, carrying a
* default encoding to be used when populating the message and a default Java
* Activation {@link FileTypeMap} to be used for resolving attachment types.
*
* <p>Created by {@link JavaMailSenderImpl} in case of a specified default encoding
* and/or default FileTypeMap. Autodetected by {@link MimeMessageHelper}, which
* will use the carried encoding and FileTypeMap unless explicitly overridden.
*
* @author Juergen Hoeller
* @see JavaMailSenderImpl#createMimeMessage()
* @see MimeMessageHelper#getDefaultEncoding(MimeMessage)
* @see MimeMessageHelper#getDefaultFileTypeMap(MimeMessage)
* @since 1.2
*/
class SmartMimeMessage extends MimeMessage {
@Nullable
private final String defaultEncoding;
@Nullable
private final FileTypeMap defaultFileTypeMap;
/**
* Create a new SmartMimeMessage.
*
* @param session the JavaMail Session to create the message for
* @param defaultEncoding the default encoding, or {@code null} if none
* @param defaultFileTypeMap the default FileTypeMap, or {@code null} if none
*/
public SmartMimeMessage(
Session session, @Nullable String defaultEncoding, @Nullable FileTypeMap defaultFileTypeMap) {
super(session);
this.defaultEncoding = defaultEncoding;
this.defaultFileTypeMap = defaultFileTypeMap;
}
/**
* Return the default encoding of this message, or {@code null} if none.
*/
@Nullable
public final String getDefaultEncoding() {
return this.defaultEncoding;
}
/**
* Return the default FileTypeMap of this message, or {@code null} if none.
*/
@Nullable
public final FileTypeMap getDefaultFileTypeMap() {
return this.defaultFileTypeMap;
}
}

View File

@ -0,0 +1,326 @@
################################################################################
# Copyright 2002-2019 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################
################################################################################
#
# Defaults for the Java Activation Framework (revised).
#
# Modified extensions registered in this file:
# text/plain java c c++ cpp pl cc h
# image/png png
# image/svg+xml svg
#
################################################################################
text/html html htm HTML HTM
text/plain txt text TXT TEXT java c c++ cpp pl cc h
image/gif gif GIF
image/ief ief
image/jpeg jpeg jpg jpe JPG
image/tiff tiff tif
image/x-xwindowdump xwd
application/postscript ai eps ps
application/rtf rtf
application/x-tex tex
application/x-texinfo texinfo texi
application/x-troff t tr roff
audio/basic au
audio/midi midi mid
audio/x-aifc aifc
audio/x-aiff aif aiff
audio/x-mpeg mpeg mpg
audio/x-wav wav
video/mpeg mpeg mpg mpe
video/quicktime qt mov
video/x-msvideo avi
################################################################################
#
# Additional file types adapted from
# http://sites.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
# kindly re-licensed to Apache Software License 2.0 by Ian Graham.
#
################################################################################
# TEXT TYPES
text/x-speech talk
text/css css
text/csv csv
# IMAGE TYPES
# X-Windows bitmap (b/w)
image/x-xbitmap xbm
# X-Windows pixelmap (8-bit color)
image/x-xpixmap xpm
# Portable Network Graphics
image/png png
# Scalable Vector Graphics
image/svg+xml svg
# Image Exchange Format (RFC 1314)
image/ief ief
# RGB
image/rgb rgb
# Group III Fax (RFC 1494)
image/g3fax g3f
# X Windowdump format
image/x-xwindowdump xwd
# Macintosh PICT format
image/x-pict pict
# PPM (UNIX PPM package)
image/x-portable-pixmap ppm
# PGM (UNIX PPM package)
image/x-portable-graymap pgm
# PBM (UNIX PPM package)
image/x-portable-bitmap pbm
# PNM (UNIX PPM package)
image/x-portable-anymap pnm
# Microsoft Windows bitmap
image/x-ms-bmp bmp
# CMU raster
image/x-cmu-raster ras
# Kodak Photo-CD
image/x-photo-cd pcd
# Computer Graphics Metafile
image/cgm cgm
# CALS Type 1 or 2
image/x-cals mil cal
# Fractal Image Format (Iterated Systems)
image/fif fif
# QuickSilver active image (Micrografx)
image/x-mgx-dsf dsf
# CMX vector image (Corel)
image/x-cmx cmx
# Wavelet-compressed (Summus)
image/wavelet wi
# AutoCad Drawing (SoftSource)
image/vnd.dwg dwg
# AutoCad DXF file (SoftSource)
image/vnd.dxf dxf
# Simple Vector Format (SoftSource)
image/vnd.svf svf
# AUDIO/VOICE/MUSIC RELATED TYPES
# """basic""audio - 8-bit u-law PCM"
audio/basic au snd
# Macintosh audio format (AIpple)
audio/x-aiff aif aiff aifc
# Microsoft audio
audio/x-wav wav
# MPEG audio
audio/x-mpeg mpa abs mpega
# MPEG-2 audio
audio/x-mpeg-2 mp2a mpa2
# compressed speech (Echo Speech Corp.)
audio/echospeech es
# Toolvox speech audio (Voxware)
audio/voxware vox
# RapidTransit compressed audio (Fast Man)
application/fastman lcc
# Realaudio (Progressive Networks)
application/x-pn-realaudio ra ram
# MIDI music data
x-music/x-midi mmid
# Koan music data (SSeyo)
application/vnd.koan skp
# Speech synthesis data (MVP Solutions)
text/x-speech talk
# VIDEO TYPES
# MPEG video
video/mpeg mpeg mpg mpe
# MPEG-2 video
video/mpeg-2 mpv2 mp2v
# Macintosh Quicktime
video/quicktime qt mov
# Microsoft video
video/x-msvideo avi
# SGI Movie format
video/x-sgi-movie movie
# VDOlive streaming video (VDOnet)
video/vdo vdo
# Vivo streaming video (Vivo software)
video/vnd.vivo viv
# SPECIAL HTTP/WEB APPLICATION TYPES
# Proxy autoconfiguration (Netscape browsers)
application/x-ns-proxy-autoconfig pac
# Netscape Cooltalk chat data (Netscape)
x-conference/x-cooltalk ice
# TEXT-RELATED
# PostScript
application/postscript ai eps ps
# Microsoft Rich Text Format
application/rtf rtf
# Adobe Acrobat PDF
application/pdf pdf
# Maker Interchange Format (FrameMaker)
application/vnd.mif mif
# Troff document
application/x-troff t tr roff
# Troff document with MAN macros
application/x-troff-man man
# Troff document with ME macros
application/x-troff-me me
# Troff document with MS macros
application/x-troff-ms ms
# LaTeX document
application/x-latex latex
# Tex/LateX document
application/x-tex tex
# GNU TexInfo document
application/x-texinfo texinfo texi
# TeX dvi format
application/x-dvi dvi
# MS word document
application/msword doc DOC
# Office Document Architecture
application/oda oda
# Envoy Document
application/envoy evy
# ARCHIVE/COMPRESSED ARCHIVES
# Gnu tar format
application/x-gtar gtar
# 4.3BSD tar format
application/x-tar tar
# POSIX tar format
application/x-ustar ustar
# Old CPIO format
application/x-bcpio bcpio
# POSIX CPIO format
application/x-cpio cpio
# UNIX sh shell archive
application/x-shar shar
# DOS/PC - Pkzipped archive
application/zip zip
# Macintosh Binhexed archive
application/mac-binhex40 hqx
# Macintosh Stuffit Archive
application/x-stuffit sit sea
# Fractal Image Format
application/fractals fif
# "Binary UUencoded"
application/octet-stream bin uu
# PC executable
application/octet-stream exe
# "WAIS ""sources"""
application/x-wais-source src wsrc
# NCSA HDF data format
application/hdf hdf
# DOWNLOADABLE PROGRAM/SCRIPTS
# Javascript program
text/javascript js ls mocha
# UNIX bourne shell program
application/x-sh sh
# UNIX c-shell program
application/x-csh csh
# Perl program
application/x-perl pl
# Tcl (Tool Control Language) program
application/x-tcl tcl
# ANIMATION/MULTIMEDIA
# FutureSplash vector animation (FutureWave)
application/futuresplash spl
# mBED multimedia data (mBED)
application/mbedlet mbd
# PowerMedia multimedia (RadMedia)
application/x-rad-powermedia rad
# PRESENTATION
# PowerPoint presentation (Microsoft)
application/mspowerpoint ppz
# ASAP WordPower (Software Publishing Corp.)
application/x-asap asp
# Astound Web Player multimedia data (GoldDisk)
application/astound asn
# SPECIAL EMBEDDED OBJECT
# OLE script e.g. Visual Basic (Ncompass)
application/x-olescript axs
# OLE Object (Microsoft/NCompass)
application/x-oleobject ods
# OpenScape OLE/OCX objects (Business@Web)
x-form/x-openscape opp
# Visual Basic objects (Amara)
application/x-webbasic wba
# Specialized data entry forms (Alpha Software)
application/x-alpha-form frm
# client-server objects (Wayfarer Communications)
x-script/x-wfxclient wfx
# GENERAL APPLICATIONS
# Undefined binary data (often executable progs)
application/octet-stream exe com
# Pointcast news data (Pointcast)
application/x-pcn pcn
# Excel spreadsheet (Microsoft)
application/vnd.ms-excel xls
# PowerPoint (Microsoft)
application/vnd.ms-powerpoint ppt
# Microsoft Project (Microsoft)
application/vnd.ms-project mpp
# SourceView document (Dataware Electronics)
application/vnd.svd svd
# Net Install - software install (20/20 Software)
application/x-net-install ins
# Carbon Copy - remote control/access (Microcom)
application/ccv ccv
# Spreadsheets (Visual Components)
workbook/formulaone vts
# 2D/3D DATA/VIRTUAL REALITY TYPES
# VRML data file
x-world/x-vrml wrl vrml
# WIRL - VRML data (VREAM)
x-world/x-vream vrw
# Play3D 3d scene data (Play3D)
application/x-p3d p3d
# Viscape Interactive 3d world data (Superscape)
x-world/x-svr svr
# WebActive 3d data (Plastic Thought)
x-world/x-wvr wvr
# QuickDraw3D scene data (Apple)
x-world/x-3dmf 3dmf
# SCIENTIFIC/MATH/CAD TYPES
# Mathematica notebook
application/mathematica ma
# Computational meshes for numerical simulations
x-model/x-mesh msh
# Vis5D 5-dimensional data
application/vis5d v5d
# IGES models -- CAD/CAM (CGM) data
application/iges igs
# Autocad WHIP vector drawings
drawing/x-dwf dwf

View File

@ -0,0 +1,11 @@
/**
* JavaMail support for Spring's mail infrastructure.
* Provides an extended JavaMailSender interface and a MimeMessageHelper
* class for convenient population of a JavaMail MimeMessage.
*/
@NonNullApi
@NonNullFields
package com.simaek.notify.email.embedded.mail.javamail;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

View File

@ -0,0 +1,10 @@
/**
* Spring's generic mail infrastructure.
* Concrete implementations are provided in the subpackages.
*/
@NonNullApi
@NonNullFields
package com.simaek.notify.email.embedded.mail;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;