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 2020/12/05 07:10:16 UTC
[james-project] 09/17: JAMES-343 Mail should carry DsnParameters
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 bc826e7722fcae2f3294ce1e0af3f79ff2b4bcd5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Dec 3 10:54:23 2020 +0700
JAMES-343 Mail should carry DsnParameters
Leverage attributes by default in order to do so.
This prevents retro-compatibility breakage of the mailet-api.
---
.../java/org/apache/mailet/AttributeValue.java | 20 ++-
.../main/java/org/apache/mailet/DsnParameters.java | 158 +++++++++++++++++++++
.../api/src/main/java/org/apache/mailet/Mail.java | 10 ++
3 files changed, 185 insertions(+), 3 deletions(-)
diff --git a/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java b/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
index d935d46..9bb6d26 100644
--- a/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
+++ b/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.mailbox.model.MessageIdDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,6 +36,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.github.steveash.guavate.Guavate;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
@@ -209,12 +211,24 @@ public class AttributeValue<T> {
}
public <U> Optional<U> valueAs(Class<U> type) {
- return tryToCast(type, value);
+ return asAttributeValueOf(type).map(AttributeValue::value);
}
- private static <U> Optional<U> tryToCast(Class<U> type, Object value) {
+ public <U> Optional<AttributeValue<U>> asAttributeValueOf(Class<U> type) {
if (type.isInstance(value)) {
- return Optional.of(type.cast(value));
+ return Optional.of((AttributeValue<U>) this);
+ } else {
+ return Optional.empty();
+ }
+ }
+ public <U> Optional<AttributeValue<Map<String, AttributeValue<U>>>> asMapAttributeValueOf(Class<U> type) {
+ if (Map.class.isInstance(value)) {
+ Map<String, AttributeValue<?>> aMap = (Map<String, AttributeValue<?>>) value;
+ Map<String, AttributeValue<U>> castedMap = aMap.entrySet()
+ .stream()
+ .flatMap(entry -> entry.getValue().asAttributeValueOf(type).stream().map(castedValue -> Pair.of(entry.getKey(), castedValue)))
+ .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue));
+ return Optional.of(new AttributeValue<>(castedMap, new Serializer.MapSerializer()));
} else {
return Optional.empty();
}
diff --git a/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java b/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java
index 03f7479..a18ea88 100644
--- a/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java
+++ b/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java
@@ -21,19 +21,25 @@ package org.apache.mailet;
import java.util.Arrays;
import java.util.EnumSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.mail.internet.AddressException;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.core.MailAddress;
+import com.github.fge.lambdas.Throwing;
import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
/**
* Represents DSN parameters attached to the envelope of an Email transiting over SMTP
@@ -64,6 +70,15 @@ public class DsnParameters {
.orElseThrow(() -> new IllegalArgumentException(input + " is not a supported value for RET DSN parameter")));
}
+ public static Ret fromAttributeValue(AttributeValue<String> attributeValue) {
+ return parse(attributeValue.value())
+ .orElseThrow(() -> new IllegalArgumentException(attributeValue.value() + " is not a supported value for RET DSN parameter"));
+ }
+
+ public static AttributeValue<String> toAttributeValue(Ret value) {
+ return AttributeValue.of(value.toString());
+ }
+
public static Optional<Ret> parse(String string) {
Preconditions.checkNotNull(string);
@@ -84,6 +99,10 @@ public class DsnParameters {
.map(EnvId::of);
}
+ public static EnvId fromAttributeValue(AttributeValue<String> attributeValue) {
+ return of(attributeValue.value());
+ }
+
public static EnvId of(String value) {
Preconditions.checkNotNull(value);
@@ -100,6 +119,10 @@ public class DsnParameters {
return value;
}
+ public AttributeValue<String> toAttributeValue() {
+ return AttributeValue.of(value);
+ }
+
@Override
public final boolean equals(Object o) {
if (o instanceof EnvId) {
@@ -134,6 +157,14 @@ public class DsnParameters {
FAILURE,
DELAY;
+ public static EnumSet<Notify> fromAttributeValue(AttributeValue<String> attributeValue) {
+ return parse(attributeValue.value());
+ }
+
+ public static AttributeValue<String> toAttributeValue(EnumSet<Notify> value) {
+ return AttributeValue.of(Joiner.on(',').join(value));
+ }
+
public static EnumSet<Notify> parse(String input) {
Preconditions.checkNotNull(input);
@@ -171,12 +202,28 @@ public class DsnParameters {
Optional<MailAddress> orcptParameter = Optional.ofNullable(rcptToArgLine.get(ORCPT_PARAMETER))
.map(RecipientDsnParameters::parseOrcpt);
+ return of(notifyParameter, orcptParameter);
+ }
+
+ public static Optional<RecipientDsnParameters> of(Optional<EnumSet<Notify>> notifyParameter, Optional<MailAddress> orcptParameter) {
if (notifyParameter.isEmpty() && orcptParameter.isEmpty()) {
return Optional.empty();
}
return Optional.of(new RecipientDsnParameters(notifyParameter, orcptParameter));
}
+ public static RecipientDsnParameters of(EnumSet<Notify> notifyParameter, MailAddress orcptParameter) {
+ return new RecipientDsnParameters(Optional.of(notifyParameter), Optional.of(orcptParameter));
+ }
+
+ public static RecipientDsnParameters of(MailAddress orcptParameter) {
+ return new RecipientDsnParameters(Optional.empty(), Optional.of(orcptParameter));
+ }
+
+ public static RecipientDsnParameters of(EnumSet<Notify> notifyParameter) {
+ return new RecipientDsnParameters(Optional.of(notifyParameter), Optional.empty());
+ }
+
private static MailAddress parseOrcpt(String input) {
Preconditions.checkArgument(input.startsWith(RFC_822_PREFIX), "ORCPT must start with the rfc822 prefix");
String addressPart = input.substring(RFC_822_PREFIX.length());
@@ -228,6 +275,65 @@ public class DsnParameters {
}
}
+ public static class DsnAttributeValues {
+ private static final AttributeName ENVID_ATTRIBUTE_NAME = AttributeName.of("dsn-envid");
+ private static final AttributeName RET_ATTRIBUTE_NAME = AttributeName.of("dsn-ret");
+ private static final AttributeName NOTIFY_ATTRIBUTE_NAME = AttributeName.of("dsn-notify");
+ private static final AttributeName ORCPT_ATTRIBUTE_NAME = AttributeName.of("dsn-orcpt");
+
+ public static DsnAttributeValues extract(Map<AttributeName, Attribute> attributesMap) {
+ Optional<AttributeValue<String>> envId = Optional.ofNullable(attributesMap.get(ENVID_ATTRIBUTE_NAME))
+ .flatMap(attribute -> attribute.getValue().asAttributeValueOf(String.class));
+ Optional<AttributeValue<String>> ret = Optional.ofNullable(attributesMap.get(RET_ATTRIBUTE_NAME))
+ .flatMap(attribute -> attribute.getValue().asAttributeValueOf(String.class));
+ Optional<AttributeValue<Map<String, AttributeValue<String>>>> notify =
+ Optional.ofNullable(attributesMap.get(NOTIFY_ATTRIBUTE_NAME))
+ .flatMap(attribute -> attribute.getValue().asMapAttributeValueOf(String.class));
+ Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcpt =
+ Optional.ofNullable(attributesMap.get(ORCPT_ATTRIBUTE_NAME))
+ .flatMap(attribute -> attribute.getValue().asMapAttributeValueOf(String.class));
+
+ return new DsnAttributeValues(notify, orcpt, envId, ret);
+ }
+
+ private final Optional<AttributeValue<Map<String, AttributeValue<String>>>> notifyAttributeValue;
+ private final Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcptAttributeValue;
+ private final Optional<AttributeValue<String>> envIdAttributeValue;
+ private final Optional<AttributeValue<String>> retAttributeValue;
+
+ public DsnAttributeValues(Optional<AttributeValue<Map<String, AttributeValue<String>>>> notifyAttributeValue, Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcptAttributeValue, Optional<AttributeValue<String>> envIdAttributeValue, Optional<AttributeValue<String>> retAttributeValue) {
+ this.notifyAttributeValue = notifyAttributeValue;
+ this.orcptAttributeValue = orcptAttributeValue;
+ this.envIdAttributeValue = envIdAttributeValue;
+ this.retAttributeValue = retAttributeValue;
+ }
+
+ public Optional<AttributeValue<Map<String, AttributeValue<String>>>> getNotifyAttributeValue() {
+ return notifyAttributeValue;
+ }
+
+ public Optional<AttributeValue<Map<String, AttributeValue<String>>>> getOrcptAttributeValue() {
+ return orcptAttributeValue;
+ }
+
+ public Optional<AttributeValue<String>> getEnvIdAttributeValue() {
+ return envIdAttributeValue;
+ }
+
+ public Optional<AttributeValue<String>> getRetAttributeValue() {
+ return retAttributeValue;
+ }
+
+ public List<Attribute> asAttributes() {
+ ImmutableList.Builder<Attribute> result = ImmutableList.builder();
+ envIdAttributeValue.map(value -> new Attribute(ENVID_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+ retAttributeValue.map(value -> new Attribute(RET_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+ notifyAttributeValue.map(value -> new Attribute(NOTIFY_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+ orcptAttributeValue.map(value -> new Attribute(ORCPT_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+ return result.build();
+ }
+ }
+
public static Optional<DsnParameters> of(Optional<EnvId> envIdParameter, Optional<Ret> retParameter, ImmutableMap<MailAddress, RecipientDsnParameters> rcptParameters) {
if (envIdParameter.isEmpty() && retParameter.isEmpty() && rcptParameters.isEmpty()) {
return Optional.empty();
@@ -235,6 +341,39 @@ public class DsnParameters {
return Optional.of(new DsnParameters(envIdParameter, retParameter, rcptParameters));
}
+ public static Optional<DsnParameters> fromAttributeValue(DsnAttributeValues dsnAttributeValues) {
+ Optional<EnvId> envId = dsnAttributeValues.getEnvIdAttributeValue().map(EnvId::fromAttributeValue);
+ Optional<Ret> ret = dsnAttributeValues.getRetAttributeValue().map(Ret::fromAttributeValue);
+ Map<MailAddress, EnumSet<Notify>> notify = dsnAttributeValues.getNotifyAttributeValue()
+ .map(mapAttributeValue -> mapAttributeValue.value()
+ .entrySet()
+ .stream()
+ .map(Throwing.function(entry -> Pair.of(new MailAddress(entry.getKey()), Notify.fromAttributeValue(entry.getValue()))))
+ .collect(Guavate.entriesToMap()))
+ .orElse(ImmutableMap.of());
+ Map<MailAddress, MailAddress> orcpt = dsnAttributeValues.getOrcptAttributeValue()
+ .map(mapAttributeValue -> mapAttributeValue.value()
+ .entrySet()
+ .stream()
+ .map(Throwing.function(entry -> Pair.of(new MailAddress(entry.getKey()), new MailAddress(entry.getValue().value()))))
+ .collect(Guavate.toImmutableMap(
+ Pair::getKey,
+ Pair::getValue)))
+ .orElse(ImmutableMap.of());
+ ImmutableSet<MailAddress> rcpts = ImmutableSet.<MailAddress>builder()
+ .addAll(notify.keySet())
+ .addAll(orcpt.keySet())
+ .build();
+ ImmutableMap<MailAddress, RecipientDsnParameters> recipientDsnParameters = rcpts.stream()
+ .map(rcpt -> Pair.of(rcpt, new RecipientDsnParameters(
+ Optional.ofNullable(notify.get(rcpt)),
+ Optional.ofNullable(orcpt.get(rcpt)))))
+ .collect(Guavate.toImmutableMap(
+ Pair::getKey,
+ Pair::getValue));
+ return of(envId, ret, recipientDsnParameters);
+ }
+
private final Optional<EnvId> envIdParameter;
private final Optional<Ret> retParameter;
private final ImmutableMap<MailAddress, RecipientDsnParameters> rcptParameters;
@@ -257,6 +396,25 @@ public class DsnParameters {
return rcptParameters;
}
+ public DsnAttributeValues toAttributes() {
+ Optional<AttributeValue<String>> envIdAttributeValue = envIdParameter.map(EnvId::asString).map(AttributeValue::of);
+ Optional<AttributeValue<String>> retAttributeValue = retParameter.map(Ret::toString).map(AttributeValue::of);
+ Optional<AttributeValue<Map<String, AttributeValue<String>>>> notifyAttributeValue = AttributeValue.of(rcptParameters.entrySet().stream()
+ .filter(entry -> entry.getValue().getNotifyParameter().isPresent())
+ .map(entry -> Pair.of(entry.getKey().asString(),
+ Notify.toAttributeValue(entry.getValue().getNotifyParameter().get())))
+ .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)))
+ .asMapAttributeValueOf(String.class);
+ Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcptAttributeValue = AttributeValue.of(rcptParameters.entrySet().stream()
+ .filter(entry -> entry.getValue().getOrcptParameter().isPresent())
+ .map(entry -> Pair.of(entry.getKey().asString(),
+ AttributeValue.of(entry.getValue().getOrcptParameter().get().asString())))
+ .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)))
+ .asMapAttributeValueOf(String.class);
+
+ return new DsnAttributeValues(notifyAttributeValue, orcptAttributeValue, envIdAttributeValue, retAttributeValue);
+ }
+
@Override
public final boolean equals(Object o) {
if (o instanceof DsnParameters) {
diff --git a/mailet/api/src/main/java/org/apache/mailet/Mail.java b/mailet/api/src/main/java/org/apache/mailet/Mail.java
index 3b37521..fe7ca1a 100644
--- a/mailet/api/src/main/java/org/apache/mailet/Mail.java
+++ b/mailet/api/src/main/java/org/apache/mailet/Mail.java
@@ -438,4 +438,14 @@ public interface Mail extends Serializable, Cloneable {
.flatMap(Optional::stream)
.collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue));
}
+
+ default Optional<DsnParameters> dsnParameters() {
+ return DsnParameters.fromAttributeValue(DsnParameters.DsnAttributeValues.extract(attributesMap()));
+ }
+
+ default void setDsnParameters(DsnParameters dsnParameters) {
+ dsnParameters.toAttributes()
+ .asAttributes()
+ .forEach(this::setAttribute);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org