All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.sii.ogham.email.builder.EmailBuilder Maven / Gradle / Ivy

package fr.sii.ogham.email.builder;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.ContentTranslatorBuilder;
import fr.sii.ogham.core.builder.MessageFillerBuilder;
import fr.sii.ogham.core.builder.MessagingSenderBuilder;
import fr.sii.ogham.core.builder.TemplateBuilder;
import fr.sii.ogham.core.condition.AndCondition;
import fr.sii.ogham.core.condition.Condition;
import fr.sii.ogham.core.condition.OrCondition;
import fr.sii.ogham.core.condition.RequiredClassCondition;
import fr.sii.ogham.core.condition.RequiredPropertyCondition;
import fr.sii.ogham.core.exception.builder.BuildException;
import fr.sii.ogham.core.filler.MessageFiller;
import fr.sii.ogham.core.filler.SubjectFiller;
import fr.sii.ogham.core.message.Message;
import fr.sii.ogham.core.sender.ConditionalSender;
import fr.sii.ogham.core.sender.ContentTranslatorSender;
import fr.sii.ogham.core.sender.FillerSender;
import fr.sii.ogham.core.sender.MessageSender;
import fr.sii.ogham.core.sender.MultiImplementationSender;
import fr.sii.ogham.core.translator.content.ContentTranslator;
import fr.sii.ogham.core.translator.resource.AttachmentResourceTranslator;
import fr.sii.ogham.core.util.BuilderUtils;
import fr.sii.ogham.email.EmailConstants;
import fr.sii.ogham.email.EmailConstants.SendGridConstants;
import fr.sii.ogham.email.sender.AttachmentResourceTranslatorSender;
import fr.sii.ogham.email.sender.EmailSender;
import fr.sii.ogham.template.TemplateConstants;

