You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/04/30 04:11:23 UTC
[james-project] 03/21: [PERF] Fasten Username and Domain core object parsing
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 294b822883978f534c855893428fd1f2205d8734
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sun Apr 17 22:24:07 2022 +0700
[PERF] Fasten Username and Domain core object parsing
- Avoid allocation of substring
- Avoid stream logic
- Groups charmatcher checks.
- Avoid needless operations in Username
This delivers a 5 fold performance enhancement on a code
path often called (event deserialization, JMAP auth,
Cassandra mailbox queries). Memory allocation is also
greatly reduced.
---
.../main/java/org/apache/james/core/Domain.java | 51 +++++++++++++---------
.../main/java/org/apache/james/core/Username.java | 5 +--
.../contract/MailboxSetMethodContract.scala | 2 +-
.../james/webadmin/routes/MappingRoutesTest.java | 2 +-
...erFastViewProjectionItemsRequestToTaskTest.java | 3 +-
.../webadmin/routes/UserMailboxesRoutesTest.java | 2 +-
.../service/MailboxesExportRequestToTaskTest.java | 3 +-
.../service/SubscribeAllRequestToTaskTest.java | 2 +-
8 files changed, 37 insertions(+), 33 deletions(-)
diff --git a/core/src/main/java/org/apache/james/core/Domain.java b/core/src/main/java/org/apache/james/core/Domain.java
index 7fb06d254f..80f0e50196 100644
--- a/core/src/main/java/org/apache/james/core/Domain.java
+++ b/core/src/main/java/org/apache/james/core/Domain.java
@@ -20,21 +20,22 @@
package org.apache.james.core;
import java.io.Serializable;
-import java.util.List;
import java.util.Locale;
import java.util.Objects;
import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
import com.google.common.net.InetAddresses;
public class Domain implements Serializable {
private static final CharMatcher DASH_MATCHER = CharMatcher.anyOf("-_");
private static final CharMatcher DIGIT_MATCHER = CharMatcher.inRange('0', '9');
- private static final CharMatcher LETTER_MATCHER = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z'));
- private static final CharMatcher PART_CHAR_MATCHER = DIGIT_MATCHER.or(LETTER_MATCHER).or(DASH_MATCHER);
+ private static final CharMatcher PART_CHAR_MATCHER = new CharMatcher() {
+ @Override
+ public boolean matches(char c) {
+ return Character.isLetterOrDigit(c) || '-' == c || '_' == c || '.' == c;
+ }
+ };
public static final Domain LOCALHOST = Domain.of("localhost");
public static final int MAXIMUM_DOMAIN_LENGTH = 253;
@@ -52,27 +53,35 @@ public class Domain implements Serializable {
"Domain name length should not exceed %s characters", MAXIMUM_DOMAIN_LENGTH);
String domainWithoutBrackets = removeBrackets(domain);
- List<String> parts = Splitter.on('.').splitToList(domainWithoutBrackets);
- parts.forEach(Domain::assertValidPart);
- assertValidLastPart(Iterables.getLast(parts), domainWithoutBrackets);
-
+ Preconditions.checkArgument(PART_CHAR_MATCHER.matchesAllOf(domainWithoutBrackets), "Domain parts ASCII chars must be a-z A-Z 0-9 - or _");
+
+ int pos = 0;
+ int nextDot = domainWithoutBrackets.indexOf('.');
+
+ while (nextDot > -1) {
+ if (pos + 1 > domainWithoutBrackets.length()) {
+ throw new IllegalArgumentException("Last domain part should not be empty");
+ }
+ Domain.assertValidPart(domainWithoutBrackets, pos, nextDot);
+ pos = nextDot + 1;
+ nextDot = domainWithoutBrackets.indexOf('.', pos);
+ }
+ Domain.assertValidPart(domainWithoutBrackets, pos, domainWithoutBrackets.length());
+ assertValidLastPart(domainWithoutBrackets, pos);
return new Domain(domainWithoutBrackets);
}
- private static void assertValidPart(String domainPart) {
- Preconditions.checkArgument(!domainPart.isEmpty(), "Domain part should not be empty");
- Preconditions.checkArgument(!DASH_MATCHER.matches(domainPart.charAt(0)), "Domain part should not start with '-' or '_'");
- Preconditions.checkArgument(!DASH_MATCHER.matches(domainPart.charAt(domainPart.length() - 1)), "Domain part should not end with '-' or '_'");
- Preconditions.checkArgument(domainPart.length() <= 63, "Domain part should not not exceed 63 characters");
-
- String asciiChars = CharMatcher.ascii().retainFrom(domainPart);
- Preconditions.checkArgument(PART_CHAR_MATCHER.matchesAllOf(asciiChars), "Domain parts ASCII chars must be a-z A-Z 0-9 - or _");
+ private static void assertValidPart(String domainPart, int begin, int end) {
+ Preconditions.checkArgument(begin != end, "Domain part should not be empty");
+ Preconditions.checkArgument(!DASH_MATCHER.matches(domainPart.charAt(begin)), "Domain part should not start with '-' or '_'");
+ Preconditions.checkArgument(!DASH_MATCHER.matches(domainPart.charAt(end - 1)), "Domain part should not end with '-' or '_'");
+ Preconditions.checkArgument(end - begin <= 63, "Domain part should not not exceed 63 characters");
}
- private static void assertValidLastPart(String domainPart, String domain) {
- boolean onlyDigits = DIGIT_MATCHER.matches(domainPart.charAt(0));
- boolean invalid = onlyDigits && !validIPAddress(domain);
- Preconditions.checkArgument(!invalid, "The k=last domain part must not start with 0-9");
+ private static void assertValidLastPart(String domainPart, int pos) {
+ boolean onlyDigits = DIGIT_MATCHER.matches(domainPart.charAt(pos));
+ boolean invalid = onlyDigits && !validIPAddress(domainPart);
+ Preconditions.checkArgument(!invalid, "The last domain part must not start with 0-9");
}
private static boolean validIPAddress(String value) {
diff --git a/core/src/main/java/org/apache/james/core/Username.java b/core/src/main/java/org/apache/james/core/Username.java
index 4a4c545f77..f1bc031210 100644
--- a/core/src/main/java/org/apache/james/core/Username.java
+++ b/core/src/main/java/org/apache/james/core/Username.java
@@ -33,7 +33,7 @@ public class Username {
public static Username of(String username) {
Preconditions.checkArgument(username != null, "username should not be null or empty");
- Preconditions.checkArgument(!username.trim().isEmpty(), "username should not be null or empty after being trimmed");
+ Preconditions.checkArgument(!username.isBlank(), "username should not be null or empty after being trimmed");
Preconditions.checkArgument(username.length() <= MAXIMUM_MAIL_ADDRESS_LENGTH,
"username length should not be longer than %s characters", MAXIMUM_MAIL_ADDRESS_LENGTH);
@@ -43,9 +43,6 @@ public class Username {
}
String userPart = username.substring(0, atPosition);
String domainPart = username.substring(atPosition + 1);
- if (domainPart.indexOf('@') >= 0) {
- throw new IllegalArgumentException("The username should not contain multiple domain delimiter. Value: " + username);
- }
return fromLocalPartWithDomain(userPart, domainPart);
}
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
index cae88d6870..6ab298d0e1 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
@@ -5628,7 +5628,7 @@ trait MailboxSetMethodContract {
| "notUpdated": {
| "${mailboxId.serialize}": {
| "type": "invalidPatch",
- | "description": "The username should not contain multiple domain delimiter. Value: invalid@invalid@domain.tld"
+ | "description": "Domain parts ASCII chars must be a-z A-Z 0-9 - or _"
| }
| }
| }, "c1"]
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
index 334c66e564..7cfee64db3 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
@@ -593,7 +593,7 @@ class MappingRoutesTest {
.body("statusCode", is(400))
.body("type", is("InvalidArgument"))
.body("message", is("Invalid arguments supplied in the user request"))
- .body("details", is("The username should not contain multiple domain delimiter. Value: alice123@domain@domain.tld"));
+ .body("details", is("Domain parts ASCII chars must be a-z A-Z 0-9 - or _"));
}
@Test
diff --git a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeUserFastViewProjectionItemsRequestToTaskTest.java b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeUserFastViewProjectionItemsRequestToTaskTest.java
index 192004eca3..e656b13b7f 100644
--- a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeUserFastViewProjectionItemsRequestToTaskTest.java
+++ b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeUserFastViewProjectionItemsRequestToTaskTest.java
@@ -66,7 +66,6 @@ import org.junit.jupiter.api.Test;
import com.github.fge.lambdas.Throwing;
import io.restassured.RestAssured;
-import io.restassured.filter.log.LogDetail;
import spark.Service;
class RecomputeUserFastViewProjectionItemsRequestToTaskTest {
@@ -243,7 +242,7 @@ class RecomputeUserFastViewProjectionItemsRequestToTaskTest {
.body("statusCode", is(400))
.body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
.body("message", is("Invalid arguments supplied in the user request"))
- .body("details", is("The username should not contain multiple domain delimiter. Value: bad@bad@bad"));
+ .body("details", is("Domain parts ASCII chars must be a-z A-Z 0-9 - or _"));
}
@Test
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java
index 861cb7bd54..d8211af8eb 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java
@@ -1533,7 +1533,7 @@ class UserMailboxesRoutesTest {
.body("statusCode", Matchers.is(400))
.body("type", Matchers.is("InvalidArgument"))
.body("message", Matchers.is("Invalid arguments supplied in the user request"))
- .body("details", Matchers.is("The username should not contain multiple domain delimiter. Value: bad@bad@bad"));
+ .body("details", Matchers.is("Domain parts ASCII chars must be a-z A-Z 0-9 - or _"));
}
}
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/MailboxesExportRequestToTaskTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/MailboxesExportRequestToTaskTest.java
index 752e1b3943..c52d8ffc1b 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/MailboxesExportRequestToTaskTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/MailboxesExportRequestToTaskTest.java
@@ -62,7 +62,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import io.restassured.RestAssured;
-import io.restassured.filter.log.LogDetail;
import reactor.core.publisher.Mono;
import spark.Service;
@@ -179,7 +178,7 @@ class MailboxesExportRequestToTaskTest {
.body("statusCode", is(400))
.body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
.body("message", is("Invalid arguments supplied in the user request"))
- .body("details", is("The username should not contain multiple domain delimiter. Value: bad@bad@bad"));
+ .body("details", is("Domain parts ASCII chars must be a-z A-Z 0-9 - or _"));
}
@Test
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/SubscribeAllRequestToTaskTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/SubscribeAllRequestToTaskTest.java
index f5aecfba18..96d52cdbf0 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/SubscribeAllRequestToTaskTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/SubscribeAllRequestToTaskTest.java
@@ -174,7 +174,7 @@ class SubscribeAllRequestToTaskTest {
.body("statusCode", is(400))
.body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
.body("message", is("Invalid arguments supplied in the user request"))
- .body("details", is("The username should not contain multiple domain delimiter. Value: bad@bad@bad"));
+ .body("details", is("Domain parts ASCII chars must be a-z A-Z 0-9 - or _"));
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org