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.mailbox.jpa.mail.JPAMessageMapper 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.mailbox.jpa.mail;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.mail.Flags;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.ModSeq;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.jpa.JPAId;
import org.apache.james.mailbox.jpa.JPATransactionalMapper;
import org.apache.james.mailbox.jpa.mail.MessageUtils.MessageChangedFlags;
import org.apache.james.mailbox.jpa.mail.model.JPAMailbox;
import org.apache.james.mailbox.jpa.mail.model.openjpa.AbstractJPAMailboxMessage;
import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAEncryptedMailboxMessage;
import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMailboxMessage;
import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAStreamingMailboxMessage;
import org.apache.james.mailbox.model.Mailbox;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MessageRange.Type;
import org.apache.james.mailbox.model.UpdatedFlags;
import org.apache.james.mailbox.store.FlagsUpdateCalculator;
import org.apache.james.mailbox.store.mail.MessageMapper;
import org.apache.james.mailbox.store.mail.ModSeqProvider;
import org.apache.james.mailbox.store.mail.UidProvider;
import org.apache.james.mailbox.store.mail.model.MailboxMessage;
import org.apache.james.mailbox.store.mail.utils.ApplicableFlagCalculator;
import org.apache.openjpa.persistence.ArgumentException;
import com.github.fge.lambdas.Throwing;
import com.github.steveash.guavate.Guavate;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
/**
* JPA implementation of a {@link MessageMapper}. This class is not thread-safe!
*/
public class JPAMessageMapper extends JPATransactionalMapper implements MessageMapper {
private static final int UNLIMIT_MAX_SIZE = -1;
private static final int UNLIMITED = -1;
private final MessageUtils messageMetadataMapper;
private final UidProvider uidProvider;
private final ModSeqProvider modSeqProvider;
public JPAMessageMapper(UidProvider uidProvider, ModSeqProvider modSeqProvider, EntityManagerFactory entityManagerFactory) {
super(entityManagerFactory);
this.messageMetadataMapper = new MessageUtils(uidProvider, modSeqProvider);
this.uidProvider = uidProvider;
this.modSeqProvider = modSeqProvider;
}
@Override
public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException {
return MailboxCounters.builder()
.mailboxId(mailbox.getMailboxId())
.count(countMessagesInMailbox(mailbox))
.unseen(countUnseenMessagesInMailbox(mailbox))
.build();
}
@Override
public Flux listAllMessageUids(Mailbox mailbox) {
return findInMailboxReactive(mailbox, MessageRange.all(), FetchType.Metadata, UNLIMITED)
.map(MailboxMessage::getUid);
}
@Override
public Iterator findInMailbox(Mailbox mailbox, MessageRange set, FetchType fType, int max)
throws MailboxException {
return findAsList(mailbox.getMailboxId(), set, max).iterator();
}
private List findAsList(MailboxId mailboxId, MessageRange set, int max) throws MailboxException {
try {
MessageUid from = set.getUidFrom();
MessageUid to = set.getUidTo();
Type type = set.getType();
JPAId jpaId = (JPAId) mailboxId;
switch (type) {
default:
case ALL:
return findMessagesInMailbox(jpaId, max);
case FROM:
return findMessagesInMailboxAfterUID(jpaId, from, max);
case ONE:
return findMessagesInMailboxWithUID(jpaId, from);
case RANGE:
return findMessagesInMailboxBetweenUIDs(jpaId, from, to, max);
}
} catch (PersistenceException e) {
throw new MailboxException("Search of MessageRange " + set + " failed in mailbox " + mailboxId.serialize(), e);
}
}
@Override
public long countMessagesInMailbox(Mailbox mailbox) throws MailboxException {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
return countMessagesInMailbox(mailboxId);
}
private long countMessagesInMailbox(JPAId mailboxId) throws MailboxException {
try {
return (Long) getEntityManager().createNamedQuery("countMessagesInMailbox")
.setParameter("idParam", mailboxId.getRawId()).getSingleResult();
} catch (PersistenceException e) {
throw new MailboxException("Count of messages failed in mailbox " + mailboxId, e);
}
}
public long countUnseenMessagesInMailbox(Mailbox mailbox) throws MailboxException {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
return countUnseenMessagesInMailbox(mailboxId);
}
private long countUnseenMessagesInMailbox(JPAId mailboxId) throws MailboxException {
try {
return (Long) getEntityManager().createNamedQuery("countUnseenMessagesInMailbox")
.setParameter("idParam", mailboxId.getRawId()).getSingleResult();
} catch (PersistenceException e) {
throw new MailboxException("Count of useen messages failed in mailbox " + mailboxId, e);
}
}
@Override
public void delete(Mailbox mailbox, MailboxMessage message) throws MailboxException {
try {
AbstractJPAMailboxMessage jpaMessage = getEntityManager().find(AbstractJPAMailboxMessage.class, buildKey(mailbox, message));
getEntityManager().remove(jpaMessage);
} catch (PersistenceException e) {
throw new MailboxException("Delete of message " + message + " failed in mailbox " + mailbox, e);
}
}
private AbstractJPAMailboxMessage.MailboxIdUidKey buildKey(Mailbox mailbox, MailboxMessage message) {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
AbstractJPAMailboxMessage.MailboxIdUidKey key = new AbstractJPAMailboxMessage.MailboxIdUidKey();
key.mailbox = mailboxId.getRawId();
key.uid = message.getUid().asLong();
return key;
}
@Override
@SuppressWarnings("unchecked")
public MessageUid findFirstUnseenMessageUid(Mailbox mailbox) throws MailboxException {
try {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
Query query = getEntityManager().createNamedQuery("findUnseenMessagesInMailboxOrderByUid").setParameter(
"idParam", mailboxId.getRawId());
query.setMaxResults(1);
List result = query.getResultList();
if (result.isEmpty()) {
return null;
} else {
return result.get(0).getUid();
}
} catch (PersistenceException e) {
throw new MailboxException("Search of first unseen message failed in mailbox " + mailbox, e);
}
}
@Override
@SuppressWarnings("unchecked")
public List findRecentMessageUidsInMailbox(Mailbox mailbox) throws MailboxException {
try {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
Query query = getEntityManager().createNamedQuery("findRecentMessageUidsInMailbox").setParameter("idParam",
mailboxId.getRawId());
List resultList = query.getResultList();
ImmutableList.Builder results = ImmutableList.builder();
for (long result: resultList) {
results.add(MessageUid.of(result));
}
return results.build();
} catch (PersistenceException e) {
throw new MailboxException("Search of recent messages failed in mailbox " + mailbox, e);
}
}
@Override
public List retrieveMessagesMarkedForDeletion(Mailbox mailbox, MessageRange messageRange) throws MailboxException {
try {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
List messages = findDeletedMessages(messageRange, mailboxId);
return getUidList(messages);
} catch (PersistenceException e) {
throw new MailboxException("Search of MessageRange " + messageRange + " failed in mailbox " + mailbox, e);
}
}
private List findDeletedMessages(MessageRange messageRange, JPAId mailboxId) {
MessageUid from = messageRange.getUidFrom();
MessageUid to = messageRange.getUidTo();
switch (messageRange.getType()) {
case ONE:
return findDeletedMessagesInMailboxWithUID(mailboxId, from);
case RANGE:
return findDeletedMessagesInMailboxBetweenUIDs(mailboxId, from, to);
case FROM:
return findDeletedMessagesInMailboxAfterUID(mailboxId, from);
case ALL:
return findDeletedMessagesInMailbox(mailboxId);
default:
throw new RuntimeException("Cannot find deleted messages, range type " + messageRange.getType() + " doesn't exist");
}
}
@Override
public Map deleteMessages(Mailbox mailbox, List uids) throws MailboxException {
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
Map data = new HashMap<>();
List ranges = MessageRange.toRanges(uids);
ranges.forEach(Throwing.consumer(range -> {
List messages = findAsList(mailboxId, range, JPAMessageMapper.UNLIMITED);
data.putAll(createMetaData(messages));
deleteMessages(range, mailboxId);
}).sneakyThrow());
return data;
}
private void deleteMessages(MessageRange messageRange, JPAId mailboxId) {
MessageUid from = messageRange.getUidFrom();
MessageUid to = messageRange.getUidTo();
switch (messageRange.getType()) {
case ONE:
deleteMessagesInMailboxWithUID(mailboxId, from);
break;
case RANGE:
deleteMessagesInMailboxBetweenUIDs(mailboxId, from, to);
break;
case FROM:
deleteMessagesInMailboxAfterUID(mailboxId, from);
break;
case ALL:
deleteMessagesInMailbox(mailboxId);
break;
default:
throw new RuntimeException("Cannot delete messages, range type " + messageRange.getType() + " doesn't exist");
}
}
@Override
public MessageMetaData move(Mailbox mailbox, MailboxMessage original) throws MailboxException {
JPAId originalMailboxId = (JPAId) original.getMailboxId();
JPAMailbox originalMailbox = getEntityManager().find(JPAMailbox.class, originalMailboxId.getRawId());
MessageMetaData messageMetaData = copy(mailbox, original);
delete(originalMailbox.toMailbox(), original);
return messageMetaData;
}
@Override
public MessageMetaData add(Mailbox mailbox, MailboxMessage message) throws MailboxException {
messageMetadataMapper.enrichMessage(mailbox, message);
return save(mailbox, message);
}
@Override
public Iterator updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagsUpdateCalculator,
MessageRange set) throws MailboxException {
Iterator messages = findInMailbox(mailbox, set, FetchType.Metadata, UNLIMIT_MAX_SIZE);
MessageChangedFlags messageChangedFlags = messageMetadataMapper.updateFlags(mailbox, flagsUpdateCalculator, messages);
for (MailboxMessage mailboxMessage : messageChangedFlags.getChangedFlags()) {
save(mailbox, mailboxMessage);
}
return messageChangedFlags.getUpdatedFlags();
}
@Override
public MessageMetaData copy(Mailbox mailbox, MailboxMessage original) throws MailboxException {
return copy(mailbox, uidProvider.nextUid(mailbox), modSeqProvider.nextModSeq(mailbox), original);
}
@Override
public Optional getLastUid(Mailbox mailbox) throws MailboxException {
return uidProvider.lastUid(mailbox);
}
@Override
public ModSeq getHighestModSeq(Mailbox mailbox) throws MailboxException {
return modSeqProvider.highestModSeq(mailbox);
}
@Override
public Flags getApplicableFlag(Mailbox mailbox) throws MailboxException {
int maxBatchSize = -1;
return new ApplicableFlagCalculator(findMessagesInMailbox((JPAId) mailbox.getMailboxId(), maxBatchSize))
.computeApplicableFlags();
}
private MessageMetaData copy(Mailbox mailbox, MessageUid uid, ModSeq modSeq, MailboxMessage original)
throws MailboxException {
MailboxMessage copy;
JPAMailbox currentMailbox = JPAMailbox.from(mailbox);
if (original instanceof JPAStreamingMailboxMessage) {
copy = new JPAStreamingMailboxMessage(currentMailbox, uid, modSeq, original);
} else if (original instanceof JPAEncryptedMailboxMessage) {
copy = new JPAEncryptedMailboxMessage(currentMailbox, uid, modSeq, original);
} else {
copy = new JPAMailboxMessage(currentMailbox, uid, modSeq, original);
}
return save(mailbox, copy);
}
/**
* @see org.apache.james.mailbox.store.mail.AbstractMessageMapper#save(Mailbox, MailboxMessage)
*/
protected MessageMetaData save(Mailbox mailbox, MailboxMessage message) throws MailboxException {
try {
// We need to reload a "JPA attached" mailbox, because the provide
// mailbox is already "JPA detached"
// If we don't this, we will get an
// org.apache.openjpa.persistence.ArgumentException.
JPAId mailboxId = (JPAId) mailbox.getMailboxId();
JPAMailbox currentMailbox = getEntityManager().find(JPAMailbox.class, mailboxId.getRawId());
if (message instanceof AbstractJPAMailboxMessage) {
((AbstractJPAMailboxMessage) message).setMailbox(currentMailbox);
getEntityManager().persist(message);
return message.metaData();
} else {
JPAMailboxMessage persistData = new JPAMailboxMessage(currentMailbox, message.getUid(), message.getModSeq(), message);
persistData.setFlags(message.createFlags());
getEntityManager().persist(persistData);
return persistData.metaData();
}
} catch (PersistenceException | ArgumentException e) {
throw new MailboxException("Save of message " + message + " failed in mailbox " + mailbox, e);
}
}
@SuppressWarnings("unchecked")
private List findMessagesInMailboxAfterUID(JPAId mailboxId, MessageUid from, int batchSize) {
Query query = getEntityManager().createNamedQuery("findMessagesInMailboxAfterUID")
.setParameter("idParam", mailboxId.getRawId()).setParameter("uidParam", from.asLong());
if (batchSize > 0) {
query.setMaxResults(batchSize);
}
return query.getResultList();
}
@SuppressWarnings("unchecked")
private List findMessagesInMailboxWithUID(JPAId mailboxId, MessageUid from) {
return getEntityManager().createNamedQuery("findMessagesInMailboxWithUID")
.setParameter("idParam", mailboxId.getRawId()).setParameter("uidParam", from.asLong()).setMaxResults(1)
.getResultList();
}
@SuppressWarnings("unchecked")
private List findMessagesInMailboxBetweenUIDs(JPAId mailboxId, MessageUid from, MessageUid to,
int batchSize) {
Query query = getEntityManager().createNamedQuery("findMessagesInMailboxBetweenUIDs")
.setParameter("idParam", mailboxId.getRawId()).setParameter("fromParam", from.asLong())
.setParameter("toParam", to.asLong());
if (batchSize > 0) {
query.setMaxResults(batchSize);
}
return query.getResultList();
}
@SuppressWarnings("unchecked")
private List findMessagesInMailbox(JPAId mailboxId, int batchSize) {
Query query = getEntityManager().createNamedQuery("findMessagesInMailbox").setParameter("idParam",
mailboxId.getRawId());
if (batchSize > 0) {
query.setMaxResults(batchSize);
}
return query.getResultList();
}
private Map createMetaData(List uids) {
final Map data = new HashMap<>();
for (MailboxMessage m : uids) {
data.put(m.getUid(), m.metaData());
}
return data;
}
private List getUidList(List messages) {
return messages.stream()
.map(message -> message.getUid())
.collect(Guavate.toImmutableList());
}
private int deleteMessagesInMailbox(JPAId mailboxId) {
return getEntityManager().createNamedQuery("deleteMessagesInMailbox")
.setParameter("idParam", mailboxId.getRawId()).executeUpdate();
}
private int deleteMessagesInMailboxAfterUID(JPAId mailboxId, MessageUid from) {
return getEntityManager().createNamedQuery("deleteMessagesInMailboxAfterUID")
.setParameter("idParam", mailboxId.getRawId()).setParameter("uidParam", from.asLong()).executeUpdate();
}
private int deleteMessagesInMailboxWithUID(JPAId mailboxId, MessageUid from) {
return getEntityManager().createNamedQuery("deleteMessagesInMailboxWithUID")
.setParameter("idParam", mailboxId.getRawId()).setParameter("uidParam", from.asLong()).executeUpdate();
}
private int deleteMessagesInMailboxBetweenUIDs(JPAId mailboxId, MessageUid from, MessageUid to) {
return getEntityManager().createNamedQuery("deleteMessagesInMailboxBetweenUIDs")
.setParameter("idParam", mailboxId.getRawId()).setParameter("fromParam", from.asLong())
.setParameter("toParam", to.asLong()).executeUpdate();
}
@SuppressWarnings("unchecked")
private List findDeletedMessagesInMailbox(JPAId mailboxId) {
return getEntityManager().createNamedQuery("findDeletedMessagesInMailbox")
.setParameter("idParam", mailboxId.getRawId()).getResultList();
}
@SuppressWarnings("unchecked")
private List findDeletedMessagesInMailboxAfterUID(JPAId mailboxId, MessageUid from) {
return getEntityManager().createNamedQuery("findDeletedMessagesInMailboxAfterUID")
.setParameter("idParam", mailboxId.getRawId()).setParameter("uidParam", from.asLong()).getResultList();
}
@SuppressWarnings("unchecked")
private List findDeletedMessagesInMailboxWithUID(JPAId mailboxId, MessageUid from) {
return getEntityManager().createNamedQuery("findDeletedMessagesInMailboxWithUID")
.setParameter("idParam", mailboxId.getRawId()).setParameter("uidParam", from.asLong()).setMaxResults(1)
.getResultList();
}
@SuppressWarnings("unchecked")
private List findDeletedMessagesInMailboxBetweenUIDs(JPAId mailboxId, MessageUid from, MessageUid to) {
return getEntityManager().createNamedQuery("findDeletedMessagesInMailboxBetweenUIDs")
.setParameter("idParam", mailboxId.getRawId()).setParameter("fromParam", from.asLong())
.setParameter("toParam", to.asLong()).getResultList();
}
}