http://git-wip-us.apache.org/repos/asf/james-project/blob/81d65f1c/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMapper.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMapper.java deleted file mode 100644 index 847d682..0000000 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMapper.java +++ /dev/null @@ -1,357 +0,0 @@ -/**************************************************************** - * 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.hbase.mail; - -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOXES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_MESSAGE_COUNT; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_NAME; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_NAMESPACE; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_USER; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGES_META_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGE_INTERNALDATE; -import static org.apache.james.mailbox.hbase.HBaseUtils.mailboxFromResult; -import static org.apache.james.mailbox.hbase.HBaseUtils.toPut; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.client.Delete; -import org.apache.hadoop.hbase.client.Get; -import org.apache.hadoop.hbase.client.HTable; -import org.apache.hadoop.hbase.client.Put; -import org.apache.hadoop.hbase.client.Result; -import org.apache.hadoop.hbase.client.ResultScanner; -import org.apache.hadoop.hbase.client.Scan; -import org.apache.hadoop.hbase.filter.BinaryPrefixComparator; -import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; -import org.apache.hadoop.hbase.filter.FilterList; -import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; -import org.apache.hadoop.hbase.filter.SubstringComparator; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.hadoop.io.IOUtils; -import org.apache.james.mailbox.acl.ACLDiff; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.exception.MailboxNotFoundException; -import org.apache.james.mailbox.hbase.HBaseId; -import org.apache.james.mailbox.hbase.HBaseNonTransactionalMapper; -import org.apache.james.mailbox.hbase.mail.model.HBaseMailbox; -import org.apache.james.mailbox.model.MailboxACL; -import org.apache.james.mailbox.model.MailboxACL.Right; -import org.apache.james.mailbox.model.MailboxId; -import org.apache.james.mailbox.model.MailboxPath; -import org.apache.james.mailbox.store.mail.MailboxMapper; -import org.apache.james.mailbox.store.mail.model.Mailbox; - -import com.google.common.collect.ImmutableList; - -/** - * Data access management for mailbox. - * - */ -public class HBaseMailboxMapper extends HBaseNonTransactionalMapper implements MailboxMapper { - - /** - * Link to the HBase Configuration object and specific mailbox names - */ - private final Configuration conf; - - public HBaseMailboxMapper(Configuration conf) { - this.conf = conf; - } - - @Override - public Mailbox findMailboxByPath(MailboxPath mailboxPath) throws MailboxException, MailboxNotFoundException { - - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - - Scan scan = new Scan(); - scan.addFamily(MAILBOX_CF); - scan.setCaching(mailboxes.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - - /* - * Filters is ORDERED. Passing the parameters in the right order - * might improve performance: passing the user first means that the - * other filters will not be tested if the mailbox does not belong - * to the passed user. - */ - FilterList filters = new FilterList(FilterList.Operator.MUST_PASS_ALL); - - if (mailboxPath.getUser() != null) { - SingleColumnValueFilter userFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_USER, CompareOp.EQUAL, Bytes.toBytes(mailboxPath.getUser())); - filters.addFilter(userFilter); - } - SingleColumnValueFilter nameFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_NAME, CompareOp.EQUAL, Bytes.toBytes(mailboxPath.getName())); - filters.addFilter(nameFilter); - SingleColumnValueFilter namespaceFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_NAMESPACE, CompareOp.EQUAL, Bytes.toBytes(mailboxPath.getNamespace())); - filters.addFilter(namespaceFilter); - scan.setFilter(filters); - - try (ResultScanner scanner = mailboxes.getScanner(scan)) { - Result result = scanner.next(); - if (result == null) { - throw new MailboxNotFoundException(mailboxPath); - } - return mailboxFromResult(result); - } - } catch (IOException e) { - throw new MailboxException("Search of mailbox " + mailboxPath + " failed", e); - } - } - - @Override - public Mailbox findMailboxById(MailboxId id) throws MailboxException, MailboxNotFoundException { - HBaseId mailboxId = (HBaseId)id; - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - Get get = new Get(mailboxId.toBytes()); - Result result = mailboxes.get(get); - if (result == null) { - throw new MailboxNotFoundException(mailboxId); - } - return mailboxFromResult(result); - } catch (IOException ex) { - throw new MailboxException("IOException in HBase cluster during get()", ex); - } - } - - @Override - public List<Mailbox> findMailboxWithPathLike(MailboxPath mailboxPath) throws MailboxException { - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - Scan scan = new Scan(); - scan.addFamily(MAILBOX_CF); - scan.setCaching(mailboxes.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - - FilterList filters = new FilterList(FilterList.Operator.MUST_PASS_ALL); - - if (mailboxPath.getUser() != null) { - SingleColumnValueFilter userFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_USER, CompareOp.EQUAL, Bytes.toBytes(mailboxPath.getUser())); - filters.addFilter(userFilter); - } - SubstringComparator pathComparator; - String mboxName = mailboxPath.getName(); - /* - * TODO: use a RegExFiler - */ - if (mboxName.length() >= 1) { - if (mboxName.charAt(mboxName.length() - 1) == '%') { - mboxName = mboxName.substring(0, mboxName.length() - 1); - } - } - if (mboxName.length() >= 1) { - if (mboxName.charAt(0) == '%') { - mboxName = mboxName.substring(1); - } - } - pathComparator = new SubstringComparator(mboxName); - SingleColumnValueFilter nameFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_NAME, CompareOp.EQUAL, pathComparator); - filters.addFilter(nameFilter); - SingleColumnValueFilter namespaceFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_NAMESPACE, CompareOp.EQUAL, Bytes.toBytes(mailboxPath.getNamespace())); - filters.addFilter(namespaceFilter); - - scan.setFilter(filters); - try (ResultScanner scanner = mailboxes.getScanner(scan)) { - List<Mailbox> mailboxList = new ArrayList<>(); - for (Result result : scanner) { - mailboxList.add(mailboxFromResult(result)); - } - return mailboxList; - } - } catch (IOException e) { - throw new MailboxException("Search of mailbox " + mailboxPath + " failed", e); - } - } - - @Override - public List<Mailbox> list() throws MailboxException { - //TODO: possible performance isssues, we are creating an object from all the rows in HBase mailbox table - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - Scan scan = new Scan(); - scan.addFamily(MAILBOX_CF); - scan.setCaching(mailboxes.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - - try (ResultScanner scanner = mailboxes.getScanner(scan)) { - List<Mailbox> mailboxList = new ArrayList<>(); - Result result; - while ((result = scanner.next()) != null) { - Mailbox mlbx = mailboxFromResult(result); - mailboxList.add(mlbx); - } - return mailboxList; - } - } catch (IOException ex) { - throw new MailboxException("HBase IOException in list()", ex); - } - } - - @Override - public void endRequest() { - } - - @Override - public MailboxId save(Mailbox mlbx) throws MailboxException { - //TODO: maybe switch to checkAndPut for transactions - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - /* - * cast to HBaseMailbox to access lastuid and ModSeq - */ - Put put = toPut((HBaseMailbox) mlbx); - mailboxes.put(put); - return mlbx.getMailboxId(); - } catch (IOException ex) { - throw new MailboxException("IOExeption", ex); - } - } - - @Override - public void delete(Mailbox mlbx) throws MailboxException { - //TODO: maybe switch to checkAndDelete - HBaseId mailboxId = (HBaseId) mlbx.getMailboxId(); - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - //TODO: delete all maessages from this mailbox - Delete delete = new Delete(mailboxId.toBytes()); - mailboxes.delete(delete); - } catch (IOException ex) { - throw new MailboxException("IOException in HBase cluster during delete()", ex); - } - } - - @Override - public boolean hasChildren(Mailbox mailbox, char c) throws MailboxException, MailboxNotFoundException { - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - Scan scan = new Scan(); - scan.addFamily(MAILBOX_CF); - scan.setCaching(mailboxes.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - - FilterList filters = new FilterList(FilterList.Operator.MUST_PASS_ALL); - - if (mailbox.getUser() != null) { - SingleColumnValueFilter userFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_USER, CompareOp.EQUAL, Bytes.toBytes(mailbox.getUser())); - filters.addFilter(userFilter); - } - SingleColumnValueFilter nameFilter = new SingleColumnValueFilter(MAILBOX_CF, - MAILBOX_NAME, - CompareOp.EQUAL, - new BinaryPrefixComparator(Bytes.toBytes(mailbox.getName() + c))); - filters.addFilter(nameFilter); - SingleColumnValueFilter namespaceFilter = new SingleColumnValueFilter(MAILBOX_CF, MAILBOX_NAMESPACE, CompareOp.EQUAL, Bytes.toBytes(mailbox.getNamespace())); - filters.addFilter(namespaceFilter); - - scan.setFilter(filters); - try (ResultScanner scanner = mailboxes.getScanner(scan)) { - if (scanner.next() != null) { - return true; - } - } catch (IOException e) { - throw new MailboxNotFoundException("hasChildren() " + mailbox.getName()); - } - return false; - } catch (IOException e) { - throw new MailboxException("Search of mailbox " + mailbox + " failed", e); - } - } - - public void deleteAllMemberships() { - try (HTable messages = new HTable(conf, MESSAGES_TABLE)) { - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - Scan scan = new Scan(); - scan.setMaxVersions(1); - scan.addColumn(MESSAGES_META_CF, MESSAGE_INTERNALDATE); - try (ResultScanner scanner = messages.getScanner(scan)) { - Result result; - List<Delete> deletes = new ArrayList<>(); - while ((result = scanner.next()) != null) { - deletes.add(new Delete(result.getRow())); - } - long totalDeletes = deletes.size(); - messages.delete(deletes); - if (deletes.size() > 0) { - //TODO: what shoul we do if not all messages are deleted? - System.out.println("Just " + deletes.size() + " out of " + totalDeletes + " messages have been deleted"); - //throw new RuntimeException("Just " + deletes.size() + " out of " + totalDeletes + " messages have been deleted"); - } - } - - List<Put> puts = new ArrayList<>(); - scan = new Scan(); - scan.setMaxVersions(1); - scan.addColumn(MAILBOX_CF, MAILBOX_MESSAGE_COUNT); - try (ResultScanner scanner = mailboxes.getScanner(scan)) { - Put put = null; - Result result; - while ((result = scanner.next()) != null) { - put = new Put(result.getRow()); - put.add(MAILBOX_CF, MAILBOX_MESSAGE_COUNT, Bytes.toBytes(0L)); - puts.add(new Put()); - } - } - } - } catch (IOException e) { - throw new RuntimeException("Error deleting MESSAGES table ", e); - } - } - - public void deleteAllMailboxes() { - HTable mailboxes = null; - ResultScanner scanner = null; - try { - mailboxes = new HTable(conf, MAILBOXES_TABLE); - Scan scan = new Scan(); - scan.setMaxVersions(1); - scan.addColumn(MAILBOX_CF, MAILBOX_NAME); - scanner = mailboxes.getScanner(scan); - Result result; - List<Delete> deletes = new ArrayList<>(); - while ((result = scanner.next()) != null) { - deletes.add(new Delete(result.getRow())); - } - mailboxes.delete(deletes); - } catch (IOException ex) { - throw new RuntimeException("IOException deleting mailboxes", ex); - } finally { - IOUtils.cleanup(null, scanner, mailboxes); - } - } - - @Override - public ACLDiff updateACL(Mailbox mailbox, MailboxACL.ACLCommand mailboxACLCommand) throws MailboxException { - MailboxACL oldACL = mailbox.getACL(); - MailboxACL newACL = mailbox.getACL().apply(mailboxACLCommand); - mailbox.setACL(newACL); - return ACLDiff.computeDiff(oldACL, newACL); - } - - @Override - public ACLDiff setACL(Mailbox mailbox, MailboxACL mailboxACL) throws MailboxException { - MailboxACL oldMailboxAcl = mailbox.getACL(); - mailbox.setACL(mailboxACL); - return ACLDiff.computeDiff(oldMailboxAcl, mailboxACL); - } - - @Override - public List<Mailbox> findNonPersonalMailboxes(String userName, Right right) throws MailboxException { - return ImmutableList.of(); - } -}
http://git-wip-us.apache.org/repos/asf/james-project/blob/81d65f1c/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMessage.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMessage.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMessage.java deleted file mode 100644 index e94f5c8..0000000 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMailboxMessage.java +++ /dev/null @@ -1,364 +0,0 @@ -/**************************************************************** - * 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.hbase.mail; - -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGE_DATA_BODY_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGE_DATA_HEADERS_CF; -import static org.apache.james.mailbox.hbase.HBaseUtils.messageRowKey; - -import java.io.IOException; -import java.io.InputStream; -import java.io.SequenceInputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Date; -import java.util.List; - -import javax.mail.Flags; - -import org.apache.hadoop.conf.Configuration; -import org.apache.james.mailbox.MessageUid; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.hbase.HBaseId; -import org.apache.james.mailbox.hbase.io.ChunkInputStream; -import org.apache.james.mailbox.model.ComposedMessageId; -import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData; -import org.apache.james.mailbox.model.MessageAttachment; -import org.apache.james.mailbox.model.MessageId; -import org.apache.james.mailbox.store.mail.model.FlagsFactory; -import org.apache.james.mailbox.store.mail.model.MailboxMessage; -import org.apache.james.mailbox.store.mail.model.Property; -import org.apache.james.mailbox.store.mail.model.impl.MessageParser; -import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; -import org.apache.james.mailbox.store.search.comparator.UidComparator; -import org.apache.james.mime4j.MimeException; - -import com.google.common.base.Objects; - -/** - * Concrete HBaseMailboxMessage implementation. This implementation does not store any - * message content. The message content is retrieved using a ChunkedInputStream - * directly from HBase. - */ -public class HBaseMailboxMessage implements MailboxMessage { - - private static final Comparator<MailboxMessage> MESSAGE_UID_COMPARATOR = new UidComparator(); - private static final String TOSTRING_SEPARATOR = " "; - /** Configuration for the HBase cluster */ - private final Configuration conf; - /** The value for the mailboxId field */ - private final HBaseId mailboxId; - /** The value for the uid field */ - private MessageUid uid; - /** The value for the modSeq field */ - private long modSeq; - /** The value for the internalDate field */ - private final Date internalDate; - /** The value for the answered field */ - private boolean answered = false; - /** The value for the deleted field */ - private boolean deleted = false; - /** The value for the draft field */ - private boolean draft = false; - /** The value for the flagged field */ - private boolean flagged = false; - /** The value for the recent field */ - private boolean recent = false; - /** The value for the seen field */ - private boolean seen = false; - /** The first body octet */ - private final int bodyStartOctet; - /** Number of octets in the full document content */ - private final long contentOctets; - /** MIME media type */ - private final String mediaType; - /** MIME sub type */ - private final String subType; - /** THE CRFL count when this document is textual, null otherwise */ - private Long textualLineCount; - /** Meta data for this message */ - private final List<Property> properties; - private final List<String> userFlags; - private final MessageId messageId; - - /** - * Create a copy of the given message. - * All properties are cloned except mailbox and UID. - */ - public HBaseMailboxMessage(Configuration conf, HBaseId mailboxId, MessageUid uid, MessageId messageId, long modSeq, MailboxMessage original) throws MailboxException { - this.conf = conf; - this.mailboxId = mailboxId; - this.uid = uid; - this.messageId = messageId; - this.modSeq = modSeq; - this.userFlags = new ArrayList<>(); - setFlags(original.createFlags()); - - // A copy of a message is recent - // See MAILBOX-85 - this.recent = true; - - this.contentOctets = original.getFullContentOctets(); - this.bodyStartOctet = (int) (original.getFullContentOctets() - original.getBodyOctets()); - this.internalDate = original.getInternalDate(); - - this.textualLineCount = original.getTextualLineCount(); - this.mediaType = original.getMediaType(); - this.subType = original.getSubType(); - this.properties = original.getProperties(); - } - - public HBaseMailboxMessage(Configuration conf, HBaseId mailboxId, MessageId messageId, - Date internalDate, Flags flags, long contentOctets, int bodyStartOctet, PropertyBuilder propertyBuilder) { - super(); - this.conf = conf; - this.mailboxId = mailboxId; - this.messageId = messageId; - this.internalDate = internalDate; - userFlags = new ArrayList<>(); - - setFlags(flags); - this.contentOctets = contentOctets; - this.bodyStartOctet = bodyStartOctet; - this.textualLineCount = propertyBuilder.getTextualLineCount(); - this.mediaType = propertyBuilder.getMediaType(); - this.subType = propertyBuilder.getSubType(); - this.properties = propertyBuilder.toProperties(); - } - - @Override - public ComposedMessageIdWithMetaData getComposedMessageIdWithMetaData() { - return ComposedMessageIdWithMetaData.builder() - .modSeq(modSeq) - .flags(createFlags()) - .composedMessageId(new ComposedMessageId(mailboxId, getMessageId(), uid)) - .build(); - } - - @Override - public InputStream getBodyContent() throws IOException { - return new ChunkInputStream(conf, MESSAGES_TABLE, MESSAGE_DATA_BODY_CF, messageRowKey(this)); - } - - @Override - public InputStream getHeaderContent() throws IOException { - return new ChunkInputStream(conf, MESSAGES_TABLE, MESSAGE_DATA_HEADERS_CF, messageRowKey(this)); - } - - @Override - public InputStream getFullContent() throws IOException { - return new SequenceInputStream(getHeaderContent(), getBodyContent()); - } - - @Override - public int hashCode() { - return Objects.hashCode(mailboxId, uid); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof HBaseMailboxMessage) { - HBaseMailboxMessage other = (HBaseMailboxMessage) obj; - return Objects.equal(this.mailboxId, other.mailboxId) && - Objects.equal(this.uid, other.uid); - } - return false; - } - - @Override - public long getModSeq() { - return modSeq; - } - - @Override - public void setModSeq(long modSeq) { - this.modSeq = modSeq; - } - - /** - * Gets the top level MIME content media type. - * - * @return top level MIME content media type, or null if default - */ - @Override - public String getMediaType() { - return mediaType; - } - - /** - * Gets the MIME content subtype. - * - * @return the MIME content subtype, or null if default - */ - @Override - public String getSubType() { - return subType; - } - - @Override - public long getBodyOctets() { - return getFullContentOctets() - bodyStartOctet; - } - - /** - * Gets a read-only list of meta-data properties. - * For properties with multiple values, this list will contain - * several enteries with the same namespace and local name. - * @return unmodifiable list of meta-data, not null - */ - @Override - public List<Property> getProperties() { - return new ArrayList<>(properties); - } - - /** - * Gets the number of CRLF in a textual document. - * @return CRLF count when document is textual, - * null otherwise - */ - @Override - public Long getTextualLineCount() { - return textualLineCount; - } - - public void setTextualLineCount(Long textualLineCount) { - this.textualLineCount = textualLineCount; - } - - @Override - public long getFullContentOctets() { - return contentOctets; - } - - @Override - public MessageId getMessageId() { - return messageId; - } - - @Override - public Date getInternalDate() { - return internalDate; - } - - @Override - public HBaseId getMailboxId() { - return mailboxId; - } - - @Override - public MessageUid getUid() { - return uid; - } - - @Override - public boolean isAnswered() { - return answered; - } - - @Override - public boolean isDeleted() { - return deleted; - } - - @Override - public boolean isDraft() { - return draft; - } - - @Override - public boolean isFlagged() { - return flagged; - } - - @Override - public boolean isRecent() { - return recent; - } - - @Override - public boolean isSeen() { - return seen; - } - - @Override - public void setUid(MessageUid uid) { - this.uid = uid; - } - - @Override - public final void setFlags(Flags flags) { - 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); - String[] userflags = flags.getUserFlags(); - userFlags.clear(); - userFlags.addAll(Arrays.asList(userflags)); - } - - @Override - public Flags createFlags() { - return FlagsFactory.createFlags(this, createUserFlags()); - } - - @Override - public long getHeaderOctets() { - return bodyStartOctet; - } - - /** - * This implementation supports user flags - */ - public String[] createUserFlags() { - return userFlags.toArray(new String[userFlags.size()]); - } - - @Override - public String toString() { - return "message(" - + "mailboxId = " + this.getMailboxId() + TOSTRING_SEPARATOR - + "uid = " + this.uid + TOSTRING_SEPARATOR - + "internalDate = " + this.internalDate + TOSTRING_SEPARATOR - + "answered = " + this.answered + TOSTRING_SEPARATOR - + "deleted = " + this.deleted + TOSTRING_SEPARATOR - + "draft = " + this.draft + TOSTRING_SEPARATOR - + "flagged = " + this.flagged + TOSTRING_SEPARATOR - + "recent = " + this.recent + TOSTRING_SEPARATOR - + "seen = " + this.seen + TOSTRING_SEPARATOR - + " )"; - } - - @Override - public int compareTo(MailboxMessage other) { - return MESSAGE_UID_COMPARATOR.compare(this, other); - } - - @Override - public List<MessageAttachment> getAttachments() { - try { - return new MessageParser().retrieveAttachments(getFullContent()); - } catch (MimeException | IOException e) { - throw new RuntimeException(e); - } - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/81d65f1c/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java deleted file mode 100644 index d6d602f..0000000 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java +++ /dev/null @@ -1,772 +0,0 @@ -/**************************************************************** - * 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.hbase.mail; - -import static org.apache.james.mailbox.hbase.FlagConvertor.FLAGS_DELETED; -import static org.apache.james.mailbox.hbase.FlagConvertor.FLAGS_RECENT; -import static org.apache.james.mailbox.hbase.FlagConvertor.FLAGS_SEEN; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOXES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_HIGHEST_MODSEQ; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_MESSAGE_COUNT; -import static org.apache.james.mailbox.hbase.HBaseNames.MARKER_MISSING; -import static org.apache.james.mailbox.hbase.HBaseNames.MARKER_PRESENT; -import static org.apache.james.mailbox.hbase.HBaseNames.MAX_COLUMN_SIZE; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGES_META_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGE_DATA_BODY_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGE_DATA_HEADERS_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MESSAGE_MODSEQ; -import static org.apache.james.mailbox.hbase.HBaseUtils.flagsToPut; -import static org.apache.james.mailbox.hbase.HBaseUtils.messageMetaFromResult; -import static org.apache.james.mailbox.hbase.HBaseUtils.messageRowKey; -import static org.apache.james.mailbox.hbase.HBaseUtils.metadataToPut; -import static org.apache.james.mailbox.hbase.HBaseUtils.minMessageRowKey; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -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 org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.client.Delete; -import org.apache.hadoop.hbase.client.Get; -import org.apache.hadoop.hbase.client.HTable; -import org.apache.hadoop.hbase.client.Put; -import org.apache.hadoop.hbase.client.Result; -import org.apache.hadoop.hbase.client.ResultScanner; -import org.apache.hadoop.hbase.client.Scan; -import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; -import org.apache.hadoop.hbase.filter.PrefixFilter; -import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter; -import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MessageUid; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.hbase.HBaseId; -import org.apache.james.mailbox.hbase.io.ChunkOutputStream; -import org.apache.james.mailbox.model.MailboxCounters; -import org.apache.james.mailbox.model.MessageId; -import org.apache.james.mailbox.model.MessageId.Factory; -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.SimpleMessageMetaData; -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.Mailbox; -import org.apache.james.mailbox.store.mail.model.MailboxMessage; -import org.apache.james.mailbox.store.mail.utils.ApplicableFlagCalculator; -import org.apache.james.mailbox.store.transaction.NonTransactionalMapper; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; - -/** - * HBase implementation of a {@link MessageMapper}. - * I don't know if this class is thread-safe! Asume it is not! - * - */ -public class HBaseMessageMapper extends NonTransactionalMapper implements MessageMapper { - - private static final int UNLIMITED = -1; - - private final Configuration conf; - private final MailboxSession mailboxSession; - private final UidProvider uidProvider; - private final ModSeqProvider modSeqProvider; - private final Factory messageIdFactory; - - public HBaseMessageMapper(MailboxSession session, - final UidProvider uidProvider, - ModSeqProvider modSeqProvider, - MessageId.Factory messageIdFactory, - Configuration conf) { - this.mailboxSession = session; - this.modSeqProvider = modSeqProvider; - this.uidProvider = uidProvider; - this.messageIdFactory = messageIdFactory; - this.conf = conf; - } - - @Override - public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException { - return MailboxCounters.builder() - .count(countMessagesInMailbox(mailbox)) - .unseen(countUnseenMessagesInMailbox(mailbox)) - .build(); - } - - @Override - public Iterator<MessageUid> listAllMessageUids(final Mailbox mailbox) throws MailboxException { - return Iterators.transform(findInMailbox(mailbox, MessageRange.all(), FetchType.Full, UNLIMITED), MailboxMessage::getUid); - } - - @Override - public void endRequest() { - } - - @Override - public Iterator<MailboxMessage> findInMailbox(Mailbox mailbox, MessageRange set, FetchType fType, int max) throws MailboxException { - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try { - List<MailboxMessage> results; - MessageUid from = set.getUidFrom(); - final MessageUid to = set.getUidTo(); - final Type type = set.getType(); - - switch (type) { - default: - case ALL: - results = findMessagesInMailbox(mailboxId, max, false); - break; - case FROM: - results = findMessagesInMailboxAfterUID(mailboxId, from, max, false); - break; - case ONE: - results = findMessagesInMailboxWithUID(mailboxId, from, false); - break; - case RANGE: - results = findMessagesInMailboxBetweenUIDs(mailboxId, from, to, max, false); - break; - } - return results.iterator(); - - } catch (IOException e) { - throw new MailboxException("Search of MessageRange " + set + " failed in mailbox " + mailbox, e); - } - } - - private List<MailboxMessage> findMessagesInMailbox(HBaseId mailboxId, int batchSize, boolean flaggedForDelete) throws IOException { - List<MailboxMessage> messageList = new ArrayList<>(); - HTable messages = new HTable(conf, MESSAGES_TABLE); - Scan scan = new Scan(minMessageRowKey(mailboxId), - new PrefixFilter(mailboxId.toBytes())); - if (flaggedForDelete) { - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - filter.setFilterIfMissing(true); - scan.setFilter(filter); - } - scan.setMaxVersions(1); - /* we exclude the message content column family because it could be too large. - * the content will be pulled from HBase on demand by using a a ChunkedInputStream implementation. - */ - scan.addFamily(MESSAGES_META_CF); - ResultScanner scanner = messages.getScanner(scan); - Result result; - long count = batchSize > 0 ? batchSize : Long.MAX_VALUE; - while (((result = scanner.next()) != null) && (count > 0)) { - messageList.add(messageMetaFromResult(conf, result, messageIdFactory)); - count--; - } - scanner.close(); - messages.close(); - // we store uids in reverse order, we send them ascending - Collections.reverse(messageList); - return messageList; - } - - private List<MailboxMessage> findMessagesInMailboxWithUID(HBaseId mailboxId, MessageUid from, boolean flaggedForDelete) throws IOException { - List<MailboxMessage> messageList = new ArrayList<>(); - HTable messages = new HTable(conf, MESSAGES_TABLE); - Get get = new Get(messageRowKey(mailboxId, from)); - get.setMaxVersions(1); - /* we exclude the message content column family because it could be too large. - * the content will be pulled from HBase on demand by using a a ChunkedInputStream implementation. - */ - if (flaggedForDelete) { - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - filter.setFilterIfMissing(true); - get.setFilter(filter); - } - get.addFamily(MESSAGES_META_CF); - Result result = messages.get(get); - MailboxMessage message = null; - if (!result.isEmpty()) { - message = messageMetaFromResult(conf, result, messageIdFactory); - messageList.add(message); - } - messages.close(); - return messageList; - } - - private List<MailboxMessage> findMessagesInMailboxAfterUID(HBaseId mailboxId, MessageUid messageUid, int batchSize, boolean flaggedForDelete) throws IOException { - List<MailboxMessage> messageList = new ArrayList<>(); - HTable messages = new HTable(conf, MESSAGES_TABLE); - // uids are stored in reverse so we need to search - - Scan scan = new Scan(messageRowKey(mailboxId, MessageUid.MAX_VALUE), previousMessageRowKey(mailboxId, messageUid)); - if (flaggedForDelete) { - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - filter.setFilterIfMissing(true); - scan.setFilter(filter); - } - scan.setMaxVersions(1); - /* we exclude the message content column family because it could be too large. - * the content will be pulled from HBase on demand by using a a ChunkedInputStream implementation. - */ - scan.addFamily(MESSAGES_META_CF); - ResultScanner scanner = messages.getScanner(scan); - Result result; - long count = batchSize > 0 ? batchSize : Long.MAX_VALUE; - while (((result = scanner.next()) != null) && (count > 0)) { - messageList.add(messageMetaFromResult(conf, result, messageIdFactory)); - count--; - } - scanner.close(); - messages.close(); - // uids are stored in reverese so we change the list - Collections.reverse(messageList); - return messageList; - } - - private byte[] previousMessageRowKey(HBaseId mailboxId, MessageUid messageUid) { - if (messageUid.isFirst()) { - return minMessageRowKey(mailboxId); - } else { - return messageRowKey(mailboxId, messageUid.previous()); - } - } - - private List<MailboxMessage> findMessagesInMailboxBetweenUIDs(HBaseId mailboxId, MessageUid from, MessageUid to, int batchSize, boolean flaggedForDelete) throws IOException { - List<MailboxMessage> messageList = new ArrayList<>(); - if (from.compareTo(to) > 0) { - return messageList; - } - HTable messages = new HTable(conf, MESSAGES_TABLE); - /*TODO: check if Between should be inclusive or exclusive regarding limits. - * HBase scan operaion are exclusive to the upper bound when providing stop row key. - */ - Scan scan = new Scan(messageRowKey(mailboxId, to), previousMessageRowKey(mailboxId, from)); - if (flaggedForDelete) { - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - filter.setFilterIfMissing(true); - scan.setFilter(filter); - } - scan.setMaxVersions(1); - /* we exclude the message content column family because it could be too large. - * the content will be pulled from HBase on demand by using a a ChunkedInputStream implementation. - */ - scan.addFamily(MESSAGES_META_CF); - ResultScanner scanner = messages.getScanner(scan); - Result result; - - long count = batchSize > 0 ? batchSize : Long.MAX_VALUE; - while (((result = scanner.next()) != null)) { - if (count == 0) { - break; - } - MailboxMessage message = messageMetaFromResult(conf, result, messageIdFactory); - messageList.add(message); - count--; - } - scanner.close(); - messages.close(); - // uids are stored in reverse order - Collections.reverse(messageList); - return messageList; - } - - @Override - public Map<MessageUid, MessageMetaData> expungeMarkedForDeletionInMailbox(Mailbox mailbox, MessageRange set) throws MailboxException { - try { - final Map<MessageUid, MessageMetaData> data; - final List<MailboxMessage> results; - final MessageUid from = set.getUidFrom(); - final MessageUid to = set.getUidTo(); - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - - switch (set.getType()) { - case ONE: - results = findMessagesInMailboxWithUID(mailboxId, from, true); - data = createMetaData(results); - deleteDeletedMessagesInMailboxWithUID(mailboxId, from); - break; - case RANGE: - results = findMessagesInMailboxBetweenUIDs(mailboxId, from, to, -1, true); - data = createMetaData(results); - deleteDeletedMessagesInMailboxBetweenUIDs(mailboxId, from, to); - break; - case FROM: - results = findMessagesInMailboxAfterUID(mailboxId, from, -1, true); - data = createMetaData(results); - deleteDeletedMessagesInMailboxAfterUID(mailboxId, from); - break; - default: - case ALL: - results = findMessagesInMailbox(mailboxId, -1, true); - data = createMetaData(results); - deleteDeletedMessagesInMailbox(mailboxId); - break; - } - - return data; - } catch (IOException e) { - throw new MailboxException("Search of MessageRange " + set + " failed in mailbox " + mailbox, e); - } - } - - @Override - public long countMessagesInMailbox(Mailbox mailbox) throws MailboxException { - HTable mailboxes = null; - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try { - mailboxes = new HTable(conf, MAILBOXES_TABLE); - Get get = new Get(mailboxId.toBytes()); - get.addColumn(MAILBOX_CF, MAILBOX_MESSAGE_COUNT); - get.setMaxVersions(1); - Result result = mailboxes.get(get); - long count = Bytes.toLong(result.getValue(MAILBOX_CF, MAILBOX_MESSAGE_COUNT)); - return count; - } catch (IOException e) { - throw new MailboxException("Count of messages failed in mailbox " + mailbox, e); - } finally { - if (mailboxes != null) { - try { - mailboxes.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + mailboxes, ex); - } - } - } - } - - @Override - public long countUnseenMessagesInMailbox(Mailbox mailbox) throws MailboxException { - /* TODO: see if it is possible to store the number of unseen messages in the mailbox table - * and just return that value with a Get and kepp it up to date. - */ - ResultScanner scanner = null; - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try (HTable messages = new HTable(conf, MESSAGES_TABLE)) { - /* Limit the number of entries scanned to just the mails in this mailbox */ - Scan scan = new Scan( - messageRowKey(mailboxId, MessageUid.MAX_VALUE), - minMessageRowKey(mailboxId)); - scan.addFamily(MESSAGES_META_CF); - scan.setFilter(new SingleColumnValueExcludeFilter(MESSAGES_META_CF, FLAGS_SEEN, CompareOp.EQUAL, MARKER_MISSING)); - scan.setCaching(messages.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - scanner = messages.getScanner(scan); - return Iterables.size(scanner); - } catch (IOException e) { - throw new MailboxException("Search of first unseen message failed in mailbox " + mailbox, e); - } finally { - scanner.close(); - } - } - - @Override - public void delete(Mailbox mailbox, MailboxMessage message) throws MailboxException { - //TODO: maybe switch to checkAndDelete - HTable messages = null; - HTable mailboxes = null; - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try { - messages = new HTable(conf, MESSAGES_TABLE); - mailboxes = new HTable(conf, MAILBOXES_TABLE); - /** TODO: also implement/update the message count for this mailbox - * and implement countMessages with get. - */ - Delete delete = new Delete(messageRowKey(message)); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_MESSAGE_COUNT, -1); - messages.delete(delete); - - } catch (IOException ex) { - throw new MailboxException("Delete of message " + message + " failed in mailbox " + mailbox, ex); - } finally { - - if (mailboxes != null) { - try { - mailboxes.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + mailboxes, ex); - } - } - if (messages != null) { - try { - messages.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + messages, ex); - } - } - - } - - } - - @Override - public MessageUid findFirstUnseenMessageUid(Mailbox mailbox) throws MailboxException { - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try (HTable messages = new HTable(conf, MESSAGES_TABLE)) { - /* Limit the number of entries scanned to just the mails in this mailbox */ - Scan scan = new Scan( - messageRowKey(mailboxId, MessageUid.MAX_VALUE), - minMessageRowKey(mailboxId)); - scan.addFamily(MESSAGES_META_CF); - // filter out all rows with FLAGS_SEEN qualifier - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_SEEN, CompareOp.EQUAL, MARKER_MISSING); - scan.setFilter(filter); - scan.setCaching(messages.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - try (ResultScanner scanner = messages.getScanner(scan)) { - Result result; - MessageUid lastUnseen = null; - byte[] row = null; - while ((result = scanner.next()) != null) { - row = result.getRow(); - } - if (row != null) { - lastUnseen = MessageUid.of(Long.MAX_VALUE - Bytes.toLong(row, 16, 8)); - } - return lastUnseen; - } - } catch (IOException e) { - throw new MailboxException("Search of first unseen message failed in mailbox " + mailbox, e); - } - } - - @Override - public List<MessageUid> findRecentMessageUidsInMailbox(Mailbox mailbox) throws MailboxException { - /** TODO: improve performance by implementing a last seen and last recent value per mailbox. - * maybe one more call to HBase is less expensive than iterating throgh all rows. - */ - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try (HTable messages = new HTable(conf, MESSAGES_TABLE)) { - /* Limit the number of entries scanned to just the mails in this mailbox */ - Scan scan = new Scan( - messageRowKey(mailboxId, MessageUid.MAX_VALUE), - minMessageRowKey(mailboxId)); - // we add the column, if it exists, the message is recent, else it is not - scan.addColumn(MESSAGES_META_CF, FLAGS_RECENT); - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_RECENT, CompareOp.EQUAL, MARKER_PRESENT); - scan.setFilter(filter); - scan.setCaching(messages.getConfiguration().getInt("hbase.client.scanner.caching", 1) * 2); - scan.setMaxVersions(1); - - try (ResultScanner scanner = messages.getScanner(scan)) { - Result result; - List<MessageUid> uids = new ArrayList<>(); - while ((result = scanner.next()) != null) { - uids.add(MessageUid.of(Long.MAX_VALUE - Bytes.toLong(result.getRow(), 16, 8))); - } - Collections.reverse(uids); - return uids; - } - } catch (IOException e) { - throw new MailboxException("Search of recent messages failed in mailbox " + mailbox, e); - } - } - - @Override - public MessageMetaData add(Mailbox mailbox, MailboxMessage message) throws MailboxException { - message.setUid(uidProvider.nextUid(mailboxSession, mailbox)); - // if a mailbox does not support mod-sequences the provider may be null - if (modSeqProvider != null) { - message.setModSeq(modSeqProvider.nextModSeq(mailboxSession, mailbox)); - } - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - MessageMetaData data = save(mailboxId, message); - - return data; - } - - @Override - public Iterator<UpdatedFlags> updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagsUpdateCalculator, MessageRange set) throws MailboxException { - - final List<UpdatedFlags> updatedFlags = new ArrayList<>(); - Iterator<MailboxMessage> messagesFound = findInMailbox(mailbox, set, FetchType.Metadata, -1); - - HTable messages = null; - long modSeq = -1; - if (messagesFound.hasNext() == false) { - // if a mailbox does not support mod-sequences the provider may be null - if (modSeqProvider != null) { - modSeq = modSeqProvider.nextModSeq(mailboxSession, mailbox); - } - } - - try { - messages = new HTable(conf, MESSAGES_TABLE); - while (messagesFound.hasNext()) { - Put put = null; - final MailboxMessage member = messagesFound.next(); - Flags originalFlags = member.createFlags(); - member.setFlags(flagsUpdateCalculator.buildNewFlags(originalFlags)); - Flags newFlags = member.createFlags(); - put = flagsToPut(member, newFlags); - if (UpdatedFlags.flagsChanged(originalFlags, newFlags)) { - // increase the mod-seq as we changed the flags - put.add(MESSAGES_META_CF, MESSAGE_MODSEQ, Bytes.toBytes(modSeq)); - // update put not to include the allready existing flags - messages.put(put); - messages.flushCommits(); - } - - updatedFlags.add(UpdatedFlags.builder() - .uid(member.getUid()) - .modSeq(member.getModSeq()) - .newFlags(newFlags) - .oldFlags(originalFlags) - .build()); - } - } catch (IOException e) { - throw new MailboxException("Error setting flags for messages in " + mailbox, e); - } finally { - if (messages != null) { - try { - messages.close(); - } catch (IOException e) { - throw new MailboxException("Error setting flags for messages in " + mailbox, e); - } - } - } - - return updatedFlags.iterator(); - } - - @Override - public MessageMetaData copy(Mailbox mailbox, MailboxMessage original) throws MailboxException { - MessageUid uid = uidProvider.nextUid(mailboxSession, mailbox); - long modSeq = -1; - if (modSeqProvider != null) { - modSeq = modSeqProvider.nextModSeq(mailboxSession, mailbox); - } - //TODO: check if creating a HBase message is the right thing to do - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - HBaseMailboxMessage message = new HBaseMailboxMessage(conf, - mailboxId, uid, original.getMessageId(), modSeq, original); - return save(mailboxId, message); - } - - @Override - public MessageMetaData move(Mailbox mailbox, MailboxMessage original) throws MailboxException { - //TODO implement if possible - throw new UnsupportedOperationException(); - } - - @Override - public Optional<MessageUid> getLastUid(Mailbox mailbox) throws MailboxException { - return uidProvider.lastUid(mailboxSession, mailbox); - } - - @Override - public long getHighestModSeq(Mailbox mailbox) throws MailboxException { - return modSeqProvider.highestModSeq(mailboxSession, mailbox); - } - - @Override - public Flags getApplicableFlag(Mailbox mailbox) throws MailboxException { - int maxBatchSize = -1; - boolean flaggedForDelete = true; - try { - return new ApplicableFlagCalculator(findMessagesInMailbox((HBaseId) mailbox.getMailboxId(), maxBatchSize, flaggedForDelete)) - .computeApplicableFlags(); - } catch (IOException e) { - throw new MailboxException("Search of all message failed in mailbox " + mailbox.getName(), e); - } - } - - /** - * Save the {@link MailboxMessage} for the given {@link Mailbox} and return the {@link MessageMetaData} - * - * @param mailboxId - * @param message - * @return metaData - * @throws MailboxException - */ - protected MessageMetaData save(HBaseId mailboxId, MailboxMessage message) throws MailboxException { - HTable messages = null; - HTable mailboxes = null; - BufferedInputStream in = null; - ChunkOutputStream out = null; - try { - //TODO: update the mailbox information about messages - messages = new HTable(conf, MESSAGES_TABLE); - mailboxes = new HTable(conf, MAILBOXES_TABLE); - //save the message metadata - Put put = metadataToPut(message); - messages.put(put); - //save the message content - //TODO: current implementation is crude. - - int b; - out = new ChunkOutputStream(conf, - MESSAGES_TABLE, MESSAGE_DATA_BODY_CF, messageRowKey(message), MAX_COLUMN_SIZE); - in = new BufferedInputStream(message.getBodyContent()); - while ((b = in.read()) != -1) { - out.write(b); - } - in.close(); - out.close(); - out = new ChunkOutputStream(conf, - MESSAGES_TABLE, MESSAGE_DATA_HEADERS_CF, messageRowKey(message), MAX_COLUMN_SIZE); - in = new BufferedInputStream(message.getHeaderContent()); - while ((b = in.read()) != -1) { - out.write(b); - } - in.close(); - out.close(); - // increase the message count for the current mailbox - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_MESSAGE_COUNT, 1); - return new SimpleMessageMetaData(message); - } catch (IOException ex) { - throw new MailboxException("Error setting flags for messages in " + mailboxId, ex); - } finally { - if (messages != null) { - try { - messages.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + messages, ex); - } - } - if (mailboxes != null) { - try { - mailboxes.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + mailboxes, ex); - } - } - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing Inputtream", ex); - } - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing OutputStream", ex); - } - } - } - } - - private void deleteDeletedMessagesInMailboxWithUID(HBaseId mailboxId, MessageUid from) throws IOException { - //TODO: do I have to check if the message is flagged for delete here? - HTable messages = new HTable(conf, MESSAGES_TABLE); - HTable mailboxes = new HTable(conf, MAILBOXES_TABLE); - Delete delete = new Delete(messageRowKey(mailboxId, from)); - messages.delete(delete); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_MESSAGE_COUNT, -1); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ, 1); - mailboxes.close(); - messages.close(); - } - - private void deleteDeletedMessagesInMailboxBetweenUIDs(HBaseId mailboxId, MessageUid fromUid, MessageUid toUid) throws IOException { - HTable messages = new HTable(conf, MESSAGES_TABLE); - HTable mailboxes = new HTable(conf, MAILBOXES_TABLE); - List<Delete> deletes = new ArrayList<>(); - /*TODO: check if Between should be inclusive or exclusive regarding limits. - * HBase scan operaion are exclusive to the upper bound when providing stop row key. - */ - Scan scan = new Scan(messageRowKey(mailboxId, fromUid), messageRowKey(mailboxId, toUid)); - scan.addColumn(MESSAGES_META_CF, FLAGS_DELETED); - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - scan.setFilter(filter); - scan.setMaxVersions(1); - ResultScanner scanner = messages.getScanner(scan); - Result result; - while ((result = scanner.next()) != null) { - deletes.add(new Delete(result.getRow())); - } - long totalDeletes = deletes.size(); - scanner.close(); - messages.delete(deletes); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_MESSAGE_COUNT, -(totalDeletes - deletes.size())); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ, 1); - mailboxes.close(); - messages.close(); - } - - private void deleteDeletedMessagesInMailboxAfterUID(HBaseId mailboxId, MessageUid fromUid) throws IOException { - HTable messages = new HTable(conf, MESSAGES_TABLE); - HTable mailboxes = new HTable(conf, MAILBOXES_TABLE); - List<Delete> deletes = new ArrayList<>(); - /*TODO: check if Between should be inclusive or exclusive regarding limits. - * HBase scan operaion are exclusive to the upper bound when providing stop row key. - */ - Scan scan = new Scan(messageRowKey(mailboxId, fromUid)); - scan.addColumn(MESSAGES_META_CF, FLAGS_DELETED); - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - scan.setFilter(filter); - scan.setMaxVersions(1); - ResultScanner scanner = messages.getScanner(scan); - Result result; - while ((result = scanner.next()) != null) { - deletes.add(new Delete(result.getRow())); - } - long totalDeletes = deletes.size(); - scanner.close(); - messages.delete(deletes); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_MESSAGE_COUNT, -(totalDeletes - deletes.size())); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ, 1); - mailboxes.close(); - messages.close(); - } - - private void deleteDeletedMessagesInMailbox(HBaseId mailboxId) throws IOException { - HTable messages = new HTable(conf, MESSAGES_TABLE); - HTable mailboxes = new HTable(conf, MAILBOXES_TABLE); - List<Delete> deletes = new ArrayList<>(); - /*TODO: check if Between should be inclusive or exclusive regarding limits. - * HBase scan operaion are exclusive to the upper bound when providing stop row key. - */ - Scan scan = new Scan(minMessageRowKey(mailboxId), - new PrefixFilter(mailboxId.toBytes())); - scan.addColumn(MESSAGES_META_CF, FLAGS_DELETED); - SingleColumnValueFilter filter = new SingleColumnValueFilter(MESSAGES_META_CF, FLAGS_DELETED, CompareOp.EQUAL, MARKER_PRESENT); - scan.setFilter(filter); - scan.setMaxVersions(1); - ResultScanner scanner = messages.getScanner(scan); - Result result; - while ((result = scanner.next()) != null) { - deletes.add(new Delete(result.getRow())); - } - long totalDeletes = deletes.size(); - scanner.close(); - messages.delete(deletes); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_MESSAGE_COUNT, -(totalDeletes - deletes.size())); - mailboxes.incrementColumnValue(mailboxId.toBytes(), MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ, 1); - mailboxes.close(); - messages.close(); - } - - private Map<MessageUid, MessageMetaData> createMetaData(List<MailboxMessage> uids) { - final Map<MessageUid, MessageMetaData> data = new HashMap<>(); - for (MailboxMessage m : uids) { - data.put(m.getUid(), new SimpleMessageMetaData(m)); - } - return data; - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/81d65f1c/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseModSeqProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseModSeqProvider.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseModSeqProvider.java deleted file mode 100644 index 9828483..0000000 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseModSeqProvider.java +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************** - * 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.hbase.mail; - -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOXES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_HIGHEST_MODSEQ; - -import java.io.IOException; - -import org.apache.commons.lang.NotImplementedException; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.client.Get; -import org.apache.hadoop.hbase.client.HTable; -import org.apache.hadoop.hbase.client.Result; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.hbase.HBaseId; -import org.apache.james.mailbox.model.MailboxId; -import org.apache.james.mailbox.store.mail.ModSeqProvider; -import org.apache.james.mailbox.store.mail.model.Mailbox; - -/** - * ModSeqProvider implementation for HBase. - * - */ -public class HBaseModSeqProvider implements ModSeqProvider { - - /** Link to the HBase Configuration object and specific mailbox names */ - private final Configuration conf; - - public HBaseModSeqProvider(Configuration conf) { - this.conf = conf; - } - - @Override - public long highestModSeq(MailboxSession session, Mailbox mailbox) throws MailboxException { - HTable mailboxes = null; - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try { - mailboxes = new HTable(conf, MAILBOXES_TABLE); - Get get = new Get(mailboxId.toBytes()); - get.addColumn(MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ); - get.setMaxVersions(1); - Result result = mailboxes.get(get); - - if (result == null) { - throw new MailboxException("Row or column not found!"); - } - long modSeq = Bytes.toLong(result.getValue(MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ)); - return modSeq; - } catch (IOException e) { - throw new MailboxException("highestModSeq", e); - } finally { - if (mailboxes != null) { - try { - mailboxes.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + mailboxes, ex); - } - } - } - } - - @Override - public long nextModSeq(MailboxSession session, Mailbox mailbox) throws MailboxException { - return nextModSeq(session, mailbox.getMailboxId()); - } - - @Override - public long nextModSeq(MailboxSession session, MailboxId mailboxId) throws MailboxException { - HTable mailboxes = null; - HBaseId hbaseId = (HBaseId) mailboxId; - try { - mailboxes = new HTable(conf, MAILBOXES_TABLE); - long newValue = mailboxes.incrementColumnValue(hbaseId.toBytes(), MAILBOX_CF, MAILBOX_HIGHEST_MODSEQ, 1); - return newValue; - } catch (IOException e) { - throw new MailboxException("lastUid", e); - } finally { - if (mailboxes != null) { - try { - mailboxes.close(); - } catch (IOException ex) { - throw new MailboxException("Error closing table " + mailboxes, ex); - } - } - } - } - - - @Override - public long highestModSeq(MailboxSession session, MailboxId mailboxId) throws MailboxException { - throw new NotImplementedException(); - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/81d65f1c/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseUidProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseUidProvider.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseUidProvider.java deleted file mode 100644 index 3f5b0db..0000000 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseUidProvider.java +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************** - * 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.hbase.mail; - -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOXES_TABLE; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_CF; -import static org.apache.james.mailbox.hbase.HBaseNames.MAILBOX_LASTUID; - -import java.io.IOException; -import java.util.Optional; - -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.client.Get; -import org.apache.hadoop.hbase.client.HTable; -import org.apache.hadoop.hbase.client.Result; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MessageUid; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.hbase.HBaseId; -import org.apache.james.mailbox.model.MailboxId; -import org.apache.james.mailbox.store.mail.UidProvider; -import org.apache.james.mailbox.store.mail.model.Mailbox; - -/** - * Message UidProvider for HBase. - * - */ -public class HBaseUidProvider implements UidProvider { - - /** Link to the HBase Configuration object and specific mailbox names */ - private final Configuration conf; - - public HBaseUidProvider(Configuration conf) { - this.conf = conf; - } - - @Override - public Optional<MessageUid> lastUid(MailboxSession session, Mailbox mailbox) throws MailboxException { - HBaseId mailboxId = (HBaseId) mailbox.getMailboxId(); - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - Get get = new Get(mailboxId.toBytes()); - get.addColumn(MAILBOX_CF, MAILBOX_LASTUID); - get.setMaxVersions(1); - Result result = mailboxes.get(get); - - if (result == null) { - throw new MailboxException("Row or column not found!"); - } - long rawUid = Bytes.toLong(result.getValue(MAILBOX_CF, MAILBOX_LASTUID)); - if (rawUid == 0) { - return Optional.empty(); - } - return Optional.of(MessageUid.of(rawUid)); - } catch (IOException e) { - throw new MailboxException("lastUid", e); - } - } - - @Override - public MessageUid nextUid(MailboxSession session, Mailbox mailbox) throws MailboxException { - return nextUid(session, mailbox.getMailboxId()); - } - - @Override - public MessageUid nextUid(MailboxSession session, MailboxId mailboxId) throws MailboxException { - HBaseId hbaseId = (HBaseId) mailboxId; - try (HTable mailboxes = new HTable(conf, MAILBOXES_TABLE)) { - MessageUid newValue = MessageUid.of(mailboxes.incrementColumnValue(hbaseId.toBytes(), MAILBOX_CF, MAILBOX_LASTUID, 1)); - mailboxes.close(); - return newValue; - } catch (IOException e) { - throw new MailboxException("lastUid", e); - } - } - - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/81d65f1c/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java deleted file mode 100644 index 42f160d..0000000 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java +++ /dev/null @@ -1,198 +0,0 @@ -/**************************************************************** - * 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.hbase.mail.model; - -import java.util.UUID; - -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.hbase.HBaseId; -import org.apache.james.mailbox.model.MailboxACL; -import org.apache.james.mailbox.model.MailboxId; -import org.apache.james.mailbox.model.MailboxPath; -import org.apache.james.mailbox.store.mail.model.Mailbox; -import org.apache.james.mailbox.store.mail.model.MailboxUtil; - -/** - * This class implements a mailbox. Most of the code is done after mailbox-jpa - * implementations. - * - */ -public class HBaseMailbox implements Mailbox { - - private static final String TAB = " "; - /** The value for the mailboxId field */ - private HBaseId mailboxId; - /** The value for the name field */ - private String name; - /** The value for the uidValidity field */ - private final long uidValidity; - private String user; - private String namespace; - private long lastUid; - private long highestModSeq; - private long messageCount; - - public HBaseMailbox(MailboxPath mailboxPath, long uidValidity) { - super(); - this.name = mailboxPath.getName(); - this.user = mailboxPath.getUser(); - this.namespace = mailboxPath.getNamespace(); - this.uidValidity = uidValidity; - //TODO: this has to change to something that can guarantee that mailboxId is unique - this.mailboxId = HBaseId.of(UUID.randomUUID()); - } - - @Override - public MailboxPath generateAssociatedPath() { - return new MailboxPath(getNamespace(), getUser(), getName()); - } - - @Override - public HBaseId getMailboxId() { - return mailboxId; - } - - @Override - public void setMailboxId(MailboxId mailboxId) { - this.mailboxId = (HBaseId)mailboxId; - } - /* - * (non-Javadoc) - * @see org.apache.james.mailbox.store.mail.model.Mailbox#getNamespace() - */ - - @Override - public String getNamespace() { - return namespace; - } - - @Override - public void setNamespace(String namespace) { - this.namespace = namespace; - } - - @Override - public String getUser() { - return user; - } - - @Override - public void setUser(String user) { - this.user = user; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setName(String name) { - this.name = name; - } - - @Override - public long getUidValidity() { - return uidValidity; - } - - @Override - public String toString() { - return "Mailbox ( " - + "mailboxId = " + this.mailboxId + TAB - + "name = " + this.name + TAB - + "uidValidity = " + this.uidValidity + TAB - + " )"; - } - - @Override - public int hashCode() { - final int PRIME = 31; - int result = 1; - result = PRIME * result + mailboxId.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final HBaseMailbox other = (HBaseMailbox) obj; - if (!mailboxId.equals(other.getMailboxId())) { - return false; - } - return true; - } - - public long getLastUid() { - return lastUid; - } - - public void setLastUid(long lastUid) { - this.lastUid = lastUid; - } - - public long getHighestModSeq() { - return highestModSeq; - } - - public void setHighestModSeq(long highestModSeq) { - this.highestModSeq = highestModSeq; - } - - public long consumeUid() { - return ++lastUid; - } - - public long consumeModSeq() { - return ++highestModSeq; - } - - public long getMessageCount() { - return messageCount; - } - - public void setMessageCount(long messageCount) { - this.messageCount = messageCount; - } - - @Override - public MailboxACL getACL() { - // TODO ACL support - return MailboxACL.OWNER_FULL_ACL; - } - - @Override - public void setACL(MailboxACL acl) { - // TODO ACL support - } - - @Override - public boolean isChildOf(Mailbox potentialParent, MailboxSession mailboxSession) { - return MailboxUtil.isMailboxChildOf(this, potentialParent, mailboxSession); - } - -} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