/**
 * 

* Specialized builder for email sender. *

* There exists several implementations to send an email: * *

* This builder provides a {@link MultiImplementationSender}. The aim of the * {@link MultiImplementationSender} is to choose the best implementation for * sending the email according to the runtime environment (detection of * libraries in the classpath, availability of a particular property, ...). *

*

* This builder lets you the possibility to register any new implementation. It * allows you to enable or not templating support and automatic filling of * message values (like sender address for example). *

* * @author Aurélien Baudet * @see EmailSender * @see JavaMailBuilder * @see SendGridBuilder * @see TemplateBuilder * @see AttachmentResourceTranslatorBuilder * @see ContentTranslatorBuilder * @see MessageFillerBuilder */ public class EmailBuilder implements MessagingSenderBuilder { private static final Logger LOG = LoggerFactory.getLogger(EmailBuilder.class); /** * The sender instance constructed by this builder */ private ConditionalSender sender; /** * The specialized {@link MultiImplementationSender}. It is useful to * register new implementations */ private EmailSender emailSender; /** * The builder for message filler used to add values to the message */ private MessageFillerBuilder messageFillerBuilder; /** * The builder for the translator that will update the content of the * message */ private ContentTranslatorBuilder contentTranslatorBuilder; /** * The builder for the resource translator to handle email attachments */ private AttachmentResourceTranslatorBuilder resourceTranslatorBuilder; /** * Map that stores email implementations indexed by associated condition */ private Map, Builder> implementations; /** * Own property key for template resolution prefix */ private String templatePrefixKey; /** * Own property key for template resolution prefix */ private String templateSuffixKey; public EmailBuilder() { super(); emailSender = new EmailSender(); sender = emailSender; implementations = new HashMap<>(); } @Override public ConditionalSender build() throws BuildException { for (Entry, Builder> impl : implementations.entrySet()) { MessageSender s = impl.getValue().build(); LOG.debug("Implementation {} registered", s); emailSender.addImplementation(impl.getKey(), s); } if (messageFillerBuilder != null) { MessageFiller messageFiller = messageFillerBuilder.build(); LOG.debug("Automatic filling of message enabled {}", messageFiller); sender = new FillerSender(messageFiller, sender); } if (resourceTranslatorBuilder != null) { AttachmentResourceTranslator resourceTranslator = resourceTranslatorBuilder.build(); LOG.debug("Resource translation enabled {}", resourceTranslator); sender = new AttachmentResourceTranslatorSender(resourceTranslator, sender); } if (contentTranslatorBuilder != null) { if (templatePrefixKey != null) { LOG.debug("Use custom property key {} for prefix template resolution", templatePrefixKey); getTemplateBuilder().setPrefixKey(templatePrefixKey); } if (templateSuffixKey != null) { LOG.debug("Use custom property key {} for suffix template resolution", templateSuffixKey); getTemplateBuilder().setSuffixKey(templateSuffixKey); } ContentTranslator contentTranslator = contentTranslatorBuilder.build(); LOG.debug("Content translation enabled {}", contentTranslator); sender = new ContentTranslatorSender(contentTranslator, sender); } return sender; } /** * Tells the builder to use all default behaviors and values: *
    *
  • Uses Java mail default behaviors and values
  • *
  • Registers Java mail API implementation
  • *
  • Enables automatic filling of message based on configuration * properties
  • *
  • Enables templating support
  • *
  • Enables attachment features (see {@link #withAttachmentFeatures()})
  • *
*

* Configuration values come from system properties. *

* * @return this instance for fluent use */ public EmailBuilder useDefaults() { return useDefaults(BuilderUtils.getDefaultProperties()); } /** * Tells the builder to use all default behaviors and values: *
    *
  • Uses Java mail default behaviors and values
  • *
  • Registers Java mail API implementation
  • *
  • Enables automatic filling of message based on configuration * properties
  • *
  • Enables templating support
  • *
  • Enables attachment features (see {@link #withAttachmentFeatures()})
  • *
*

* Configuration values come from provided properties. *

* * @param properties * the properties to use instead of default ones * @return this instance for fluent use */ public EmailBuilder useDefaults(Properties properties) { registerDefaultImplementations(properties); withAutoFilling(properties); withTemplate(properties); enableEmailTemplateKeys(); withAttachmentFeatures(); return this; } /** * Register a new implementation for sending email. The implementation is * associated to a condition. If the condition evaluation returns true at * runtime then it means that the implementation can be used. If several * implementations are available, only the first implementation is really * invoked. * * @param condition * the condition that indicates at runtime if the implementation * can be used or not * @param implementation * the implementation to register * @return this instance for fluent use */ public EmailBuilder registerImplementation(Condition condition, MessageSender implementation) { emailSender.addImplementation(condition, implementation); return this; } /** * Register a new implementation for sending email. The implementation is * associated to a condition. If the condition evaluation returns true at * runtime then it means that the implementation can be used. If several * implementations are available, only the first implementation is really * invoked. * * @param condition * the condition that indicates at runtime if the implementation * can be used or not * @param builder * the builder for the implementation to register * @return this instance for fluent use */ public EmailBuilder registerImplementation(Condition condition, Builder builder) { implementations.put(condition, builder); return this; } /** * Register all default implementations: *
    *
  • Java mail API implementation
  • *
*

* Configuration values come from system properties. *

*

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @return this instance for fluent use */ public EmailBuilder registerDefaultImplementations() { return registerDefaultImplementations(BuilderUtils.getDefaultProperties()); } /** * Register all default implementations: *
    *
  • Java mail API implementation
  • *
*

* Configuration values come from provided properties. *

*

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @param properties * the properties to use * @return this instance for fluent use */ public EmailBuilder registerDefaultImplementations(Properties properties) { withJavaMail(properties); withSendGrid(properties); return this; } /** * Enable Java Mail API implementation. This implementation is used only if * the associated condition indicates that Java Mail API can be used. The * condition checks if: *
    *
  • The property mail.smtp.host is set
  • *
  • The class javax.mail.Transport (Java Mail API) is * available in the classpath
  • *
  • The class com.sun.mail.smtp.SMTPTransport (Java Mail * implementation) is available in the classpath
  • *
* The registration can silently fail if the javax.mail jar is not in the * classpath. In this case, the Java Mail API is not registered at all. * * @param properties * the properties used to check if property exists * @return this builder instance for fluent use */ public EmailBuilder withJavaMail(Properties properties) { // Java Mail API can be used only if the property "mail.smtp.host" is // provided and also if the class "javax.mail.Transport" is defined in // the classpath. The try/catch clause is mandatory in order to prevent // failure when javax.mail jar is not in the classpath try { // @formatter:off registerImplementation(new AndCondition<>( new OrCondition<>( new RequiredPropertyCondition("mail.smtp.host", properties), new RequiredPropertyCondition("mail.host", properties)), new RequiredClassCondition("javax.mail.Transport"), new RequiredClassCondition("com.sun.mail.smtp.SMTPTransport")), new JavaMailBuilder().useDefaults(properties)); // @formatter:on } catch (Throwable e) { LOG.debug("Can't register Java Mail implementation", e); } return this; } /** * Enable SendGrid implementation. This implementation is used only if the * associated condition indicates that Java Mail API can be used. The * condition checks if: *
    *
  • The property ogham.email.sendgrid.api.key is set
  • *
  • The property ogham.email.sendgrid.username and * ogham.email.sendgrid.password is set
  • *
  • The class com.sendgrid.SendGrid is available in the * classpath
  • *
* The registration can silently fail if the javax.mail jar is not in the * classpath. In this case, the SendGrid is not registered at all. * * @param properties * the properties used to check if property exists * @return this builder instance for fluent use */ public EmailBuilder withSendGrid(Properties properties) { // SendGrid can be used only if the property "sendgrid.api.key" is // provided and also if the class "com.sendgrid.SendGrid" is defined in // the classpath. The try/catch clause is mandatory in order to prevent // failure when sendgrid jar is not in the classpath try { // @formatter:off registerImplementation(new AndCondition<>( new OrCondition<>( new RequiredPropertyCondition(SendGridConstants.API_KEY, properties), new AndCondition<>( new RequiredPropertyCondition(SendGridConstants.USERNAME, properties), new RequiredPropertyCondition(SendGridConstants.PASSWORD, properties))), new RequiredClassCondition("com.sendgrid.SendGrid")), new SendGridBuilder().useDefaults(properties)); // @formatter:on } catch (Throwable e) { LOG.debug("Can't register SendGrid implementation", e); } return this; } /** * Enables automatic filling of emails with values that come from multiple * sources. It let you use your own builder instead of using default * behaviors. * * @param builder * the builder for constructing the message filler * @return this instance for fluent use */ public EmailBuilder withAutoFilling(MessageFillerBuilder builder) { messageFillerBuilder = builder; return this; } /** * Enables automatic filling of emails with values that come from multiple * sources: *
    *
  • Fill email with values that come from provided configuration * properties.
  • *
  • Generate subject for the email (see {@link SubjectFiller})
  • *
* See {@link MessageFillerBuilder#useDefaults(Properties, String...)} for * more information. *

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @param props * the properties that contains the values to set on the email * @param baseKeys * the prefix(es) for the keys used for filling the message * @return this instance for fluent use */ public EmailBuilder withAutoFilling(Properties props, String... baseKeys) { withAutoFilling(new MessageFillerBuilder().useDefaults(props, baseKeys)); return this; } /** * Enables automatic filling of emails with values that come from multiple * sources: *
    *
  • Fill email with values that come from provided configuration * properties. It uses the default prefix for the keys ("mail" and * "ogham.email").
  • *
  • Generate subject for the email (see {@link SubjectFiller})
  • *
*

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @param props * the properties that contains the values to set on the email * @return this instance for fluent use */ public EmailBuilder withAutoFilling(Properties props) { return withAutoFilling(props, EmailConstants.FILL_PREFIXES); } /** * Enables automatic filling of emails with values that come from multiple * sources: *
    *
  • Fill email with values that come from system configuration * properties. It uses the default prefix for the keys ("ogham.email").
  • *
  • Generate subject for the email (see {@link SubjectFiller})
  • *
*

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @return this instance for fluent use */ public EmailBuilder withAutoFilling() { return withAutoFilling(BuilderUtils.getDefaultProperties()); } /** * Enables templating support using all default behaviors and values. See * {@link ContentTranslatorBuilder#useDefaults()} for more information. * *

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @return this instance for fluent use */ public EmailBuilder withTemplate() { return withTemplate(BuilderUtils.getDefaultProperties()); } /** * Enables templating support using all default behaviors and values. See * {@link ContentTranslatorBuilder#useDefaults()} for more information. * *

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @param properties * the properties to use * @return this instance for fluent use */ public EmailBuilder withTemplate(Properties properties) { return withTemplate(new ContentTranslatorBuilder().useDefaults(properties)); } /** * Enables templating support using the provided * {@link ContentTranslatorBuilder}. It decorates the email sender with a * {@link ContentTranslatorSender}. * * @param builder * the builder to use for constructing the * {@link ContentTranslator} instead of using the default one * @return this instance for fluent use */ public EmailBuilder withTemplate(ContentTranslatorBuilder builder) { this.contentTranslatorBuilder = builder; return this; } /** *

* Calling this method will enable different location for email templates * from default one. The location will be specified by different property * keys for prefix and suffix. *

* * By default default properties are: *
    *
  • ogham.template.prefix (see {@link TemplateConstants#PREFIX_PROPERTY}) *
  • *
  • ogham.template.suffix (see {@link TemplateConstants#SUFFIX_PROPERTY}
  • *
* * Calling this method will change the property keys to: *
    *
  • ogham.email.template.prefix (see * {@link fr.sii.ogham.email.EmailConstants.TemplateConstants#PREFIX_PROPERTY} *
  • *
  • ogham.email.template.suffix (see * {@link fr.sii.ogham.email.EmailConstants.TemplateConstants#SUFFIX_PROPERTY} *
  • *
* * @return this instance for fluent use */ public EmailBuilder enableEmailTemplateKeys() { setTemplatePrefixKey(EmailConstants.TemplateConstants.PREFIX_PROPERTY); setTemplateSuffixKey(EmailConstants.TemplateConstants.SUFFIX_PROPERTY); return this; } /** *

* Calling this method will enable different location for email templates * from default one. The location will be specified by a different property * key for prefix. *

* *

* By default default property key is ogham.template.prefix (see * {@link TemplateConstants#PREFIX_PROPERTY}) *

* *

* Calling this method will change the property key to the provided key. *

* * @param prefixKey * the new key for the email template prefix * @return this instance for fluent use */ public EmailBuilder setTemplatePrefixKey(String prefixKey) { this.templatePrefixKey = prefixKey; return this; } /** *

* Calling this method will enable different location for email templates * from default one. The location will be specified by a different property * key for suffix. *

* *

* By default default property key is ogham.template.prefix (see * {@link TemplateConstants#SUFFIX_PROPERTY}) *

* *

* Calling this method will change the property key to the provided key. *

* * @param suffixKey * the new key for the email template suffix * @return this instance for fluent use */ public EmailBuilder setTemplateSuffixKey(String suffixKey) { this.templateSuffixKey = suffixKey; return this; } /** * Enable attachment features like attachment resolution based on lookup * mapping. It delegates to {@link AttachmentResourceTranslatorBuilder} with * the default behavior and values (see * {@link AttachmentResourceTranslatorBuilder#useDefaults()}). * *

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @return this instance for fluent use */ public EmailBuilder withAttachmentFeatures() { return withAttachmentFeatures(new AttachmentResourceTranslatorBuilder().useDefaults()); } /** * Enable attachment features using the provided translator builder. * *

* Automatically called by {@link #useDefaults()} and * {@link #useDefaults(Properties)} *

* * @param builder * the builder for the translator to use instead of the default * one * @return this instance for fluent use */ public EmailBuilder withAttachmentFeatures(AttachmentResourceTranslatorBuilder builder) { resourceTranslatorBuilder = builder; return this; } /** *

* Get reference to the specialized builder. It may be useful to fine tune a * specific implementation. *

*

* There also exists shortcuts: {@link #getJavaMailBuilder()} and * {@link #getSendGridBuilder()}. *

* * @param clazz * the class of the builder to get * @param * the type of the class to get * @return the builder instance for the specific implementation * @throws IllegalArgumentException * when provided class references an nonexistent builder */ @SuppressWarnings("unchecked") public > B getImplementationBuilder(Class clazz) { for (Builder builder : implementations.values()) { if (clazz.isAssignableFrom(builder.getClass())) { return (B) builder; } } throw new IllegalArgumentException("No implementation builder exists for " + clazz.getSimpleName()); } /** *

* Get the reference to the specialized builder for Java Mail API. It may be * useful to fine tune Java Mail API implementation. *

* * Access this builder if you want to: *
    *
  • Customize content handler management
  • *
  • Customize resource attachment handler management
  • *
  • Customize Mimetype detection
  • *
  • Use custom Authenticator
  • *
  • Use custom interceptor
  • *
* * @return The specialized builder for Java Mail API */ public JavaMailBuilder getJavaMailBuilder() { return getImplementationBuilder(JavaMailBuilder.class); } /** *

* Get the reference to the specialized builder for SendGrid. It may be * useful to fine tune SendGrid implementation. *

* * Access this builder if you want to: *
    *
  • Customize content handler management
  • *
  • Customize Mimetype detection
  • *
  • Customize username/password/API key
  • *
  • Provide your own SendGrid client implementation
  • *
* * @return The specialized builder for SendGrid */ public SendGridBuilder getSendGridBuilder() { return getImplementationBuilder(SendGridBuilder.class); } /** *

* Get the builder used for filling messages. *

* * Access this builder if you want to: *
    *
  • Enable/disable automatic filling of messages with values provided in * configuration
  • *
  • Enable/disable automatic filling of subject for messages based on * templates
  • *
  • Add your own message filler
  • *
* * @return the builder used for filling messages */ public MessageFillerBuilder getMessageFillerBuilder() { return messageFillerBuilder; } /** *

* Get the builder used transform the content of the message. It may be * useful to fine tune templating mechanism, resource inlining and messages * with with several contents. *

* * Access this builder if you want to: *
    *
  • Customize templating mechanism (see {@link #getTemplateBuilder()})
  • *
  • Enable/disable support for messages with multiple contents
  • *
  • Enable/disable support for inlining of resources
  • *
  • Add your own content translator
  • *
* * @return the builder used to transform the content of the message */ public ContentTranslatorBuilder getContentTranslatorBuilder() { return contentTranslatorBuilder; } /** *

* Shortcut to directly access template builder for fine tuning templating * mechanism. *

* * Access this builder if you want to: *
    *
  • Customize how template resources are resolved
  • *
  • Register a custom lookup mapping resolver for template resources
  • *
  • Use your own template engine
  • *
  • Customize the template engine configuration
  • *
  • Set the prefix and suffix for template resolution
  • *
  • Set the property key for prefix and suffix resolution
  • *
* * @return the template builder */ public TemplateBuilder getTemplateBuilder() { return contentTranslatorBuilder.getTemplateBuilder(); } /** *

* Get the builder used to transform the resources associated to the * message. It may be useful to fine tune how to attach resources to * messages. *

* * Access this builder if you want to: *
    *
  • Customize how attached resources are transformed
  • *
  • Customize how attached resources are resolved for transformation
  • *
  • Register a custom lookup mapping resolver for attached resources
  • *
* * @return the builder used to transform the resources associated to the * message */ public AttachmentResourceTranslatorBuilder getResourceTranslatorBuilder() { return resourceTranslatorBuilder; } }