Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -80,32 +80,32 @@ public MailboxId getMailboxId() {

@Override
public MessageUid getUid() {
return messageMetaData().getUid();
return message.getUid();
}

@Override
public MessageId getMessageId() {
return messageMetaData().getMessageId();
return message.getMessageId();
}

@Override
public Date getInternalDate() {
return messageMetaData().getInternalDate();
return message.getInternalDate();
}

@Override
public Flags getFlags() {
return messageMetaData().getFlags();
return message.createFlags();
}

@Override
public ModSeq getModSeq() {
return messageMetaData().getModSeq();
return message.getModSeq();
}

@Override
public long getSize() {
return messageMetaData().getSize();
return message.getFullContentOctets();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
Expand Down Expand Up @@ -97,6 +98,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -660,19 +662,33 @@ private Function<Flux<Mailbox>, Flux<MailboxMetaData>> metadataTransformation(Ma

private Function<Flux<Mailbox>, Flux<MailboxMetaData>> withCounters(MailboxSession session, List<Mailbox> mailboxes) {
MessageMapper messageMapper = mailboxSessionMapperFactory.getMessageMapper(session);
Map<MailboxPath, Boolean> parentMap = parentMap(mailboxes, session);
int concurrency = 4;
return mailboxFlux -> mailboxFlux
.flatMap(mailbox -> retrieveCounters(messageMapper, mailbox, session)
.map(Throwing.<MailboxCounters, MailboxMetaData>function(
counters -> toMailboxMetadata(session, mailboxes, mailbox, counters))
counters -> toMailboxMetadata(session, parentMap, mailbox, counters))
.sneakyThrow()),
concurrency);
}

private Map<MailboxPath, Boolean> parentMap(List<Mailbox> mailboxes, MailboxSession session) {
return mailboxes.stream().map(Mailbox::generateAssociatedPath)
.flatMap(path -> {
List<MailboxPath> hierarchyLevels = path.getHierarchyLevels(session.getPathDelimiter());
return Lists.reverse(hierarchyLevels).stream().skip(1);
})
.collect(Guavate.toImmutableMap(
Function.identity(),
any -> true,
(a, b) -> true));
}

private Function<Flux<Mailbox>, Flux<MailboxMetaData>> withoutCounters(MailboxSession session, List<Mailbox> mailboxes) {
Map<MailboxPath, Boolean> parentMap = parentMap(mailboxes, session);
return mailboxFlux -> mailboxFlux
.map(Throwing.<Mailbox, MailboxMetaData>function(
mailbox -> toMailboxMetadata(session, mailboxes, mailbox, MailboxCounters
mailbox -> toMailboxMetadata(session, parentMap, mailbox, MailboxCounters
.builder()
.mailboxId(mailbox.getMailboxId())
.count(0)
Expand Down Expand Up @@ -738,30 +754,25 @@ private Flux<MailboxId> getDelegatedMailboxes(MailboxMapper mailboxMapper, Multi
.map(Mailbox::getMailboxId);
}

private MailboxMetaData toMailboxMetadata(MailboxSession session, List<Mailbox> mailboxes, Mailbox mailbox, MailboxCounters counters) throws UnsupportedRightException {
private MailboxMetaData toMailboxMetadata(MailboxSession session, Map<MailboxPath, Boolean> parentMap, Mailbox mailbox, MailboxCounters counters) throws UnsupportedRightException {
return new MailboxMetaData(
mailbox.generateAssociatedPath(),
mailbox.getMailboxId(),
getDelimiter(),
computeChildren(session, mailboxes, mailbox),
computeChildren(parentMap, mailbox),
Selectability.NONE,
storeRightManager.getResolvedMailboxACL(mailbox, session),
counters);
}

private MailboxMetaData.Children computeChildren(MailboxSession session, List<Mailbox> potentialChildren, Mailbox mailbox) {
if (hasChildIn(mailbox, potentialChildren, session)) {
private MailboxMetaData.Children computeChildren(Map<MailboxPath, Boolean> parentMap, Mailbox mailbox) {
if (parentMap.getOrDefault(mailbox.generateAssociatedPath(), false)) {
return MailboxMetaData.Children.HAS_CHILDREN;
} else {
return MailboxMetaData.Children.HAS_NO_CHILDREN;
}
}

private boolean hasChildIn(Mailbox parentMailbox, List<Mailbox> mailboxesWithPathLike, MailboxSession mailboxSession) {
return mailboxesWithPathLike.stream()
.anyMatch(mailbox -> mailbox.isChildOf(parentMailbox, mailboxSession));
}

@Override
public Flux<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) throws MailboxException {
return getInMailboxIds(expression, session)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
import com.google.common.collect.ImmutableList;

public class FlagsFactory {

private static Flags asFlags(MailboxMessage mailboxMessage, String[] userFlags) {
public static Flags createFlags(MailboxMessage mailboxMessage, String[] userFlags) {
final Flags flags = new Flags();
if (mailboxMessage.isAnswered()) {
flags.add(Flags.Flag.ANSWERED);
Expand Down Expand Up @@ -59,13 +58,6 @@ private static Flags asFlags(MailboxMessage mailboxMessage, String[] userFlags)
return flags;
}

public static Flags createFlags(MailboxMessage mailboxMessage, String[] userFlags) {
return builder()
.flags(asFlags(mailboxMessage, userFlags))
.addUserFlags(userFlags)
.build();
}

public static Builder builder() {
return new Builder();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class ReadyToBuild {

PeriodicalHealthChecksConfiguration build() {
Preconditions.checkArgument(period.compareTo(MINIMAL_HEALTH_CHECK_PERIOD) >= 0,
"'period' must be equal or greater than " + MINIMAL_HEALTH_CHECK_PERIOD.toMillis() + "ms");
"'period' must be equal or greater than %d ms", MINIMAL_HEALTH_CHECK_PERIOD.toMillis());

return new PeriodicalHealthChecksConfiguration(period);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private static String truncateToMaxLength(String body) {
Preview(String value) {
Preconditions.checkNotNull(value);
Preconditions.checkArgument(value.length() <= MAX_LENGTH,
String.format("the preview value '%s' has length longer than %d", value, MAX_LENGTH));
"the preview value '%s' has length longer than %s", value, MAX_LENGTH);

this.value = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@
public class JmapResponseWriterImpl implements JmapResponseWriter {

public static final String PROPERTIES_FILTER = "propertiesFilter";
private final ObjectMapperFactory objectMapperFactory;
private final ObjectMapper objectMapper;

@Inject
public JmapResponseWriterImpl(ObjectMapperFactory objectMapperFactory) {
this.objectMapperFactory = objectMapperFactory;
this.objectMapper = objectMapperFactory.forWriting();
}

@Override
Expand All @@ -60,17 +60,13 @@ public Flux<InvocationResponse> formatMethodResponse(Flux<JmapResponse> jmapResp
}

private ObjectMapper newConfiguredObjectMapper(JmapResponse jmapResponse) {
ObjectMapper objectMapper = objectMapperFactory.forWriting();

FilterProvider filterProvider = jmapResponse
.getFilterProvider()
.orElseGet(SimpleFilterProvider::new)
.setDefaultFilter(SimpleBeanPropertyFilter.serializeAll())
.addFilter(PROPERTIES_FILTER, getPropertiesFilter(jmapResponse.getProperties()));

objectMapper.setFilterProvider(filterProvider);

return objectMapper;
return objectMapper.copy().setFilterProvider(filterProvider);
}

private PropertyFilter getPropertiesFilter(Optional<? extends Set<? extends Property>> properties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageResult;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.codec.DecoderUtil;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.MimeConfig;
import org.apache.james.mime4j.util.MimeUtil;
import org.apache.james.util.ReactorUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -111,7 +112,7 @@ static ImmutableMap<String, String> toHeaderMap(List<Field> fields) {
Function<Map.Entry<String, Collection<Field>>, String> bodyConcatenator = fieldListEntry -> fieldListEntry.getValue()
.stream()
.map(Field::getBody)
.map(MimeUtil::unscrambleHeaderValue)
.map(body -> DecoderUtil.decodeEncodedWords(body, DecodeMonitor.SILENT))
.collect(Collectors.toList())
.stream()
.collect(Collectors.joining(JMAP_MULTIVALUED_FIELD_DELIMITER));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@
import javax.annotation.PreDestroy;
import javax.inject.Inject;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.lifecycle.api.Startable;
import org.apache.james.util.Port;
import org.slf4j.LoggerFactory;

import com.github.steveash.guavate.Guavate;
import com.google.common.collect.Multimap;

import reactor.netty.DisposableServer;
import reactor.netty.http.server.HttpServer;
import reactor.netty.http.server.HttpServerRequest;
Expand All @@ -40,16 +44,24 @@ public class JMAPServer implements Startable {
private static final int RANDOM_PORT = 0;

private final JMAPConfiguration configuration;
private final Set<JMAPRoutesHandler> jmapRoutesHandlers;
private final VersionParser versionParser;
private final Multimap<Version, JMAPRoute> routes;
private Optional<DisposableServer> server;

@Inject
public JMAPServer(JMAPConfiguration configuration, Set<JMAPRoutesHandler> jmapRoutesHandlers, VersionParser versionParser) {
this.configuration = configuration;
this.jmapRoutesHandlers = jmapRoutesHandlers;
this.versionParser = versionParser;
this.server = Optional.empty();

this.routes = versionParser.getSupportedVersions()
.stream()
.flatMap(version -> jmapRoutesHandlers.stream()
.flatMap(handler -> handler.routes(version)
.map(route -> Pair.of(version, route))))
.collect(Guavate.toImmutableListMultimap(
Pair::getKey,
Pair::getValue));
}

public Port getPort() {
Expand All @@ -76,8 +88,9 @@ private boolean wireTapEnabled() {

private JMAPRoute.Action handleVersionRoute(HttpServerRequest request) {
try {
return jmapRoutesHandlers.stream()
.flatMap(jmapRoutesHandler -> jmapRoutesHandler.routes(versionParser.parseRequestVersionHeader(request)))
Version version = versionParser.parseRequestVersionHeader(request);

return routes.get(version).stream()
.filter(jmapRoute -> jmapRoute.matches(request))
.map(JMAPRoute::getAction)
.findFirst()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ public class VersionParser {
public VersionParser(Set<Version> supportedVersions, JMAPConfiguration jmapConfiguration) {
this.jmapConfiguration = jmapConfiguration;
Preconditions.checkArgument(supportedVersions.contains(jmapConfiguration.getDefaultVersion()),
jmapConfiguration + " is not a supported JMAP version");
"%s is not a supported JMAP version", jmapConfiguration);

this.supportedVersions = supportedVersions;
}

public Set<Version> getSupportedVersions() {
return supportedVersions;
}

@VisibleForTesting
Version parse(String version) {
Preconditions.checkNotNull(version);
Expand Down