org.apache.james.mailbox.jcr.mail.model.JCRMessage.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.james.mailbox.jcr.mail.model.JCRMessage.java

Source

/****************************************************************
 * 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.jcr.mail.model;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.jcr.Binary;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.mail.Flags;
import javax.mail.internet.SharedInputStream;
import javax.mail.util.SharedByteArrayInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BoundedInputStream;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.jcr.JCRImapConstants;
import org.apache.james.mailbox.jcr.Persistent;
import org.apache.james.mailbox.store.mail.model.AbstractMessage;
import org.apache.james.mailbox.store.mail.model.Message;
import org.apache.james.mailbox.store.mail.model.Property;
import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
import org.slf4j.Logger;

/**
 * JCR implementation of {@link Message}
 *
 */
public class JCRMessage extends AbstractMessage<String> implements JCRImapConstants, Persistent {

    private Node node;
    private final Logger logger;
    private SharedInputStream content;
    private String mediaType;
    private Long textualLineCount;
    private String subType;
    private List<JCRProperty> properties;
    private int bodyStartOctet;

    private String mailboxUUID;
    private long uid;
    private Date internalDate;
    private long size;
    private boolean answered;
    private boolean deleted;
    private boolean draft;
    private boolean flagged;
    private boolean recent;
    private boolean seen;
    private String[] userFlags;
    private long modSeq;

    private static final String TOSTRING_SEPARATOR = " ";

    public final static String MAILBOX_UUID_PROPERTY = "jamesMailbox:mailboxUUID";
    public final static String UID_PROPERTY = "jamesMailbox:uid";
    public final static String SIZE_PROPERTY = "jamesMailbox:size";
    public final static String ANSWERED_PROPERTY = "jamesMailbox:answered";
    public final static String DELETED_PROPERTY = "jamesMailbox:deleted";
    public final static String DRAFT_PROPERTY = "jamesMailbox:draft";
    public final static String FLAGGED_PROPERTY = "jamesMailbox:flagged";
    public final static String USERFLAGS_PROPERTY = "jamesMailbox:userFlags";

    public final static String RECENT_PROPERTY = "jamesMailbox:recent";
    public final static String SEEN_PROPERTY = "jamesMailbox:seen";
    public final static String INTERNAL_DATE_PROPERTY = "jamesMailbox:internalDate";

    public final static String BODY_START_OCTET_PROPERTY = "jamesMailbox:messageBodyStartOctet";
    public final static String HEADER_NODE_TYPE = "jamesMailbox:messageHeader";

    public final static String PROPERTY_NODE_TYPE = "jamesMailbox:messageProperty";
    public final static String TEXTUAL_LINE_COUNT_PROPERTY = "jamesMailbox:messageTextualLineCount";
    public final static String SUBTYPE_PROPERTY = "jamesMailbox:messageSubType";
    public final static String MODSEQ_PROPERTY = "jamesMailbox:modSeq";

    public JCRMessage(Node node, Logger logger) {
        this.logger = logger;
        this.node = node;
    }

    public JCRMessage(String mailboxUUID, Date internalDate, int size, Flags flags, SharedInputStream content,
            int bodyStartOctet, final PropertyBuilder propertyBuilder, Logger logger) {
        super();
        this.mailboxUUID = mailboxUUID;
        this.internalDate = internalDate;
        this.size = size;
        this.logger = logger;
        setFlags(flags);
        this.content = content;

        this.bodyStartOctet = bodyStartOctet;
        this.textualLineCount = propertyBuilder.getTextualLineCount();
        this.mediaType = propertyBuilder.getMediaType();
        this.subType = propertyBuilder.getSubType();
        final List<Property> properties = propertyBuilder.toProperties();
        this.properties = new ArrayList<JCRProperty>(properties.size());
        for (final Property property : properties) {
            this.properties.add(new JCRProperty(property, logger));
        }

    }

