What are the good email address validation libraries for Java? Are there any alternatives to commons validator?
-
15I'll just leave this here: http://davidcelis.com/blog/2012/09/06/stop-validating-email-addresses-with-regex/ – mpenkov Oct 30 '12 at 06:44
-
4Current URL for Commons: http://commons.apache.org/proper/commons-validator/apidocs/org/apache/commons/validator/routines/EmailValidator.html – james.garriss Jun 16 '14 at 11:30
-
You shouldn't want to use libraries (or regexes) that don't comprehensively validate. Due to the complexity of valid email address, there is no middle-ground between no validation and comprehensive validation. Apache Commons' implementation is *not* comprehensive. I'm aware of only one library that is ([email-rfc2822-validator](https://github.com/bbottema/email-rfc2822-validator)), but it still works with huge regexes. A comprehensive **lexer** is what you really want. [EmailValidator4J](https://github.com/egulias/EmailValidator4J) says it does the job, but I don't have experience with it. – Benny Bottema Dec 29 '17 at 10:10
-
1@BennyBottema Instead of editing the question with commentary, please make a Meta post to discuss why this was closed if you still have questions. – Machavity Jun 29 '18 at 16:48
19 Answers
Using the official java email package is the easiest:
public static boolean isValidEmailAddress(String email) {
boolean result = true;
try {
InternetAddress emailAddr = new InternetAddress(email);
emailAddr.validate();
} catch (AddressException ex) {
result = false;
}
return result;
}

- 2,111
- 2
- 21
- 34

- 3,491
- 2
- 18
- 16
-
in the easiest and you probably already have it in your project to send emails – Eduardo Sep 01 '11 at 20:37
-
59Note that InternetAddress.validate() considers user@[10.9.8.7] and user@localhost as valid email addresses - which they are according to the RFC. Though, depending on the use case (web form), you might want to treat them as invalid. – zillion1 Oct 05 '11 at 08:30
-
Also - I believe throwing and then catching exceptions is very inefficient – Nic Cottrell Feb 01 '12 at 15:55
-
the inefficiency shouldn't matter unless you are dealing with many invalid email addresses. – TofuBeer May 09 '12 at 15:54
-
8not only that is valid as @zillion1 said, but also things like bla@bla are considered valid. Really not the best solution. – Diego Plentz Nov 20 '12 at 17:30
-
4@NicholasTolleyCottrell This is Java, here we throw and catch exceptions, I don't really get your point – gyorgyabraham Jan 25 '13 at 15:44
-
2My point is that creating exceptions in Java is a relatively expensive process, whereas doing a validation returning a boolean would have much less overhead. – Nic Cottrell Jan 25 '13 at 16:29
-
17I suspect that InternetAddress constructor has been tampered with. Or my system has been tampered with. Or RFC822 has been tampered with. Or I could really use some sleep right now. But I just tried some code and the following five strings all pass as valid e-mail addresses if you pass them to the InternetAddress constructor, and "clearly", they are not valid. Here we go: `.`, `.com`, `com.`, `abc` and `123`. Also, adding leading or trailing white space do not invalidate the strings either. You be the judge! – Martin Andersson Mar 19 '13 at 21:12
-
Wouldn't this also return true for `John Doe
`? This is a valid address to use in the email API, but might not be valid in other use cases. – Arjan Apr 05 '13 at 10:18 -
This is not really a good answer. The mentioned validator is way too generous on its rules. Email addresses like a@b should not be considered as valid. – Robert Kang Nov 27 '13 at 17:12
-
1Yeah, so it seems that this method considers `asdf` and `1234` to be valid email addresses... – Ajedi32 Dec 01 '13 at 18:19
-
1Its even validating a single character as valid, which can't be accepted..!! `System.out.println(isValidEmailAddress("adafdad"));` – Sreedhar GS Mar 20 '14 at 06:46
-
-
@RobertKang it is a valid email address according to the RFC, even if a non-existent one. – jwenting Aug 06 '14 at 13:42
-
this passes just fine but I don't think it should: `new InternetAddress(";...'foo@xxx.asd.sd,").validate` – Erik Kaplun Jan 22 '15 at 14:02
-
"cheese" is not a valid emai address, but it is according to this mechanism – NimChimpsky Feb 09 '15 at 16:50
-
4um, cheese fails properly when I run it. what the hell javax.mail library are you linking to??? – Aaron Davidson Feb 12 '15 at 19:37
-
4People complaining about all kinds of non-existing, but valid email addresses being validated. It is a validator for the correctness of the email address, not whether it exists! After the validator a website should always employ an activation mechanism to check whether the address actually exists. – dstibbe Mar 20 '15 at 08:08
-
-
3_a//c@gmail.com_ is syntactically valid and all validators would accept it (unless they are broken). – David Balažic Aug 06 '15 at 15:18
-
-
1@ismailyavuz You need to add the [javax.mail](https://java.net/projects/javamail/downloads/download/javax.mail.jar) library. – Majora320 May 31 '16 at 23:24
-
Apache Commons is generally known as a solid project. Keep in mind, though, you'll still have to send a verification email to the address if you want to ensure it's a real email, and that the owner wants it used on your site.
EDIT: There was a bug where it was too restrictive on domain, causing it to not accept valid emails from new TLDs.
This bug was resolved on 03/Jan/15 02:48 in commons-validator version 1.4.1

- 1
- 1

- 278,309
- 50
- 514
- 539
-
1I agree with the extra bits you cited, but are those part of the Commons Validation project? – duffymo Mar 09 '09 at 00:33
-
2No, the Apache `EmailValidator` class does not send an email message for verification. – Matthew Flaschen Jul 14 '11 at 18:34
-
3If your use case is to validate a user's remote email address, this solution has a considerable flaw (similar to InternetAddress.validate()): EmailValidator considers user@[10.9.8.7] as a valid email addresses - which they are according to the RFC, but maybe not for user registration/contact form. – zillion1 Oct 05 '11 at 09:45
-
1@zillion, that is documented in Apache COmmons: "This implementation is not guaranteed to catch all possible errors in an email address." And I said what you have to do to "ensure it's a real email". Addresses with local IPs could be valid in rare environments, though. – Matthew Flaschen Oct 05 '11 at 22:11
-
1EmailValidator also gives you the option of excluding local email addresses so that the top level domain must be present, which was a requirement for me. – leojh Jan 30 '14 at 19:28
-
EmailValidator is very, very, very simple. It is also limited to ASCII. – user1050755 Mar 27 '14 at 13:25
-
5Apache Commons EmailValidator has one serious drawback: doesn't support IDN. – Piohen Jun 05 '14 at 10:50
-
The "isValid(emailString)" method of commons-validator-1.4.1.jar library returns that email like "somename...somesurname@mail.ru" is invalid even it is valid email. – Bake May 22 '15 at 13:49
-
commons-validator-1.4.1 reports `Leo Notenboom
` as invalid - althought it is accepted by common mailservers. – fiffy May 29 '15 at 11:17 -
It also consider something like `mgarin@.com` as a valid email (tried with version 1.4.1). Or is it actually valid one? – Mikle Garin Oct 06 '15 at 09:31
-
Check https://commons.apache.org/proper/commons-validator/download_validator.cgi for the newest version, and if it broken, you can just google it – HendraWD Jun 07 '18 at 10:27
Apache Commons validator can be used as mentioned in the other answers.
pom.xml:
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.4.1</version>
</dependency>
build.gradle:
compile 'commons-validator:commons-validator:1.4.1'
The import:
import org.apache.commons.validator.routines.EmailValidator;
The code:
String email = "myName@example.com";
boolean valid = EmailValidator.getInstance().isValid(email);
and to allow local addresses
boolean allowLocal = true;
boolean valid = EmailValidator.getInstance(allowLocal).isValid(email);

- 11,367
- 5
- 53
- 74
-
Only problem with apache commons is that it allows email addresses like "ab??$#@gmail.com". Although this address may be legit in terms of RFC, it's probably wrong input provided by the user. – Elad Tabak Jan 21 '15 at 12:02
-
2In Android Studio you can add compile 'commons-validator:commons-validator:1.4.1' into your app\build.gradle's dependencies {} – zOqvxf Dec 11 '15 at 17:08
-
2After actually trying to build my project, it seems apache commons doesn't work with Android very well, hundreds of warnings and some errors, it didn't even compile. This is what I ended up using http://howtodoinjava.com/2014/11/11/java-regex-validate-email-address/ – zOqvxf Dec 12 '15 at 11:39
-
1Same problem with me as of Benjiko99. After adding the dependency, the project wont compile, says java.exe finished with non zero exit code 2. – Amit Mittal Mar 18 '16 at 21:01
-
I think best option is to create new question to debug that crash – Aksel Willgert Mar 18 '16 at 22:12
-
1I was getting errors in Android Studio as well. I changed from 1.4.1 to 1.5.1 and it works! – Matt Oct 11 '16 at 22:20
-
1Note: Use_the Emailvalidator in org.apache.commons.validator.routines since EmailValidator in org.apache.commons.validator is deprecated (I am using 1.6 commons Validator) – HopeKing Dec 22 '17 at 03:55
-
Agree the apache commons is better than the official java one..which didn't mark personsname@gmail as invalid. – daticon Jan 19 '18 at 15:21
-
Check here for the newest version https://commons.apache.org/proper/commons-validator/download_validator.cgi, or if the link is broken, you can just google it. – HendraWD Jun 07 '18 at 10:28
Late answer, but I think it is simple and worthy:
public boolean isValidEmailAddress(String email) {
String ePattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(ePattern);
java.util.regex.Matcher m = p.matcher(email);
return m.matches();
}
Test Cases:
For production purpose, Domain Name validations should be performed network-wise.

- 3,154
- 3
- 38
- 52
-
40That's a pretty brutally simplistic validator that ignores most of the RFC rules along with IDNs. I'd steer clear of this for any production quality app. – mlaccetti Mar 25 '14 at 17:52
-
1
-
14Don't roll your own regex-based validator for things covered by RFCs. – Josh Glover Aug 07 '14 at 10:16
-
6reinventing the wheel is OK as long as you don't mind the occasional flat tire – dldnh Apr 18 '15 at 01:38
-
If you are trying to do a form validation received from the client, or just a bean validation - keep it simple. It's better to do a loose email validation rather than to do a strict one and reject some people, (e.g. when they are trying to register for your web service). With almost anything allowed in the username part of the email and so many new domains being added literally every month (e.g. .company, .entreprise, .estate), it's safer not to be restrictive:
Pattern pattern = Pattern.compile("^.+@.+\\..+$");
Matcher matcher = pattern.matcher(email);

- 2,396
- 1
- 24
- 25
-
3this is a really good point, any reasonable app should have other measures to prevent this input from being exploited down the line anyway – jmaculate Dec 12 '14 at 18:04
-
4How about change it to "^.+@.+(\\.[^\\.]+)+$" to avoid a trailing dot? – Alpha Huang Jun 30 '15 at 18:54
Late to the question, here, but: I maintain a class at this address: http://lacinato.com/cm/software/emailrelated/emailaddress
It is based on Les Hazlewood's class, but has numerous improvements and fixes a few bugs. Apache license.
I believe it is the most capable email parser in Java, and I have yet to see one more capable in any language, though there may be one out there. It's not a lexer-style parser, but uses some complicated java regex, and thus is not as efficient as it could be, but my company has parsed well over 10 billion real-world addresses with it: it's certainly usable in a high-performance situation. Maybe once a year it'll hit an address that causes a regex stack overflow (appropriately), but these are spam addresses which are hundreds or thousands of characters long with many many quotes and parenthesis and the like.
RFC 2822 and the related specs are really quite permissive in terms of email addresses, so a class like this is overkill for most uses. For example, the following is a legitimate address, according to spec, spaces and all:
"<bob \" (here) " < (hi there) "bob(the man)smith" (hi) @ (there) example.com (hello) > (again)
No mail server would allow that, but this class can parse it (and rewrite it to a usable form).
We found the existing Java email parser options to be insufficiently durable (meaning, all of them could not parse some valid addresses), so we created this class.
The code is well-documented and has a lot of easy-to-change options to allow or disallow certain email forms. It also provides a lot of methods to access certain parts of the address (left-hand side, right-hand side, personal names, comments, etc), to parse/validate mailbox-list headers, to parse/validate the return-path (which is unique among the headers), and so forth.
The code as written has a javamail dependency, but it's easy to remove if you don't want the minor functionality it provides.

- 421
- 5
- 9
-
1Hi, I copied it into GitHub for public the open source community. Now everybody can comment, document, and improve on the code. https://github.com/bbottema/email-rfc2822-validator. I used to use the older version by Les, but I had to remove it due to regex freezing bugs: http://leshazlewood.com/2006/11/06/emailaddress-java-class/comment-page-1/#comment_count – Benny Bottema Feb 25 '16 at 21:19
I'm just wondering why nobody came up with @Email
from Hibernate Validator's additional constraints. The validator itself is EmailValidator
.

- 7,738
- 2
- 38
- 67
-
Although an alternative to Apache commons, its implementation is as rudimentary as most regex-based libraries. From the docs: "However, as this article discusses it is not necessarily practical to implement a 100% compliant email validator". The only regex based comprehensive validator I know is [email-rfc2822-validator](https://github.com/bbottema/email-rfc2822-validator) and otherwise [EmailValidator4J](https://github.com/egulias/EmailValidator4J) seems promising. – Benny Bottema Dec 29 '17 at 08:50
Les Hazlewood has written a very thorough RFC 2822 compliant email validator class using Java regular expressions. You can find it at http://www.leshazlewood.com/?p=23. However, its thoroughness (or the Java RE implementation) leads to inefficiency - read the comments about parsing times for long addresses.

- 697
- 7
- 20
-
1I built upon Les Hazlewood's excellent class (which does have some bugs). (See my separate answer to this question.) Though I did maintain the java regex method, we use it just fine in a performance-critical environment. If all you're doing is parsing addresses, the performance may be an issue, but for most users I'd suspect it's just the beginning of whatever they're doing. My updates to the class did also fix a number of long-recursion issues. – lacinato Oct 30 '12 at 06:41
-
This is an outdated library and has been superseded twice, finally by [email-rfc2822-validator](https://github.com/bbottema/email-rfc2822-validator). Although it still fits all modern needs, it is also still prone to hidden performance bugs (and doesn't support the limited changes by the newer RFC specs). – Benny Bottema Dec 29 '17 at 10:06
public class Validations {
private Pattern regexPattern;
private Matcher regMatcher;
public String validateEmailAddress(String emailAddress) {
regexPattern = Pattern.compile("^[(a-zA-Z-0-9-\\_\\+\\.)]+@[(a-z-A-z)]+\\.[(a-zA-z)]{2,3}$");
regMatcher = regexPattern.matcher(emailAddress);
if(regMatcher.matches()) {
return "Valid Email Address";
} else {
return "Invalid Email Address";
}
}
public String validateMobileNumber(String mobileNumber) {
regexPattern = Pattern.compile("^\\+[0-9]{2,3}+-[0-9]{10}$");
regMatcher = regexPattern.matcher(mobileNumber);
if(regMatcher.matches()) {
return "Valid Mobile Number";
} else {
return "Invalid Mobile Number";
}
}
public static void main(String[] args) {
String emailAddress = "suryaprakash.pisay@gmail.com";
String mobileNumber = "+91-9986571622";
Validations validations = new Validations();
System.out.println(validations.validateEmailAddress(emailAddress));
System.out.println(validations.validateMobileNumber(mobileNumber));
}
}

- 1,467
- 2
- 13
- 20

- 642
- 8
- 23
I ported some of the code in Zend_Validator_Email:
@FacesValidator("emailValidator")
public class EmailAddressValidator implements Validator {
private String localPart;
private String hostName;
private boolean domain = true;
Locale locale;
ResourceBundle bundle;
private List<FacesMessage> messages = new ArrayList<FacesMessage>();
private HostnameValidator hostnameValidator;
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
setOptions(component);
String email = (String) value;
boolean result = true;
Pattern pattern = Pattern.compile("^(.+)@([^@]+[^.])$");
Matcher matcher = pattern.matcher(email);
locale = context.getViewRoot().getLocale();
bundle = ResourceBundle.getBundle("com.myapp.resources.validationMessages", locale);
boolean length = true;
boolean local = true;
if (matcher.find()) {
localPart = matcher.group(1);
hostName = matcher.group(2);
if (localPart.length() > 64 || hostName.length() > 255) {
length = false;
addMessage("enterValidEmail", "email.AddressLengthExceeded");
}
if (domain == true) {
hostnameValidator = new HostnameValidator();
hostnameValidator.validate(context, component, hostName);
}
local = validateLocalPart();
if (local && length) {
result = true;
} else {
result = false;
}
} else {
result = false;
addMessage("enterValidEmail", "invalidEmailAddress");
}
if (result == false) {
throw new ValidatorException(messages);
}
}
private boolean validateLocalPart() {
// First try to match the local part on the common dot-atom format
boolean result = false;
// Dot-atom characters are: 1*atext *("." 1*atext)
// atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
// "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
String atext = "a-zA-Z0-9\\u0021\\u0023\\u0024\\u0025\\u0026\\u0027\\u002a"
+ "\\u002b\\u002d\\u002f\\u003d\\u003f\\u005e\\u005f\\u0060\\u007b"
+ "\\u007c\\u007d\\u007e";
Pattern regex = Pattern.compile("^["+atext+"]+(\\u002e+["+atext+"]+)*$");
Matcher matcher = regex.matcher(localPart);
if (matcher.find()) {
result = true;
} else {
// Try quoted string format
// Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE
// qtext: Non white space controls, and the rest of the US-ASCII characters not
// including "\" or the quote character
String noWsCtl = "\\u0001-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f";
String qText = noWsCtl + "\\u0021\\u0023-\\u005b\\u005d-\\u007e";
String ws = "\\u0020\\u0009";
regex = Pattern.compile("^\\u0022(["+ws+qText+"])*["+ws+"]?\\u0022$");
matcher = regex.matcher(localPart);
if (matcher.find()) {
result = true;
} else {
addMessage("enterValidEmail", "email.AddressDotAtom");
addMessage("enterValidEmail", "email.AddressQuotedString");
addMessage("enterValidEmail", "email.AddressInvalidLocalPart");
}
}
return result;
}
private void addMessage(String detail, String summary) {
String detailMsg = bundle.getString(detail);
String summaryMsg = bundle.getString(summary);
messages.add(new FacesMessage(FacesMessage.SEVERITY_ERROR, summaryMsg, detailMsg));
}
private void setOptions(UIComponent component) {
Boolean domainOption = Boolean.valueOf((String) component.getAttributes().get("domain"));
//domain = (domainOption == null) ? true : domainOption.booleanValue();
}
}
With a hostname validator as follows:
@FacesValidator("hostNameValidator")
public class HostnameValidator implements Validator {
private Locale locale;
private ResourceBundle bundle;
private List<FacesMessage> messages;
private boolean checkTld = true;
private boolean allowLocal = false;
private boolean allowDNS = true;
private String tld;
private String[] validTlds = {"ac", "ad", "ae", "aero", "af", "ag", "ai",
"al", "am", "an", "ao", "aq", "ar", "arpa", "as", "asia", "at", "au",
"aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "biz",
"bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca",
"cat", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co",
"com", "coop", "cr", "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk",
"dm", "do", "dz", "ec", "edu", "ee", "eg", "er", "es", "et", "eu", "fi",
"fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh",
"gi", "gl", "gm", "gn", "gov", "gp", "gq", "gr", "gs", "gt", "gu", "gw",
"gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in",
"info", "int", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jobs",
"jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz",
"la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma",
"mc", "md", "me", "mg", "mh", "mil", "mk", "ml", "mm", "mn", "mo",
"mobi", "mp", "mq", "mr", "ms", "mt", "mu", "museum", "mv", "mw", "mx",
"my", "mz", "na", "name", "nc", "ne", "net", "nf", "ng", "ni", "nl",
"no", "np", "nr", "nu", "nz", "om", "org", "pa", "pe", "pf", "pg", "ph",
"pk", "pl", "pm", "pn", "pr", "pro", "ps", "pt", "pw", "py", "qa", "re",
"ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si",
"sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "su", "sv", "sy", "sz",
"tc", "td", "tel", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to",
"tp", "tr", "travel", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
"us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws",
"ye", "yt", "yu", "za", "zm", "zw"};
private Map<String, Map<Integer, Integer>> idnLength;
private void init() {
Map<Integer, Integer> biz = new HashMap<Integer, Integer>();
biz.put(5, 17);
biz.put(11, 15);
biz.put(12, 20);
Map<Integer, Integer> cn = new HashMap<Integer, Integer>();
cn.put(1, 20);
Map<Integer, Integer> com = new HashMap<Integer, Integer>();
com.put(3, 17);
com.put(5, 20);
Map<Integer, Integer> hk = new HashMap<Integer, Integer>();
hk.put(1, 15);
Map<Integer, Integer> info = new HashMap<Integer, Integer>();
info.put(4, 17);
Map<Integer, Integer> kr = new HashMap<Integer, Integer>();
kr.put(1, 17);
Map<Integer, Integer> net = new HashMap<Integer, Integer>();
net.put(3, 17);
net.put(5, 20);
Map<Integer, Integer> org = new HashMap<Integer, Integer>();
org.put(6, 17);
Map<Integer, Integer> tw = new HashMap<Integer, Integer>();
tw.put(1, 20);
Map<Integer, Integer> idn1 = new HashMap<Integer, Integer>();
idn1.put(1, 20);
Map<Integer, Integer> idn2 = new HashMap<Integer, Integer>();
idn2.put(1, 20);
Map<Integer, Integer> idn3 = new HashMap<Integer, Integer>();
idn3.put(1, 20);
Map<Integer, Integer> idn4 = new HashMap<Integer, Integer>();
idn4.put(1, 20);
idnLength = new HashMap<String, Map<Integer, Integer>>();
idnLength.put("BIZ", biz);
idnLength.put("CN", cn);
idnLength.put("COM", com);
idnLength.put("HK", hk);
idnLength.put("INFO", info);
idnLength.put("KR", kr);
idnLength.put("NET", net);
idnLength.put("ORG", org);
idnLength.put("TW", tw);
idnLength.put("ایران", idn1);
idnLength.put("中国", idn2);
idnLength.put("公司", idn3);
idnLength.put("网络", idn4);
messages = new ArrayList<FacesMessage>();
}
public HostnameValidator() {
init();
}
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String hostName = (String) value;
locale = context.getViewRoot().getLocale();
bundle = ResourceBundle.getBundle("com.myapp.resources.validationMessages", locale);
Pattern ipPattern = Pattern.compile("^[0-9a-f:\\.]*$", Pattern.CASE_INSENSITIVE);
Matcher ipMatcher = ipPattern.matcher(hostName);
if (ipMatcher.find()) {
addMessage("hostname.IpAddressNotAllowed");
throw new ValidatorException(messages);
}
boolean result = false;
// removes last dot (.) from hostname
hostName = hostName.replaceAll("(\\.)+$", "");
String[] domainParts = hostName.split("\\.");
boolean status = false;
// Check input against DNS hostname schema
if ((domainParts.length > 1) && (hostName.length() > 4) && (hostName.length() < 255)) {
status = false;
dowhile:
do {
// First check TLD
int lastIndex = domainParts.length - 1;
String domainEnding = domainParts[lastIndex];
Pattern tldRegex = Pattern.compile("([^.]{2,10})", Pattern.CASE_INSENSITIVE);
Matcher tldMatcher = tldRegex.matcher(domainEnding);
if (tldMatcher.find() || domainEnding.equals("ایران")
|| domainEnding.equals("中国")
|| domainEnding.equals("公司")
|| domainEnding.equals("网络")) {
// Hostname characters are: *(label dot)(label dot label); max 254 chars
// label: id-prefix [*ldh{61} id-prefix]; max 63 chars
// id-prefix: alpha / digit
// ldh: alpha / digit / dash
// Match TLD against known list
tld = (String) tldMatcher.group(1).toLowerCase().trim();
if (checkTld == true) {
boolean foundTld = false;
for (int i = 0; i < validTlds.length; i++) {
if (tld.equals(validTlds[i])) {
foundTld = true;
}
}
if (foundTld == false) {
status = false;
addMessage("hostname.UnknownTld");
break dowhile;
}
}
/**
* Match against IDN hostnames
* Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
*/
List<String> regexChars = getIdnRegexChars();
// Check each hostname part
int check = 0;
for (String domainPart : domainParts) {
// Decode Punycode domainnames to IDN
if (domainPart.indexOf("xn--") == 0) {
domainPart = decodePunycode(domainPart.substring(4));
}
// Check dash (-) does not start, end or appear in 3rd and 4th positions
if (domainPart.indexOf("-") == 0
|| (domainPart.length() > 2 && domainPart.indexOf("-", 2) == 2 && domainPart.indexOf("-", 3) == 3)
|| (domainPart.indexOf("-") == (domainPart.length() - 1))) {
status = false;
addMessage("hostname.DashCharacter");
break dowhile;
}
// Check each domain part
boolean checked = false;
for (int key = 0; key < regexChars.size(); key++) {
String regexChar = regexChars.get(key);
Pattern regex = Pattern.compile(regexChar);
Matcher regexMatcher = regex.matcher(domainPart);
status = regexMatcher.find();
if (status) {
int length = 63;
if (idnLength.containsKey(tld.toUpperCase())
&& idnLength.get(tld.toUpperCase()).containsKey(key)) {
length = idnLength.get(tld.toUpperCase()).get(key);
}
int utf8Length;
try {
utf8Length = domainPart.getBytes("UTF8").length;
if (utf8Length > length) {
addMessage("hostname.InvalidHostname");
} else {
checked = true;
break;
}
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
if (checked) {
++check;
}
}
// If one of the labels doesn't match, the hostname is invalid
if (check != domainParts.length) {
status = false;
addMessage("hostname.InvalidHostnameSchema");
}
} else {
// Hostname not long enough
status = false;
addMessage("hostname.UndecipherableTld");
}
} while (false);
if (status == true && allowDNS) {
result = true;
}
} else if (allowDNS == true) {
addMessage("hostname.InvalidHostname");
throw new ValidatorException(messages);
}
// Check input against local network name schema;
Pattern regexLocal = Pattern.compile("^(([a-zA-Z0-9\\x2d]{1,63}\\x2e)*[a-zA-Z0-9\\x2d]{1,63}){1,254}$", Pattern.CASE_INSENSITIVE);
boolean checkLocal = regexLocal.matcher(hostName).find();
if (allowLocal && !status) {
if (checkLocal) {
result = true;
} else {
// If the input does not pass as a local network name, add a message
result = false;
addMessage("hostname.InvalidLocalName");
}
}
// If local network names are not allowed, add a message
if (checkLocal && !allowLocal && !status) {
result = false;
addMessage("hostname.LocalNameNotAllowed");
}
if (result == false) {
throw new ValidatorException(messages);
}
}
private void addMessage(String msg) {
String bundlMsg = bundle.getString(msg);
messages.add(new FacesMessage(FacesMessage.SEVERITY_ERROR, bundlMsg, bundlMsg));
}
/**
* Returns a list of regex patterns for the matched TLD
* @param tld
* @return
*/
private List<String> getIdnRegexChars() {
List<String> regexChars = new ArrayList<String>();
regexChars.add("^[a-z0-9\\x2d]{1,63}$");
Document doc = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try {
InputStream validIdns = getClass().getClassLoader().getResourceAsStream("com/myapp/resources/validIDNs_1.xml");
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(validIdns);
doc.getDocumentElement().normalize();
} catch (SAXException ex) {
Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
} catch (ParserConfigurationException ex) {
Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
}
// prepare XPath
XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nodes = null;
String xpathRoute = "//idn[tld=\'" + tld.toUpperCase() + "\']/pattern/text()";
try {
XPathExpression expr;
expr = xpath.compile(xpathRoute);
Object res = expr.evaluate(doc, XPathConstants.NODESET);
nodes = (NodeList) res;
} catch (XPathExpressionException ex) {
Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
}
for (int i = 0; i < nodes.getLength(); i++) {
regexChars.add(nodes.item(i).getNodeValue());
}
return regexChars;
}
/**
* Decode Punycode string
* @param encoded
* @return
*/
private String decodePunycode(String encoded) {
Pattern regex = Pattern.compile("([^a-z0-9\\x2d]{1,10})", Pattern.CASE_INSENSITIVE);
Matcher matcher = regex.matcher(encoded);
boolean found = matcher.find();
if (encoded.isEmpty() || found) {
// no punycode encoded string, return as is
addMessage("hostname.CannotDecodePunycode");
throw new ValidatorException(messages);
}
int separator = encoded.lastIndexOf("-");
List<Integer> decoded = new ArrayList<Integer>();
if (separator > 0) {
for (int x = 0; x < separator; ++x) {
decoded.add((int) encoded.charAt(x));
}
} else {
addMessage("hostname.CannotDecodePunycode");
throw new ValidatorException(messages);
}
int lengthd = decoded.size();
int lengthe = encoded.length();
// decoding
boolean init = true;
int base = 72;
int index = 0;
int ch = 0x80;
int indexeStart = (separator == 1) ? (separator + 1) : 0;
for (int indexe = indexeStart; indexe < lengthe; ++lengthd) {
int oldIndex = index;
int pos = 1;
for (int key = 36; true; key += 36) {
int hex = (int) encoded.charAt(indexe++);
int digit = (hex - 48 < 10) ? hex - 22
: ((hex - 65 < 26) ? hex - 65
: ((hex - 97 < 26) ? hex - 97
: 36));
index += digit * pos;
int tag = (key <= base) ? 1 : ((key >= base + 26) ? 26 : (key - base));
if (digit < tag) {
break;
}
pos = (int) (pos * (36 - tag));
}
int delta = (int) (init ? ((index - oldIndex) / 700) : ((index - oldIndex) / 2));
delta += (int) (delta / (lengthd + 1));
int key;
for (key = 0; delta > 910; key += 36) {
delta = (int) (delta / 35);
}
base = (int) (key + 36 * delta / (delta + 38));
init = false;
ch += (int) (index / (lengthd + 1));
index %= (lengthd + 1);
if (lengthd > 0) {
for (int i = lengthd; i > index; i--) {
decoded.set(i, decoded.get(i - 1));
}
}
decoded.set(index++, ch);
}
// convert decoded ucs4 to utf8 string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < decoded.size(); i++) {
int value = decoded.get(i);
if (value < 128) {
sb.append((char) value);
} else if (value < (1 << 11)) {
sb.append((char) (192 + (value >> 6)));
sb.append((char) (128 + (value & 63)));
} else if (value < (1 << 16)) {
sb.append((char) (224 + (value >> 12)));
sb.append((char) (128 + ((value >> 6) & 63)));
sb.append((char) (128 + (value & 63)));
} else if (value < (1 << 21)) {
sb.append((char) (240 + (value >> 18)));
sb.append((char) (128 + ((value >> 12) & 63)));
sb.append((char) (128 + ((value >> 6) & 63)));
sb.append((char) (128 + (value & 63)));
} else {
addMessage("hostname.CannotDecodePunycode");
throw new ValidatorException(messages);
}
}
return sb.toString();
}
/**
* Eliminates empty values from input array
* @param data
* @return
*/
private String[] verifyArray(String[] data) {
List<String> result = new ArrayList<String>();
for (String s : data) {
if (!s.equals("")) {
result.add(s);
}
}
return result.toArray(new String[result.size()]);
}
}
And a validIDNs.xml with regex patterns for the different tlds (too big to include:)
<idnlist>
<idn>
<tld>AC</tld>
<pattern>^[\u002d0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$</pattern>
</idn>
<idn>
<tld>AR</tld>
<pattern>^[\u002d0-9a-zà-ãç-êìíñ-õü]{1,63}$</pattern>
</idn>
<idn>
<tld>AS</tld>
<pattern>/^[\u002d0-9a-zà-öø-ÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĸĺļľłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźż]{1,63}$</pattern>
</idn>
<idn>
<tld>AT</tld>
<pattern>/^[\u002d0-9a-zà-öø-ÿœšž]{1,63}$</pattern>
</idn>
<idn>
<tld>BIZ</tld>
<pattern>^[\u002d0-9a-zäåæéöøü]{1,63}$</pattern>
<pattern>^[\u002d0-9a-záéíñóúü]{1,63}$</pattern>
<pattern>^[\u002d0-9a-záéíóöúüőű]{1,63}$</pattern>
</id>
</idlist>

- 805
- 1
- 9
- 11
-
This answer isn't applicable anymore for obvious reasons. Remove TLD validation and it's probably acceptable if you want to accept non-English e-mail addresses. – Christopher Schneider Apr 14 '16 at 13:09
Although there are many alternatives to Apache commons, their implementations are rudimentary at best (like Apache commons' implementation itself) and even dead wrong in other cases.
I'd also stay away from so called simple 'non-restrictive' regex; there's no such thing. For example @ is allowed multiple times depending on context, how do you know the required one is there? Simple regex won't understand it, even though the email should be valid. Anything more complex becomes error-prone or even contain hidden performance killers. How are you going to maintain something like this?
The only comprehensive RFC compliant regex based validator I'm aware of is email-rfc2822-validator with its 'refined' regex appropriately named Dragons.java. It supports only the older RFC-2822 spec though, although appropriate enough for modern needs (RFC-5322 updates it in areas already out of scope for daily use cases).
But really what you want is a lexer that properly parses a string and breaks it up into the component structure according to the RFC grammar. EmailValidator4J seems promising in that regard, but is still young and limited.
Another option you have is using a webservice such as Mailgun's battle-tested validation webservice or Mailboxlayer API (just took the first Google results). It is not strictly RFC compliant, but works well enough for modern needs.

- 1
- 1

- 11,111
- 10
- 71
- 96
If you're looking to verify whether an email address is valid, then VRFY will get you some of the way. I've found it's useful for validating intranet addresses (that is, email addresses for internal sites). However it's less useful for internet mail servers (see the caveats at the top of this page)

- 268,207
- 37
- 334
- 440
Current Apache Commons Validator version is 1.3.1.
Class that validates is org.apache.commons.validator.EmailValidator. It has an import for org.apache.oro.text.perl.Perl5Util which is from a retired Jakarta ORO project.
BTW, I found that there is a 1.4 version, here are the API docs. On the site it says: "Last Published: 05 March 2008 | Version: 1.4-SNAPSHOT", but that's not final. Only way to build yourself (but this is a snapshot, not RELEASE) and use, or download from here. This means 1.4 has not been made final for three years (2008-2011). This is not in Apache's style. I'm looking for a better option, but didn't find one that is very adopted. I want to use something that is well tested, don't want to hit any bugs.

- 1,853
- 2
- 19
- 33
-
1.4 SNAPSHOT also requires Jakarta ORO. Apache Commons Validator is not usable for me. – mist Apr 03 '11 at 18:09
-
Finally chose Dr.Vet. Cumpanasu Florin's solution: http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/ – mist Apr 03 '11 at 20:06
-
1I agree that the Apache Commons validator works well, but I find it to be quite slow - over 3ms per call. – Nic Cottrell Feb 01 '12 at 15:57
-
-
current trunk SNAPSHOT (SVN REV 1227719 as of now) has no external dependencies like ORO anymore - you don't even need the whole validation module anymore - the four classes org.apache.commons.validator.routines.EmailValidator, InetAddressValidator, DomainValidator and RegexValidator are able to stand alone – Jörg Feb 15 '12 at 10:58
What do you want to validate? The email address?
The email address can only be checked for its format conformance. See the standard: RFC2822. Best way to do that is a regular expression. You will never know if really exists without sending an email.
I checked the commons validator. It contains an org.apache.commons.validator.EmailValidator class. Seems to be a good starting point.
-
I am not sure the regex is the best way to do so, it's [quite unreadable](http://stackoverflow.com/a/2148664/2813274) if you intend to follow the RFC to the letter – user2813274 Nov 01 '14 at 20:25
-
Agree with @user2813274, you would want a proper lexer, not spaghetti regex. – Benny Bottema Dec 29 '17 at 09:50
You may also want to check for the length - emails are a maximum of 254 chars long. I use the apache commons validator and it doesn't check for this.

- 59
- 1
- 1
-
[RFC 2821](http://www.ietf.org/rfc/rfc2821.txt) species (section 4.5.3.1) specifies a `local-part` length of 64 and a `domain` length of 255. (They do say that longer is allowed by might be rejected by other software.) – sarnold Nov 23 '11 at 07:44
There don't seem to be any perfect libraries or ways to do this yourself, unless you have to time to send an email to the email address and wait for a response (this might not be an option though). I ended up using a suggestion from here http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/ and adjusting the code so it would work in Java.
public static boolean isValidEmailAddress(String email) {
boolean stricterFilter = true;
String stricterFilterString = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
String laxString = ".+@.+\\.[A-Za-z]{2}[A-Za-z]*";
String emailRegex = stricterFilter ? stricterFilterString : laxString;
java.util.regex.Pattern p = java.util.regex.Pattern.compile(emailRegex);
java.util.regex.Matcher m = p.matcher(email);
return m.matches();
}

- 674
- 10
- 22
This is the best method:
public static boolean isValidEmail(String enteredEmail){
String EMAIL_REGIX = "^[\\\\w!#$%&’*+/=?`{|}~^-]+(?:\\\\.[\\\\w!#$%&’*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\\\.)+[a-zA-Z]{2,6}$";
Pattern pattern = Pattern.compile(EMAIL_REGIX);
Matcher matcher = pattern.matcher(enteredEmail);
return ((!enteredEmail.isEmpty()) && (enteredEmail!=null) && (matcher.matches()));
}
Sources:- http://howtodoinjava.com/2014/11/11/java-regex-validate-email-address/

- 2,516
- 4
- 32
- 50
Another option is use the Hibernate email validator, using the annotation @Email
or using the validator class programatically, like:
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
class Validator {
// code
private boolean isValidEmail(String email) {
EmailValidator emailValidator = new EmailValidator();
return emailValidator.isValid(email, null);
}
}

- 17,757
- 11
- 115
- 164
Heres my pragmatic approach, where I just want reasonable distinct blah@domain addresses using the allowable characters from the RFC. Addresses must be converted to lowercase beforehand.
public class EmailAddressValidator {
private static final String domainChars = "a-z0-9\\-";
private static final String atomChars = "a-z0-9\\Q!#$%&'*+-/=?^_`{|}~\\E";
private static final String emailRegex = "^" + dot(atomChars) + "@" + dot(domainChars) + "$";
private static final Pattern emailPattern = Pattern.compile(emailRegex);
private static String dot(String chars) {
return "[" + chars + "]+(?:\\.[" + chars + "]+)*";
}
public static boolean isValidEmailAddress(String address) {
return address != null && emailPattern.matcher(address).matches();
}
}

- 2,525
- 1
- 24
- 26