Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.james.jmap.methods.SetMailboxesCreationProcessor Maven / Gradle / Ivy
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.jmap.methods;
import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;
import org.apache.james.jmap.exceptions.MailboxNotOwnedException;
import org.apache.james.jmap.exceptions.MailboxParentNotFoundException;
import org.apache.james.jmap.model.MailboxCreationId;
import org.apache.james.jmap.model.MailboxFactory;
import org.apache.james.jmap.model.SetError;
import org.apache.james.jmap.model.SetMailboxesRequest;
import org.apache.james.jmap.model.SetMailboxesResponse;
import org.apache.james.jmap.model.mailbox.Mailbox;
import org.apache.james.jmap.model.mailbox.MailboxCreateRequest;
import org.apache.james.jmap.utils.DependencyGraph.CycleDetectedException;
import org.apache.james.jmap.utils.SortingHierarchicalCollections;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.SubscriptionManager;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.MailboxExistsException;
import org.apache.james.mailbox.exception.MailboxNameException;
import org.apache.james.mailbox.exception.MailboxNotFoundException;
import org.apache.james.mailbox.exception.TooLongMailboxNameException;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxId.Factory;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.metrics.api.MetricFactory;
import org.apache.james.metrics.api.TimeMetric;
import org.apache.james.util.OptionalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.fge.lambdas.Throwing;
import com.github.fge.lambdas.functions.FunctionChainer;
import com.google.common.annotations.VisibleForTesting;
public class SetMailboxesCreationProcessor implements SetMailboxesProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(SetMailboxesCreationProcessor.class);
private final MailboxManager mailboxManager;
private final SortingHierarchicalCollections, MailboxCreationId> sortingHierarchicalCollections;
private final MailboxFactory mailboxFactory;
private final Factory mailboxIdFactory;
private final SubscriptionManager subscriptionManager;
private final MetricFactory metricFactory;
@Inject
@VisibleForTesting
SetMailboxesCreationProcessor(MailboxManager mailboxManager, SubscriptionManager subscriptionManager, MailboxFactory mailboxFactory, Factory mailboxIdFactory, MetricFactory metricFactory) {
this.mailboxManager = mailboxManager;
this.subscriptionManager = subscriptionManager;
this.metricFactory = metricFactory;
this.sortingHierarchicalCollections =
new SortingHierarchicalCollections<>(
Map.Entry::getKey,
x -> x.getValue().getParentId());
this.mailboxFactory = mailboxFactory;
this.mailboxIdFactory = mailboxIdFactory;
}
@Override
public SetMailboxesResponse process(SetMailboxesRequest request, MailboxSession mailboxSession) {
TimeMetric timeMetric = metricFactory.timer(JMAP_PREFIX + "SetMailboxesCreationProcessor");
SetMailboxesResponse.Builder builder = SetMailboxesResponse.builder();
try {
Map creationIdsToCreatedMailboxId = new HashMap<>();
sortingHierarchicalCollections.sortFromRootToLeaf(request.getCreate().entrySet())
.forEach(entry ->
createMailbox(entry.getKey(), entry.getValue(), mailboxSession, creationIdsToCreatedMailboxId, builder));
} catch (CycleDetectedException e) {
markRequestsAsNotCreatedDueToCycle(request, builder);
}
timeMetric.stopAndPublish();
return builder.build();
}
private void markRequestsAsNotCreatedDueToCycle(SetMailboxesRequest request, SetMailboxesResponse.Builder builder) {
request.getCreate().entrySet()
.forEach(entry ->
builder.notCreated(entry.getKey(),
SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description("The created mailboxes introduce a cycle.")
.build()));
}
private void createMailbox(MailboxCreationId mailboxCreationId, MailboxCreateRequest mailboxRequest, MailboxSession mailboxSession,
Map creationIdsToCreatedMailboxId, SetMailboxesResponse.Builder builder) {
try {
ensureValidMailboxName(mailboxRequest, mailboxSession);
MailboxPath mailboxPath = computeMailboxPath(mailboxRequest, creationIdsToCreatedMailboxId, mailboxSession);
Optional mailboxId = mailboxManager.createMailbox(mailboxPath, mailboxSession);
Optional mailbox = mailboxId.flatMap(id -> mailboxFactory.builder()
.id(id)
.session(mailboxSession)
.build());
if (mailbox.isPresent()) {
subscriptionManager.subscribe(mailboxSession, mailboxPath.getName());
builder.created(mailboxCreationId, mailbox.get());
creationIdsToCreatedMailboxId.put(mailboxCreationId, mailbox.get().getId());
} else {
builder.notCreated(mailboxCreationId, SetError.builder()
.type(SetError.Type.ERROR)
.description("An error occurred when creating the mailbox")
.build());
}
} catch (TooLongMailboxNameException e) {
builder.notCreated(mailboxCreationId, SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description("The mailbox name length is too long")
.build());
} catch (MailboxNotOwnedException e) {
builder.notCreated(mailboxCreationId, SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description("The mailbox can not be created with a parent mailbox belonging to another user")
.build());
} catch (MailboxNameException | MailboxParentNotFoundException e) {
builder.notCreated(mailboxCreationId, SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description(e.getMessage())
.build());
} catch (MailboxExistsException e) {
String message = String.format("The mailbox '%s' already exists.", mailboxCreationId.getCreationId());
builder.notCreated(mailboxCreationId, SetError.builder()
.type(SetError.Type.INVALID_ARGUMENTS)
.description(message)
.build());
} catch (MailboxException e) {
String message = String.format("An error occurred when creating the mailbox '%s'", mailboxCreationId.getCreationId());
LOGGER.error(message, e);
builder.notCreated(mailboxCreationId, SetError.builder()
.type(SetError.Type.ERROR)
.description(message)
.build());
}
}
private void ensureValidMailboxName(MailboxCreateRequest mailboxRequest, MailboxSession mailboxSession) throws MailboxNameException {
String name = mailboxRequest.getName();
char pathDelimiter = mailboxSession.getPathDelimiter();
if (name.contains(String.valueOf(pathDelimiter))) {
throw new MailboxNameException(String.format("The mailbox '%s' contains an illegal character: '%c'", name, pathDelimiter));
}
}
private MailboxPath computeMailboxPath(MailboxCreateRequest mailboxRequest, Map creationIdsToCreatedMailboxId, MailboxSession mailboxSession) throws MailboxException {
if (mailboxRequest.getParentId().isPresent()) {
MailboxCreationId parentId = mailboxRequest.getParentId().get();
MailboxPath parentPath = getMailboxPath(creationIdsToCreatedMailboxId, mailboxSession, parentId);
assertBelongsToUser(parentPath, mailboxSession);
return MailboxPath.forUser(mailboxSession.getUser().getUserName(),
parentPath.getName() + mailboxSession.getPathDelimiter() + mailboxRequest.getName());
}
return MailboxPath.forUser(mailboxSession.getUser().getUserName(), mailboxRequest.getName());
}
private void assertBelongsToUser(MailboxPath mailboxPath, MailboxSession mailboxSession) throws MailboxNotOwnedException {
if (!mailboxPath.belongsTo(mailboxSession)) {
throw new MailboxNotOwnedException();
}
}
private MailboxPath getMailboxPath(Map creationIdsToCreatedMailboxId, MailboxSession mailboxSession, MailboxCreationId parentId) throws MailboxException {
Optional mailboxId = OptionalUtils.or(
readCreationIdAsMailboxId(parentId),
Optional.ofNullable(creationIdsToCreatedMailboxId.get(parentId)));
return getMailboxPathFromId(mailboxId, mailboxSession)
.orElseThrow(() -> new MailboxParentNotFoundException(parentId));
}
private Optional readCreationIdAsMailboxId(MailboxCreationId creationId) {
try {
return Optional.of(mailboxIdFactory.fromString(creationId.getCreationId()));
} catch (Exception e) {
return Optional.empty();
}
}
@VisibleForTesting
Optional getMailboxPathFromId(Optional mailboxId, MailboxSession mailboxSession) {
FunctionChainer> fromMailboxIdToMailboxPath = Throwing.function(id -> {
try {
return Optional.of(mailboxManager.getMailbox(id, mailboxSession).getMailboxPath());
} catch (MailboxNotFoundException e) {
return Optional.empty();
}
});
return mailboxId
.flatMap(fromMailboxIdToMailboxPath.sneakyThrow());
}
}