    /**
     * Create a copy of the given message
     * 
     * @param message
     * @throws IOException 
     */
    public JCRMessage(String mailboxUUID, long uid, long modSeq, JCRMessage message, Logger logger)
            throws MailboxException {
        this.mailboxUUID = mailboxUUID;
        this.internalDate = message.getInternalDate();
        this.size = message.getFullContentOctets();
        setFlags(message.createFlags());
        this.uid = uid;
        this.modSeq = modSeq;
        this.logger = logger;
        try {
            this.content = new SharedByteArrayInputStream(IOUtils.toByteArray(message.getFullContent()));
        } catch (IOException e) {
            throw new MailboxException("Unable to parse message", e);
        }

        this.bodyStartOctet = (int) (message.getFullContentOctets() - message.getBodyOctets());

        PropertyBuilder pBuilder = new PropertyBuilder(message.getProperties());
        this.textualLineCount = message.getTextualLineCount();
        this.mediaType = message.getMediaType();
        this.subType = message.getSubType();
        final List<Property> properties = pBuilder.toProperties();
        this.properties = new ArrayList<JCRProperty>(properties.size());
        for (final Property property : properties) {
            this.properties.add(new JCRProperty(property, logger));
        }
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Document#getFullContentOctets()
     */
    public long getFullContentOctets() {
        if (isPersistent()) {
            try {
                return node.getProperty(SIZE_PROPERTY).getLong();
            } catch (RepositoryException e) {
                logger.error("Unable to retrieve property " + SIZE_PROPERTY, e);

            }
            return 0;
        }
        return size;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Document#getMediaType()
     */
    public String getMediaType() {
        if (isPersistent()) {
            try {
                return node.getNode(JcrConstants.JCR_CONTENT).getProperty(JcrConstants.JCR_MIMETYPE).getString();
            } catch (RepositoryException e) {
                logger.error("Unable to retrieve node " + JcrConstants.JCR_MIMETYPE, e);
            }
            return null;
        }
        return mediaType;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Document#getProperties()
     */
    public List<Property> getProperties() {
        if (isPersistent()) {
            try {
                List<Property> properties = new ArrayList<Property>();
                NodeIterator nodeIt = node.getNodes("messageProperty");
                while (nodeIt.hasNext()) {
                    properties.add(new JCRProperty(nodeIt.nextNode(), logger));
                }
                return properties;
            } catch (RepositoryException e) {
                logger.error("Unable to retrieve nodes messageProperty", e);
            }
        }
        return new ArrayList<Property>(properties);
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Document#getSubType()
     */
    public String getSubType() {
        if (isPersistent()) {
            try {
                return node.getProperty(SUBTYPE_PROPERTY).getString();
            } catch (RepositoryException e) {
                logger.error("Unable to retrieve node " + SUBTYPE_PROPERTY, e);
            }
            return null;
        }
        return subType;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Document#getTextualLineCount()
     */
    public Long getTextualLineCount() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(TEXTUAL_LINE_COUNT_PROPERTY)) {
                    return node.getProperty(TEXTUAL_LINE_COUNT_PROPERTY).getLong();
                }
            } catch (RepositoryException e) {
                logger.error("Unable to retrieve property " + TEXTUAL_LINE_COUNT_PROPERTY, e);

            }
            return null;
        }
        return textualLineCount;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.jcr.Persistent#getNode()
     */
    public Node getNode() {
        return node;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.jcr.Persistent#isPersistent()
     */
    public boolean isPersistent() {
        return node != null;
    }

    public String getUUID() {
        if (isPersistent()) {
            try {
                return node.getIdentifier();
            } catch (RepositoryException e) {
                logger.error("Unable to access UUID", e);
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.jcr.Persistent#merge(javax.jcr.Node)
     */
    public void merge(Node node) throws RepositoryException, IOException {

        // update the flags 
        node.setProperty(ANSWERED_PROPERTY, isAnswered());
        node.setProperty(DELETED_PROPERTY, isDeleted());
        node.setProperty(DRAFT_PROPERTY, isDraft());
        node.setProperty(FLAGGED_PROPERTY, isFlagged());
        node.setProperty(RECENT_PROPERTY, isRecent());
        node.setProperty(SEEN_PROPERTY, isSeen());
        node.setProperty(USERFLAGS_PROPERTY, createFlags().getUserFlags());
        // This stuff is only ever changed on a new message
        // so if it is persistent we don'T need to set all the of this.
        //
        // This also fix https://issues.apache.org/jira/browse/IMAP-159
        if (isPersistent() == false) {
            node.setProperty(SIZE_PROPERTY, getFullContentOctets());
            node.setProperty(MAILBOX_UUID_PROPERTY, getMailboxId());
            node.setProperty(UID_PROPERTY, getUid());
            node.setProperty(MODSEQ_PROPERTY, getModSeq());

            if (getInternalDate() == null) {
                internalDate = new Date();
            }

            Calendar cal = Calendar.getInstance();

            cal.setTime(getInternalDate());
            node.setProperty(INTERNAL_DATE_PROPERTY, cal);

            Node contentNode = JcrUtils.getOrAddNode(node, JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE);
            Binary binaryContent = contentNode.getSession().getValueFactory().createBinary(getFullContent());
            contentNode.setProperty(JcrConstants.JCR_DATA, binaryContent);
            contentNode.setProperty(JcrConstants.JCR_MIMETYPE, getMediaType());

            if (getTextualLineCount() != null) {
                node.setProperty(TEXTUAL_LINE_COUNT_PROPERTY, getTextualLineCount());
            }
            node.setProperty(SUBTYPE_PROPERTY, getSubType());
            node.setProperty(BODY_START_OCTET_PROPERTY, getBodyStartOctet());

            List<Property> currentProperties = getProperties();
            List<Property> newProperites = new ArrayList<Property>();
            for (int i = 0; i < currentProperties.size(); i++) {
                Property prop = currentProperties.get(i);
                newProperites.add(new JCRProperty(prop, logger));
            }
            // remove old properties, we will add a bunch of new ones
            NodeIterator iterator = node.getNodes("messageProperty");
            while (iterator.hasNext()) {
                iterator.nextNode().remove();
            }

            // store new properties
            for (int i = 0; i < newProperites.size(); i++) {
                JCRProperty prop = (JCRProperty) newProperites.get(i);
                Node propNode = node.addNode("messageProperty", "nt:unstructured");
                propNode.addMixin(PROPERTY_NODE_TYPE);
                prop.merge(propNode);
            }
        }
        this.node = node;

    }

    @Override
    protected String[] createUserFlags() {
        return userFlags;
    }

    @Override
    protected int getBodyStartOctet() {
        if (isPersistent()) {
            try {
                return (int) node.getProperty(BODY_START_OCTET_PROPERTY).getLong();
            } catch (RepositoryException e) {
                logger.error("Unable to retrieve property " + TEXTUAL_LINE_COUNT_PROPERTY, e);

            }
            return 0;
        }
        return bodyStartOctet;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;

        final JCRMessage other = (JCRMessage) obj;

        if (getUUID() != null) {
            if (!getUUID().equals(other.getUUID()))
                return false;
        } else {
            if (other.getUUID() != null)
                return false;
        }
        if (getMailboxId() != null) {
            if (!getMailboxId().equals(other.getMailboxId()))
                return false;
        } else {
            if (other.getMailboxId() != null)
                return false;
        }
        if (getId() != null) {
            if (!getId().equals(other.getId()))
                return false;
        } else {
            if (other.getId() != null)
                return false;
        }
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.apache.james.mailbox.store.mail.model.MailboxMembership#getInternalDate
     * ()
     */
    public Date getInternalDate() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(INTERNAL_DATE_PROPERTY)) {
                    return node.getProperty(INTERNAL_DATE_PROPERTY).getDate().getTime();
                }

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + FLAGGED_PROPERTY, e);
            }
            return null;
        }
        return internalDate;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.apache.james.mailbox.store.mail.model.MailboxMembership#getMailboxId()
     */
    public String getMailboxId() {
        if (isPersistent()) {
            try {
                return node.getProperty(MAILBOX_UUID_PROPERTY).getString();
            } catch (RepositoryException e) {
                logger.error("Unable to access property " + MAILBOX_UUID_PROPERTY, e);
            }
        }
        return mailboxUUID;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.james.mailbox.store.mail.model.MailboxMembership#getUid()
     */
    public long getUid() {
        if (isPersistent()) {
            try {
                return node.getProperty(UID_PROPERTY).getLong();

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + UID_PROPERTY, e);
            }
            return 0;
        }
        return uid;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.apache.james.mailbox.store.mail.model.MailboxMembership#isAnswered()
     */
    public boolean isAnswered() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(ANSWERED_PROPERTY)) {
                    return node.getProperty(ANSWERED_PROPERTY).getBoolean();
                }

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + ANSWERED_PROPERTY, e);
            }
            return false;
        }
        return answered;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.james.mailbox.store.mail.model.MailboxMembership#isDeleted()
     */
    public boolean isDeleted() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(DELETED_PROPERTY)) {
                    return node.getProperty(DELETED_PROPERTY).getBoolean();
                }

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + DELETED_PROPERTY, e);
            }
            return false;
        }
        return deleted;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.james.mailbox.store.mail.model.MailboxMembership#isDraft()
     */
    public boolean isDraft() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(DRAFT_PROPERTY)) {
                    return node.getProperty(DRAFT_PROPERTY).getBoolean();
                }
            } catch (RepositoryException e) {
                logger.error("Unable to access property " + DRAFT_PROPERTY, e);
            }
            return false;
        }
        return draft;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.james.mailbox.store.mail.model.MailboxMembership#isFlagged()
     */
    public boolean isFlagged() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(FLAGGED_PROPERTY)) {
                    return node.getProperty(FLAGGED_PROPERTY).getBoolean();
                }
            } catch (RepositoryException e) {
                logger.error("Unable to access property " + FLAGGED_PROPERTY, e);
            }
            return false;
        }
        return flagged;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.james.mailbox.store.mail.model.MailboxMembership#isRecent()
     */
    public boolean isRecent() {
        if (isPersistent()) {
            try {
                if (node.hasProperty(RECENT_PROPERTY)) {
                    return node.getProperty(RECENT_PROPERTY).getBoolean();
                }
            } catch (RepositoryException e) {
                logger.error("Unable to access property " + RECENT_PROPERTY, e);
            }
            return false;
        }
        return recent;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.james.mailbox.store.mail.model.MailboxMembership#isSeen()
     */
    public boolean isSeen() {
        if (isPersistent()) {
            try {
                return node.getProperty(SEEN_PROPERTY).getBoolean();

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + SEEN_PROPERTY, e);
            }
            return false;
        }
        return seen;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.apache.james.mailbox.store.mail.model.MailboxMembership#setFlags(javax
     * .mail.Flags)
     */
    public void setFlags(Flags flags) {
        if (isPersistent()) {
            try {
                node.setProperty(ANSWERED_PROPERTY, flags.contains(Flags.Flag.ANSWERED));
                node.setProperty(DELETED_PROPERTY, flags.contains(Flags.Flag.DELETED));
                node.setProperty(DRAFT_PROPERTY, flags.contains(Flags.Flag.DRAFT));
                node.setProperty(FLAGGED_PROPERTY, flags.contains(Flags.Flag.FLAGGED));
                node.setProperty(RECENT_PROPERTY, flags.contains(Flags.Flag.RECENT));
                node.setProperty(SEEN_PROPERTY, flags.contains(Flags.Flag.SEEN));
                node.setProperty(USERFLAGS_PROPERTY, flags.getUserFlags());
            } catch (RepositoryException e) {
                logger.error("Unable to set flags", e);
            }
        } else {
            answered = flags.contains(Flags.Flag.ANSWERED);
            deleted = flags.contains(Flags.Flag.DELETED);
            draft = flags.contains(Flags.Flag.DRAFT);
            flagged = flags.contains(Flags.Flag.FLAGGED);
            recent = flags.contains(Flags.Flag.RECENT);
            seen = flags.contains(Flags.Flag.SEEN);
            userFlags = flags.getUserFlags();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.apache.james.mailbox.store.mail.model.MailboxMembership#unsetRecent()
     */
    public void unsetRecent() {
        if (isPersistent()) {
            try {
                node.setProperty(RECENT_PROPERTY, false);

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + RECENT_PROPERTY, e);
            }
        } else {
            recent = false;
        }
    }

    public String getId() {
        if (isPersistent()) {
            try {
                return node.getIdentifier();
            } catch (RepositoryException e) {
                logger.error("Unable to access property " + JcrConstants.JCR_UUID, e);
            }
        }
        return null;
    }

    @Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = PRIME * result + getUUID().hashCode();
        result = PRIME * result + getMailboxId().hashCode();
        return result;
    }

    public String toString() {
        final String retValue = "message(" + "uuid = " + getUUID() + "mailboxUUID = " + this.getMailboxId()
                + TOSTRING_SEPARATOR + "uuid = " + this.getId() + TOSTRING_SEPARATOR + "internalDate = "
                + this.getInternalDate() + TOSTRING_SEPARATOR + "size = " + this.getFullContentOctets()
                + TOSTRING_SEPARATOR + "answered = " + this.isAnswered() + TOSTRING_SEPARATOR + "deleted = "
                + this.isDeleted() + TOSTRING_SEPARATOR + "draft = " + this.isDraft() + TOSTRING_SEPARATOR
                + "flagged = " + this.isFlagged() + TOSTRING_SEPARATOR + "recent = " + this.isRecent()
                + TOSTRING_SEPARATOR + "seen = " + this.isSeen() + TOSTRING_SEPARATOR + " )";

        return retValue;
    }

    @Override
    public InputStream getFullContent() throws IOException {
        if (isPersistent()) {
            try {
                //TODO: Maybe we should cache this somehow...
                InputStream contentStream = node.getNode(JcrConstants.JCR_CONTENT)
                        .getProperty(JcrConstants.JCR_DATA).getBinary().getStream();
                return contentStream;
            } catch (RepositoryException e) {
                throw new IOException("Unable to retrieve property " + JcrConstants.JCR_CONTENT, e);
            }
        }
        return content.newStream(0, -1);
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Message#getBodyContent()
     */
    public InputStream getBodyContent() throws IOException {
        InputStream body = getFullContent();
        IOUtils.skipFully(body, getBodyStartOctet());
        return body;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Message#getModSeq()
     */
    public long getModSeq() {
        if (isPersistent()) {
            try {
                return node.getProperty(MODSEQ_PROPERTY).getLong();

            } catch (RepositoryException e) {
                logger.error("Unable to access property " + MODSEQ_PROPERTY, e);
            }
            return 0;
        }
        return modSeq;
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Message#setModSeq(long)
     */
    public void setModSeq(long modSeq) {
        if (isPersistent()) {
            try {
                node.setProperty(MODSEQ_PROPERTY, modSeq);
            } catch (RepositoryException e) {
                logger.error("Unable to set mod-sequence", e);
            }
        } else {
            this.modSeq = modSeq;
        }
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Message#setUid(long)
     */
    public void setUid(long uid) {
        if (isPersistent()) {
            try {
                node.setProperty(UID_PROPERTY, uid);
            } catch (RepositoryException e) {
                logger.error("Unable to set uid", e);
            }
        } else {
            this.uid = uid;
        }
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.mailbox.store.mail.model.Message#getHeaderContent()
     */
    public InputStream getHeaderContent() throws IOException {
        long limit = getBodyStartOctet();
        if (limit < 0) {
            limit = 0;
        }
        return new BoundedInputStream(getFullContent(), limit);
    }
}