AbstractSendGridBuilder.java
package fr.sii.ogham.email.sendgrid.builder;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import javax.activation.MimetypesFileTypeMap;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.CloseableHttpClient;
import com.sendgrid.SendGrid;
import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
import fr.sii.ogham.core.builder.configurer.Configurer;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.builder.mimetype.MimetypeDetectionBuilder;
import fr.sii.ogham.core.builder.mimetype.MimetypeDetectionBuilderDelegate;
import fr.sii.ogham.core.builder.mimetype.SimpleMimetypeDetectionBuilder;
import fr.sii.ogham.core.fluent.AbstractParent;
import fr.sii.ogham.email.sendgrid.sender.SendGridSender;
@SuppressWarnings("squid:S00119")
public abstract class AbstractSendGridBuilder<MYSELF extends AbstractSendGridBuilder<MYSELF, EmailBuilder>, EmailBuilder> extends AbstractParent<EmailBuilder> implements Builder<SendGridSender> {
protected final MYSELF myself;
protected final BuildContext buildContext;
protected MimetypeDetectionBuilder<MYSELF> mimetypeBuilder;
protected final ConfigurationValueBuilderHelper<MYSELF, String> apiKeyValueBuilder;
protected final ConfigurationValueBuilderHelper<MYSELF, URL> urlValueBuilder;
protected CloseableHttpClient httpClient;
@SuppressWarnings("unchecked")
protected AbstractSendGridBuilder(Class<?> selfType, EmailBuilder parent, BuildContext buildContext, MimetypeDetectionBuilder<?> mimetypeBuilder) {
super(parent);
myself = (MYSELF) selfType.cast(this);
this.buildContext = buildContext;
apiKeyValueBuilder = buildContext.newConfigurationValueBuilder(myself, String.class);
urlValueBuilder = buildContext.newConfigurationValueBuilder(myself, URL.class);
if (mimetypeBuilder != null) {
mimetype(mimetypeBuilder);
}
}
public AbstractSendGridBuilder(Class<?> selfType, EmailBuilder parent, BuildContext buildContext) {
this(selfType, parent, buildContext, null);
mimetype();
}
/**
* Set SendGrid <a href=
* "https://sendgrid.com/docs/Classroom/Send/How_Emails_Are_Sent/api_keys.html">API
* key</a>.
*
* <p>
* The value set using this method takes precedence over any property and
* default value configured using {@link #apiKey()}.
*
* <pre>
* .apiKey("my-key")
* .apiKey()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-key")
* </pre>
*
* <pre>
* .apiKey("my-key")
* .apiKey()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-key")
* </pre>
*
* In both cases, {@code apiKey("my-key")} is used.
*
* <p>
* If this method is called several times, only the last value is used.
*
* <p>
* If {@code null} value is set, it is like not setting a value at all. The
* property/default value configuration is applied.
*
* @param apiKey
* the API key to use
* @return this instance for fluent chaining
*/
public MYSELF apiKey(String apiKey) {
apiKeyValueBuilder.setValue(apiKey);
return myself;
}
/**
* Set SendGrid <a href=
* "https://sendgrid.com/docs/Classroom/Send/How_Emails_Are_Sent/api_keys.html">API
* key</a>.
*
* <p>
* This method is mainly used by {@link Configurer}s to register some
* property keys and/or a default value. The aim is to let developer be able
* to externalize its configuration (using system properties, configuration
* file or anything else). If the developer doesn't configure any value for
* the registered properties, the default value is used (if set).
*
* <pre>
* .apiKey()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-key")
* </pre>
*
* <p>
* Non-null value set using {@link #apiKey(String)} takes precedence over
* property values and default value.
*
* <pre>
* .apiKey("my-key")
* .apiKey()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-key")
* </pre>
*
* The value {@code "my-key"} is used regardless of the value of the
* properties and default value.
*
* <p>
* See {@link ConfigurationValueBuilder} for more information.
*
*
* @return the builder to configure property keys/default value
*/
public ConfigurationValueBuilder<MYSELF, String> apiKey() {
return apiKeyValueBuilder;
}
/**
* Set username for SendGrid HTTP API.
*
* <p>
* <strong>WARNING:</strong> SendGrid v4 doesn't use username/password
* anymore. You must use an {@link #apiKey(String)}.
*
* <p>
* The value set using this method takes precedence over any property and
* default value configured using {@link #username()}.
*
* <pre>
* .username("my-username")
* .username()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-username")
* </pre>
*
* <pre>
* .username("my-username")
* .username()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-username")
* </pre>
*
* In both cases, {@code username("my-username")} is used.
*
* <p>
* If this method is called several times, only the last value is used.
*
* <p>
* If {@code null} value is set, it is like not setting a value at all. The
* property/default value configuration is applied.
*
* @param username
* the user name for SendGrid HTTP API
* @return this instance for fluent chaining
*
*/
public abstract MYSELF username(String username);
/**
* Set username for SendGrid HTTP API.
*
* <p>
* <strong>WARNING:</strong> SendGrid v4 doesn't use username/password
* anymore. You must use an {@link #apiKey(String)}.
*
* <p>
* This method is mainly used by {@link Configurer}s to register some
* property keys and/or a default value. The aim is to let developer be able
* to externalize its configuration (using system properties, configuration
* file or anything else). If the developer doesn't configure any value for
* the registered properties, the default value is used (if set).
*
* <pre>
* .username()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-username")
* </pre>
*
* <p>
* Non-null value set using {@link #username(String)} takes precedence over
* property values and default value.
*
* <pre>
* .username("my-username")
* .username()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-username")
* </pre>
*
* The value {@code "my-username"} is used regardless of the value of the
* properties and default value.
*
* <p>
* See {@link ConfigurationValueBuilder} for more information.
*
* @return the builder to configure property keys/default value
*/
public abstract ConfigurationValueBuilder<MYSELF, String> username();
/**
* Set password for SendGrid HTTP API.
*
* <p>
* <strong>WARNING:</strong> SendGrid v4 doesn't use username/password
* anymore. You must use an {@link #apiKey(String)}.
*
* <p>
* The value set using this method takes precedence over any property and
* default value configured using {@link #password()}.
*
* <pre>
* .password("my-password")
* .password()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-password")
* </pre>
*
* <pre>
* .password("my-password")
* .password()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-password")
* </pre>
*
* In both cases, {@code password("my-password")} is used.
*
* <p>
* If this method is called several times, only the last value is used.
*
* <p>
* If {@code null} value is set, it is like not setting a value at all. The
* property/default value configuration is applied.
*
* @param password
* the password value
* @return this instance for fluent chaining
*/
public abstract MYSELF password(String password);
/**
* Set password for SendGrid HTTP API.
*
* <p>
* <strong>WARNING:</strong> SendGrid v4 doesn't use username/password
* anymore. You must use an {@link #apiKey(String)}.
*
* <p>
* This method is mainly used by {@link Configurer}s to register some
* property keys and/or a default value. The aim is to let developer be able
* to externalize its configuration (using system properties, configuration
* file or anything else). If the developer doesn't configure any value for
* the registered properties, the default value is used (if set).
*
* <pre>
* .password()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-password")
* </pre>
*
* <p>
* Non-null value set using {@link #password(String)} takes precedence over
* property values and default value.
*
* <pre>
* .password("my-password")
* .password()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("default-password")
* </pre>
*
* The value {@code "my-password"} is used regardless of the value of the
* properties and default value.
*
* <p>
* See {@link ConfigurationValueBuilder} for more information.
*
* @return the builder to configure property keys/default value
*/
public abstract ConfigurationValueBuilder<MYSELF, String> password();
/**
* Set SendGrid API base URL.
*
* <p>
* The value set using this method takes precedence over any property and
* default value configured using {@link #url()}.
*
* <pre>
* .url("http://localhost/sendgrid")
* .url()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("http://api.sendgrid.com")
* </pre>
*
* <pre>
* .url("http://localhost/sendgrid")
* .url()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("http://api.sendgrid.com")
* </pre>
*
* In both cases, {@code url("http://localhost/sendgrid")} is used.
*
* <p>
* If this method is called several times, only the last value is used.
*
* <p>
* If {@code null} value is set, it is like not setting a value at all. The
* property/default value configuration is applied.
*
* @param url
* the base URL for SendGrid HTTP API
* @return this instance for fluent chaining
*/
public MYSELF url(URL url) {
urlValueBuilder.setValue(url);
return myself;
}
/**
* Set SendGrid API base URL.
*
* <p>
* The value set using this method takes precedence over any property and
* default value configured using {@link #url()}.
*
* <pre>
* .url("http://localhost/sendgrid")
* .url()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("http://api.sendgrid.com")
* </pre>
*
* <pre>
* .url("http://localhost/sendgrid")
* .url()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("http://api.sendgrid.com")
* </pre>
*
* In both cases, {@code url("http://localhost/sendgrid")} is used.
*
* <p>
* If this method is called several times, only the last value is used.
*
* <p>
* If {@code null} value is set, it is like not setting a value at all. The
* property/default value configuration is applied.
*
* @param url
* the base URL for SendGrid HTTP API
* @return this instance for fluent chaining
* @throws IllegalArgumentException
* if URL is malformed
*/
public MYSELF url(String url) {
try {
return url(new URL(url));
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid URL " + url, e);
}
}
/**
* Set SendGrid API base URL.
*
* <p>
* This method is mainly used by {@link Configurer}s to register some
* property keys and/or a default value. The aim is to let developer be able
* to externalize its configuration (using system properties, configuration
* file or anything else). If the developer doesn't configure any value for
* the registered properties, the default value is used (if set).
*
* <pre>
* .url()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("http://api.sendgrid.com")
* </pre>
*
* <p>
* Non-null value set using {@link #url(URL)} takes precedence over property
* values and default value.
*
* <pre>
* .url("http://localhost/sendgrid")
* .url()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue("http://api.sendgrid.com")
* </pre>
*
* The value {@code "http://localhost/sendgrid"} is used regardless of the
* value of the properties and default value.
*
* <p>
* See {@link ConfigurationValueBuilder} for more information.
*
*
* @return the builder to configure property keys/default value
*/
public ConfigurationValueBuilder<MYSELF, URL> url() {
return urlValueBuilder;
}
/**
* By default, calling SendGrid HTTP API is done through the default
* {@link SendGrid} implementation that uses default {@link HttpClient}
* (calling {@code HttpClientBuilder.create().build()}). If you want to use
* another HTTP client implementation, you can extend the
* {@link CloseableHttpClient} class and provide it:
*
* <pre>
* .client(new MyCustomHttpClient())
* </pre>
*
* @param httpClient
* the custom implementation of {@link HttpClient} used to call
* SendGrid HTTP API. SendGrid requires a
* {@link CloseableHttpClient}.
* @return this instance for fluent chaining
*/
public MYSELF httpClient(CloseableHttpClient httpClient) {
this.httpClient = httpClient;
return myself;
}
/**
* Builder that configures mimetype detection.
*
* There exists several implementations to provide the mimetype:
* <ul>
* <li>Using Java {@link MimetypesFileTypeMap}</li>
* <li>Using Java 7 {@link Files#probeContentType(java.nio.file.Path)}</li>
* <li>Using <a href="http://tika.apache.org/">Apache Tika</a></li>
* <li>Using
* <a href="https://github.com/arimus/jmimemagic">JMimeMagic</a></li>
* </ul>
*
* <p>
* Both implementations provided by Java are based on file extensions. This
* can't be used in most cases as we often handle {@link InputStream}s.
* </p>
*
* <p>
* In previous version of Ogham, JMimeMagic was used and was working quite
* well. Unfortunately, the library is no more maintained.
* </p>
*
* <p>
* You can configure how Tika will detect mimetype:
*
* <pre>
* .mimetype()
* .tika()
* ...
* </pre>
*
* <p>
* This builder allows to use several providers. It will chain them until
* one can find a valid mimetype. If none is found, you can explicitly
* provide the default one:
*
* <pre>
* .mimetype()
* .defaultMimetype("text/html")
* </pre>
*
* <p>
* If no mimetype detector was previously defined, it creates a new one.
* Then each time you call {@link #mimetype()}, the same instance is used.
* </p>
*
* @return the builder to configure mimetype detection
*/
public MimetypeDetectionBuilder<MYSELF> mimetype() {
if (mimetypeBuilder == null) {
mimetypeBuilder = new SimpleMimetypeDetectionBuilder<>(myself, buildContext);
}
return mimetypeBuilder;
}
/**
* NOTE: this is mostly for advance usage (when creating a custom module).
*
* Inherits mimetype configuration from another builder. This is useful for
* configuring independently different parts of Ogham but keeping a whole
* coherence.
*
* The same instance is shared meaning that all changes done here will also
* impact the other builder.
*
* <p>
* If a previous builder was defined (by calling {@link #mimetype()} for
* example), the new builder will override it.
*
* @param builder
* the builder to inherit
* @return this instance for fluent chaining
*/
public MYSELF mimetype(MimetypeDetectionBuilder<?> builder) {
mimetypeBuilder = new MimetypeDetectionBuilderDelegate<>(myself, builder);
return myself;
}
}