Awesome
JMail
<a href="https://search.maven.org/artifact/com.sanctionco.jmail/jmail"> <img src="https://img.shields.io/maven-central/v/com.sanctionco.jmail/jmail.svg?colorB=brightgreen&label=maven%20central" alt="Maven Central"> </a> <a href="http://javadoc.io/doc/com.sanctionco.jmail/jmail"> <img src="http://javadoc.io/badge/com.sanctionco.jmail/jmail.svg" alt="Javadoc"> </a> <a href="https://codecov.io/gh/RohanNagar/jmail"> <img src="https://codecov.io/gh/RohanNagar/jmail/branch/master/graph/badge.svg" alt="Coverage Status"> </a>A modern, fast, zero-dependency library for working with email addresses and performing email address validation in Java.
Built for Java 8 and up.
Why JMail? • Installation • Usage • IP Validation • Contributing
Why JMail?
JMail was built mainly because I wanted to tackle the complex problem of email address validation without using Regex. Along the way, JMail became a much better choice than other Java email validation libraries (such as Apache Commons Validator or Jakarta (Javax) Mail Validation) for the following reasons:
-
JMail is more correct than other libraries. For example, both Apache Commons and Jakarta Mail consider
first@last@test.org
as a valid email address! It clearly is not, as it has two@
characters. JMail correctly considers this address invalid. You can see a full comparison of correctness and try it out for yourself online. -
JMail is faster than other libraries by, on average, at least 2x, thanks in part to lack of regex.
-
JMail has zero dependencies and is very lightweight.
-
JMail is modern. It is built for Java 8+, and provides many useful methods and data accessors.
Click here for a full report of the differences in correctness and speed between JMail and other libraries.
While JMail is more correct than other libraries, I cannot guarantee that it is 100% correct. Email RFCs are long and complex, and I have likely missed some specific details. Please open an issue if you find an incorrect validation result for a specific email (or even better, a pull request with a fix).</br></br> I also highly recommend that you send verification emails to user-provided email addresses. This is the only way to ensure that the email address exists and that the recipient wants that email address to be used.
Installation
Add this library as a dependency in your pom.xml
:
<dependency>
<groupId>com.sanctionco.jmail</groupId>
<artifactId>jmail</artifactId>
<version>1.6.2</version>
</dependency>
Or in your build.gradle
:
implementation 'com.sanctionco.jmail:jmail:1.6.2'
Usage
Standard Email Validation
To perform standard email validation, use the static methods
available in JMail
. For example, to test validation:
String email = "test@example.com";
if (JMail.isValid(email)) {
// Work with your email string
}
Or to enforce validation, throwing an InvalidEmailException
on failure:
String email = "test@example.com";
try {
JMail.enforceValid(email);
// Work with your email string
} catch (InvalidEmailException _) {
// Handle invalid email
}
You can also retrieve the reason for validation failure with the validate
method:
String email = "test@example.com"
EmailValidationResult result = JMail.validate(email);
if (result.isSuccess()) {
// Use the email address
} else {
FailureReason reason = result.getFailureReason();
logger.error("Validating email address failed with reason: " + reason);
}
Custom Email Validation
JMail also provides an EmailValidator
class that allows for much more
customization of what constitutes a valid email address. You can require
additional common validation rules,
or supply your own. For example:
// In general, you should use JMail.strictValidator()
EmailValidator validator = JMail.strictValidator()
// Require that the top-level-domain is ".com"
.requireOnlyTopLevelDomains(TopLevelDomain.DOT_COM)
// Require that the local-part starts with "allowed"
.withRule(email -> email.localPart().startsWith("allowed"));
boolean valid = validator.isValid("allowed-email@test.com");
boolean invalidWithoutTld = validator.isValid("allowed@test");
boolean invalidWithoutDotCom = validator.isValid("allowed@test.net");
boolean invalidWithoutAllowed = validator.isValid("invalid@test.com");
The Email
Object
JMail also includes an Email
object that makes working with
email addresses easier. The Email
object has the following properties:
Property getter | Description | Example using test(hello)@(world)example.one.com |
---|---|---|
localPart() | The local-part of the email address | test(hello) |
localPartWithoutComments() | The local-part of the email address without comments | test |
domain() | The domain of the email address | (world)example.one.com |
domainWithoutComments() | The domain of the email address without comments | example.one.com |
domainParts() | A list of the parts of the domain | [example, one, com] |
identifier() | The identifier of the email address, if it has one. | null <br/>(For Admin <test@server.com> , it would be Admin ) |
comments() | A list of the comments in the email address | [hello, world] |
explicitSourceRoutes() | A list of explicit source routes in the address, if present | [] <br/>(For @1st.relay,@2nd.relay:user@final.domain , it would be [1st.relay, 2nd.relay] ) |
isIpAddress() | Whether the domain is an IP address | false |
containsWhitespace() | Whether the address contains obsolete whitespace | false |
isAscii() | Whether the address contains only ASCII characters | true |
hasIdentifier() | Whether the address has an identifier | false |
topLevelDomain() | The TopLevelDomain of the email address, or TopLevelDomain.OTHER if it is unknown | TopLevelDomain.DOT_COM |
To create a new instance of Email
from a string, use the tryParse(String email)
method, either the default version or on your own EmailValidator
instance:
Optional<Email> parsed = JMail.tryParse("test@example.com");
Optional<Email> parsed = JMail.validator()
.disallowIpDomain()
.tryParse("test@example.com");
Since tryParse(String email)
returns an Optional<Email>
, you can do
some cool things, such as:
Use a default email address
String email = JMail.tryParse("invalidEmailString")
.map(Email::toString)
.orElse("default@example.com");
Send an email if the address is valid
JMail.tryParse("test@example.com")
.ifPresentOrElse(
email -> myEmailService.sendTo(email.toString()),
() -> log.error("Could not send email to invalid email"));
Get a normalized version of the email address
// Get a normalized email address without any comments
Optional<String> normalized = JMail.tryParse("admin(comment)@mysite.org")
.map(Email::normalized);
// normalized == Optional.of("admin@mysite.org")
// Get a normalized email address and strip quotes if the address would
// still be valid
Optional<String> normalized = JMail.tryParse("\"test.1\"@mysite.org")
.map(e -> e.normalized(true));
// normalized == Optional.of("test.1@mysite.org")
Note: You can also set the
-Djmail.normalize.strip.quotes=true
JVM property to strip quotes when callingnormalized()
without parameters.
Additional Validation Rules
Disallow IP Address Domain
Although an email with an IP address in the domain is valid,
these email addresses are often rejected from mail servers or only
used for spam. You can require that your EmailValidator
reject all
emails with an IP address in the domain:
JMail.validator().disallowIpDomain();
Note:
JMail.strictValidator()
includes this rule automatically.
Require a Top Level Domain
Although an email address can be a local domain name with no TLD,
ICANN highly discourages dotless email addresses. You can require that
your EmailValidator
reject all emails without a TLD:
JMail.validator().requireTopLevelDomain();
Note:
JMail.strictValidator()
includes this rule automatically.
Disallow Explicit Source Routing
Explicit source routing has been deprecated as of RFC 5321 and you SHOULD NOT use explicit source routing except under unusual circumstances.
JMail.validator().disallowExplicitSourceRouting();
Note:
JMail.strictValidator()
includes this rule automatically.
Disallow Reserved Domains
As specified in RFC 2606,
some domains are reserved and should not be resolvable. Mail addressed to
mailboxes in those reserved domains (and their subdomains) should be non-deliverable.
You can require that your EmailValidator
reject
all emails that have a reserved domain:
JMail.validator().disallowReservedDomains();
Disallow Quoted Identifiers
If you want email addresses to only be the raw email address, use this rule.
Adding this will invalidate addresses of the form John Smith <john@smith.com>
.
JMail.validator().disallowQuotedIdentifiers();
Require a specific common Top Level Domain
You can require that your EmailValidator
reject all emails that have
a top-level domain other than the ones you specify:
JMail.validator().requireOnlyTopLevelDomains(TopLevelDomain.DOT_COM);
JMail.validator().requireOnlyTopLevelDomains(
TopLevelDomain.DOT_NET, TopLevelDomain.DOT_EDU);
Disallow Obsolete Whitespace
Whitespace (spaces, newlines, and carriage returns) is by default allowed between dot-separated parts of the local-part and domain since RFC 822. However, this whitespace is considered obsolete since RFC 2822.
You can require that your EmailValidator
reject all emails that have obsolete whitespace.
JMail.validator().disallowObsoleteWhitespace();
Require a valid MX record
You can require that your EmailValidator
reject all email addresses that do not have a valid MX
record associated with the domain.
Please note that including this rule on your email validator can increase the amount of time it takes to validate email addresses by approximately 600ms in the worst case. To further control the amount of time spent doing DNS lookups, you can use the overloaded method to customize the timeout and retries.
JMail.validator().requireValidMXRecord();
// Or, customize the timeout and retries
JMail.validator().requireValidMXRecord(50, 2);
Require the address to be ASCII
Some older email servers cannot yet accept non-ASCII email addresses. You can
require that your EmailValidator
reject all email addresses that contain characters
other than ASCII characters.
JMail.validator().requireAscii();
Bonus: IP Address Validation
Since validating email addresses requires validation of IP addresses, these IP address validation methods are exposed for your convenience!
Determine if an IP Address is Valid
String ipv4 = "12.34.56.78";
if (InternetProtocolAddress.isValid(ipv4)) {
// Use address
}
String ipv6 = "2001:db8::1234:5678";
if (InternetProtocolAddress.isValid(ipv6)) {
// Use address
}
Enforce an IP Address to be Valid
String ipv4 = "12.34.56.78";
try {
InternetProtocolAddress.enforceValid(ipv4);
} catch (InvalidAddressException e) {
// Failure
}
String ipv6 = "2001:db8::1234:5678";
try {
InternetProtocolAddress.enforceValid(ipv6);
} catch (InvalidAddressException e) {
// Failure
}
Validate and return the IP
String ipv4 = "12.34.56.78";
Optional<String> validated = InternetProtocolAddress.validate(ipv4);
// The validate() method allows for convenience such as:
String ip = InternetProtocolAddress
.validate("notvalid")
.orElse("0.0.0.0");
String ipv6 = "2001:db8::1234:5678";
Optional<String> validated = InternetProtocolAddress.validate(ipv6);
// The validate() method allows for convenience such as:
String ip = InternetProtocolAddress
.validate("notvalid")
.orElse("2001:db8::1234:5678");
Contributing
All contributions are welcome! Open issues for bug reports or feature requests. Pull requests with fixes or enhancements are encouraged.