Java tutorial
/**************************************************************** * 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.model; import java.io.IOException; import java.io.InputStream; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TimeZone; import java.util.function.Function; import java.util.stream.Collectors; import javax.inject.Inject; import javax.mail.Flags; import javax.mail.internet.SharedInputStream; import org.apache.james.jmap.model.MessageContentExtractor.MessageContent; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.model.Cid; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MessageAttachment; import org.apache.james.mailbox.model.MessageMetaData; import org.apache.james.mailbox.model.MessageResult; import org.apache.james.mime4j.dom.address.AddressList; import org.apache.james.mime4j.dom.address.Mailbox; import org.apache.james.mime4j.dom.address.MailboxList; import org.apache.james.mime4j.message.MessageBuilder; import org.apache.james.mime4j.stream.Field; import com.github.steveash.guavate.Guavate; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimaps; public class MessageFactory { public static final ZoneId UTC_ZONE_ID = ZoneId.of("Z"); private final MessagePreviewGenerator messagePreview; private final MessageContentExtractor messageContentExtractor; @Inject public MessageFactory(MessagePreviewGenerator messagePreview, MessageContentExtractor messageContentExtractor) { this.messagePreview = messagePreview; this.messageContentExtractor = messageContentExtractor; } public Message fromMetaDataWithContent(MetaDataWithContent message) throws MailboxException { org.apache.james.mime4j.dom.Message mimeMessage = parse(message); MessageContent messageContent = extractContent(mimeMessage); return Message.builder().id(message.getMessageId()).blobId(BlobId.of(String.valueOf(message.getUid()))) .threadId(message.getMessageId().serialize()).mailboxIds(ImmutableList.of(message.getMailboxId())) .inReplyToMessageId(getHeader(mimeMessage, "in-reply-to")) .isUnread(!message.getFlags().contains(Flags.Flag.SEEN)) .isFlagged(message.getFlags().contains(Flags.Flag.FLAGGED)) .isAnswered(message.getFlags().contains(Flags.Flag.ANSWERED)) .isDraft(message.getFlags().contains(Flags.Flag.DRAFT)) .subject(Strings.nullToEmpty(mimeMessage.getSubject()).trim()) .headers(toMap(mimeMessage.getHeader().getFields())) .from(firstFromMailboxList(mimeMessage.getFrom())).to(fromAddressList(mimeMessage.getTo())) .cc(fromAddressList(mimeMessage.getCc())).bcc(fromAddressList(mimeMessage.getBcc())) .replyTo(fromAddressList(mimeMessage.getReplyTo())).size(message.getSize()) .date(message.getInternalDateAsZonedDateTime()).textBody(messageContent.getTextBody().orElse(null)) .htmlBody(messageContent.getHtmlBody().orElse(null)).preview(getPreview(messageContent)) .attachments(getAttachments(message.getAttachments())).build(); } private org.apache.james.mime4j.dom.Message parse(MetaDataWithContent message) throws MailboxException { try { return MessageBuilder.read(message.getContent()) .setDate(message.getInternalDate(), TimeZone.getTimeZone(UTC_ZONE_ID)).build(); } catch (IOException e) { throw new MailboxException("Unable to parse message: " + e.getMessage(), e); } } private MessageContent extractContent(org.apache.james.mime4j.dom.Message mimeMessage) throws MailboxException { try { return messageContentExtractor.extract(mimeMessage); } catch (IOException e) { throw new MailboxException("Unable to extract content: " + e.getMessage(), e); } } private String getPreview(MessageContent messageContent) { if (messageContent.getHtmlBody().isPresent()) { return messagePreview.forHTMLBody(messageContent.getHtmlBody()); } return messagePreview.forTextBody(messageContent.getTextBody()); } private Emailer firstFromMailboxList(MailboxList list) { if (list == null) { return null; } return list.stream().map(this::fromMailbox).findFirst().orElse(null); } private ImmutableList<Emailer> fromAddressList(AddressList list) { if (list == null) { return ImmutableList.of(); } return list.flatten().stream().map(this::fromMailbox).collect(Guavate.toImmutableList()); } private Emailer fromMailbox(Mailbox mailbox) { return Emailer.builder().name(getNameOrAddress(mailbox)).email(mailbox.getAddress()).build(); } private String getNameOrAddress(Mailbox mailbox) { if (mailbox.getName() != null) { return mailbox.getName(); } return mailbox.getAddress(); } private ImmutableMap<String, String> toMap(List<Field> fields) { Function<Entry<String, Collection<Field>>, String> bodyConcatenator = fieldListEntry -> fieldListEntry .getValue().stream().map(Field::getBody).collect(Collectors.toList()).stream() .collect(Collectors.joining(",")); return Multimaps.index(fields, Field::getName).asMap().entrySet().stream() .collect(Guavate.toImmutableMap(Map.Entry::getKey, bodyConcatenator)); } private String getHeader(org.apache.james.mime4j.dom.Message message, String header) { Field field = message.getHeader().getField(header); if (field == null) { return null; } return field.getBody(); } private List<Attachment> getAttachments(List<MessageAttachment> attachments) { return attachments.stream().map(this::fromMailboxAttachment).collect(Guavate.toImmutableList()); } private Attachment fromMailboxAttachment(MessageAttachment attachment) { return Attachment.builder().blobId(BlobId.of(attachment.getAttachmentId().getId())) .type(attachment.getAttachment().getType()).size(attachment.getAttachment().getSize()) .name(attachment.getName().orNull()).cid(attachment.getCid().transform(Cid::getValue).orNull()) .isInline(attachment.isInline()).build(); } public static class MetaDataWithContent implements MessageMetaData { public static Builder builder() { return new Builder(); } public static Builder builderFromMessageResult(MessageResult messageResult) throws MailboxException { Builder builder = builder().uid(messageResult.getUid()).modSeq(messageResult.getModSeq()) .flags(messageResult.getFlags()).size(messageResult.getSize()) .internalDate(messageResult.getInternalDate()).attachments(messageResult.getAttachments()); try { return builder.content(messageResult.getFullContent().getInputStream()); } catch (IOException e) { throw new MailboxException("Can't get message full content: " + e.getMessage(), e); } } public static class Builder { private Long uid; private Long modSeq; private Flags flags; private Long size; private Date internalDate; private InputStream content; private SharedInputStream sharedContent; private List<MessageAttachment> attachments; private MailboxId mailboxId; private MessageId messageId; public Builder uid(long uid) { this.uid = uid; return this; } public Builder modSeq(long modSeq) { this.modSeq = modSeq; return this; } public Builder flags(Flags flags) { this.flags = flags; return this; } public Builder size(long size) { this.size = size; return this; } public Builder internalDate(Date internalDate) { this.internalDate = internalDate; return this; } public Builder content(InputStream content) { this.content = content; return this; } public Builder sharedContent(SharedInputStream sharedContent) { this.sharedContent = sharedContent; return this; } public Builder attachments(List<MessageAttachment> attachments) { this.attachments = attachments; return this; } public Builder mailboxId(MailboxId mailboxId) { this.mailboxId = mailboxId; return this; } public Builder messageId(MessageId messageId) { this.messageId = messageId; return this; } public MetaDataWithContent build() { Preconditions.checkArgument(uid != null); if (modSeq == null) { modSeq = -1L; } Preconditions.checkArgument(flags != null); Preconditions.checkArgument(size != null); Preconditions.checkArgument(internalDate != null); Preconditions.checkArgument(content != null ^ sharedContent != null); Preconditions.checkArgument(attachments != null); Preconditions.checkArgument(mailboxId != null); Preconditions.checkArgument(messageId != null); return new MetaDataWithContent(uid, modSeq, flags, size, internalDate, content, sharedContent, attachments, mailboxId, messageId); } } private final long uid; private final long modSeq; private final Flags flags; private final long size; private final Date internalDate; private final InputStream content; private final SharedInputStream sharedContent; private final List<MessageAttachment> attachments; private final MailboxId mailboxId; private final MessageId messageId; private MetaDataWithContent(long uid, long modSeq, Flags flags, long size, Date internalDate, InputStream content, SharedInputStream sharedContent, List<MessageAttachment> attachments, MailboxId mailboxId, MessageId messageId) { this.uid = uid; this.modSeq = modSeq; this.flags = flags; this.size = size; this.internalDate = internalDate; this.content = content; this.sharedContent = sharedContent; this.attachments = attachments; this.mailboxId = mailboxId; this.messageId = messageId; } @Override public long getUid() { return uid; } @Override public long getModSeq() { return modSeq; } @Override public Flags getFlags() { return flags; } @Override public long getSize() { return size; } @Override public Date getInternalDate() { return internalDate; } public ZonedDateTime getInternalDateAsZonedDateTime() { return ZonedDateTime.ofInstant(internalDate.toInstant(), UTC_ZONE_ID); } public InputStream getContent() { if (sharedContent != null) { long begin = 0; long allContent = -1; return sharedContent.newStream(begin, allContent); } return content; } public List<MessageAttachment> getAttachments() { return attachments; } public MailboxId getMailboxId() { return mailboxId; } public MessageId getMessageId() { return messageId; } } }