Skip to content

Commit

Permalink
IMAP-370 Factorize code between copies and moves
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Mar 4, 2016
1 parent ea9d77d commit b06992f
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 56 deletions.
@@ -0,0 +1,57 @@
/****************************************************************
* 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.store;

import java.util.ArrayList;
import java.util.List;

import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.store.mail.model.MailboxId;
import org.msgpack.core.Preconditions;

public class MessageBatcher {

public static final int NO_BATCH_SIZE = 0;

public interface BatchedOperation {
List<MessageRange> execute(MessageRange messageRange) throws MailboxException;
}

private final int moveBatchSize;

public MessageBatcher(int moveBatchSize) {
Preconditions.checkArgument(moveBatchSize >= NO_BATCH_SIZE);
this.moveBatchSize = moveBatchSize;
}

public List<MessageRange> batchMessages(MessageRange set, BatchedOperation batchedOperation) throws MailboxException {
if (moveBatchSize > 0) {
List<MessageRange> movedRanges = new ArrayList<MessageRange>();
for (MessageRange messageRange : set.split(moveBatchSize)) {
movedRanges.addAll(batchedOperation.execute(messageRange));
}
return movedRanges;
} else {
return batchedOperation.execute(set);
}
}

}
Expand Up @@ -21,7 +21,6 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Random;
Expand Down Expand Up @@ -99,9 +98,9 @@ public class StoreMailboxManager<Id extends MailboxId> implements MailboxManager

private final static Random RANDOM = new Random();

private int copyBatchSize = 0;
private MessageBatcher copyBatcher;

private int moveBatchSize = 0;
private MessageBatcher moveBatcher;

private final MailboxPathLocker locker;

Expand Down Expand Up @@ -148,11 +147,11 @@ public void setQuotaUpdater(QuotaUpdater quotaUpdater) {
}

public void setCopyBatchSize(int copyBatchSize) {
this.copyBatchSize = copyBatchSize;
this.copyBatcher = new MessageBatcher(copyBatchSize);
}

public void setMoveBatchSize(int moveBatchSize) {
this.moveBatchSize = moveBatchSize;
this.moveBatcher = new MessageBatcher(moveBatchSize);
}

public void setFetchBatchSize(int fetchBatchSize) {
Expand Down Expand Up @@ -189,6 +188,12 @@ public void init() throws MailboxException {
if (quotaUpdater != null && quotaUpdater instanceof MailboxListener) {
this.addGlobalListener((MailboxListener) quotaUpdater, null);
}
if (copyBatcher == null) {
copyBatcher = new MessageBatcher(MessageBatcher.NO_BATCH_SIZE);
}
if (moveBatcher == null) {
moveBatcher = new MessageBatcher(MessageBatcher.NO_BATCH_SIZE);
}
}

/**
Expand Down Expand Up @@ -505,38 +510,28 @@ public Void execute() throws MailboxException {

@Override
@SuppressWarnings("unchecked")
public List<MessageRange> copyMessages(MessageRange set, MailboxPath from, MailboxPath to, MailboxSession session) throws MailboxException {
StoreMessageManager<Id> toMailbox = (StoreMessageManager<Id>) getMailbox(to, session);
StoreMessageManager<Id> fromMailbox = (StoreMessageManager<Id>) getMailbox(from, session);

if (copyBatchSize > 0) {
List<MessageRange> copiedRanges = new ArrayList<MessageRange>();
Iterator<MessageRange> ranges = set.split(copyBatchSize).iterator();
while (ranges.hasNext()) {
copiedRanges.addAll(fromMailbox.copyTo(ranges.next(), toMailbox, session));
public List<MessageRange> copyMessages(MessageRange set, MailboxPath from, MailboxPath to, final MailboxSession session) throws MailboxException {
final StoreMessageManager<Id> toMailbox = (StoreMessageManager<Id>) getMailbox(to, session);
final StoreMessageManager<Id> fromMailbox = (StoreMessageManager<Id>) getMailbox(from, session);

return copyBatcher.batchMessages(set, new MessageBatcher.BatchedOperation() {
public List<MessageRange> execute(MessageRange messageRange) throws MailboxException {
return fromMailbox.copyTo(messageRange, toMailbox, session);
}
return copiedRanges;
} else {
return fromMailbox.copyTo(set, toMailbox, session);
}
});
}

@Override
@SuppressWarnings("unchecked")
public List<MessageRange> moveMessages(MessageRange set, MailboxPath from, MailboxPath to, MailboxSession session) throws MailboxException {
StoreMessageManager<Id> toMailbox = (StoreMessageManager<Id>) getMailbox(to, session);
StoreMessageManager<Id> fromMailbox = (StoreMessageManager<Id>) getMailbox(from, session);

if (moveBatchSize > 0) {
List<MessageRange> movedRanges = new ArrayList<MessageRange>();
Iterator<MessageRange> ranges = set.split(moveBatchSize).iterator();
while (ranges.hasNext()) {
movedRanges.addAll(fromMailbox.moveTo(ranges.next(), toMailbox, session));
public List<MessageRange> moveMessages(MessageRange set, MailboxPath from, MailboxPath to, final MailboxSession session) throws MailboxException {
final StoreMessageManager<Id> toMailbox = (StoreMessageManager<Id>) getMailbox(to, session);
final StoreMessageManager<Id> fromMailbox = (StoreMessageManager<Id>) getMailbox(from, session);

return moveBatcher.batchMessages(set, new MessageBatcher.BatchedOperation() {
public List<MessageRange> execute(MessageRange messageRange) throws MailboxException {
return fromMailbox.moveTo(messageRange, toMailbox, session);
}
return movedRanges;
} else {
return fromMailbox.moveTo(set, toMailbox, session);
}
});
}

@Override
Expand Down
Expand Up @@ -51,6 +51,7 @@
import org.apache.james.mailbox.exception.UnsupportedRightException;
import org.apache.james.mailbox.model.MailboxACL;
import org.apache.james.mailbox.model.MailboxACL.MailboxACLRights;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MessageResult.FetchGroup;
Expand Down Expand Up @@ -735,41 +736,29 @@ public MessageMetaData run() throws MailboxException {
}


/**
* @see org.apache.james.mailbox.store.AbstractStoreMessageManager#copy(org.apache.james.mailbox.model.MessageRange,
* org.apache.james.mailbox.store.AbstractStoreMessageManager,
* org.apache.james.mailbox.MailboxSession)
*/
private SortedMap<Long, MessageMetaData> copy(MessageRange set, StoreMessageManager<Id> to, MailboxSession session) throws MailboxException {
Iterator<MailboxMessage<Id>> originalRows = retrieveOriginalRows(set, session);
return collectMetadata(to.copy(originalRows, session));
}

private SortedMap<Long, MessageMetaData> move(MessageRange set, StoreMessageManager<Id> to, MailboxSession session) throws MailboxException {
Iterator<MailboxMessage<Id>> originalRows = retrieveOriginalRows(set, session);
return collectMetadata(to.move(originalRows, session));
}

private Iterator<MailboxMessage<Id>> retrieveOriginalRows(MessageRange set, MailboxSession session) throws MailboxException {
MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
return messageMapper.findInMailbox(mailbox, set, FetchType.Full, -1);
}

private SortedMap<Long, MessageMetaData> collectMetadata(Iterator<MessageMetaData> ids) {
final SortedMap<Long, MessageMetaData> copiedMessages = new TreeMap<Long, MessageMetaData>();
Iterator<MailboxMessage<Id>> originalRows = messageMapper.findInMailbox(mailbox, set, FetchType.Full, -1);
Iterator<MessageMetaData> ids = to.copy(originalRows, session);
while (ids.hasNext()) {
MessageMetaData data = ids.next();
copiedMessages.put(data.getUid(), data);
}

return copiedMessages;
}

private SortedMap<Long, MessageMetaData> move(MessageRange set,
final StoreMessageManager<Id> to, MailboxSession session) throws MailboxException {
MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);

final SortedMap<Long, MessageMetaData> movedMessages = new TreeMap<Long, MessageMetaData>();
Iterator<MailboxMessage<Id>> originalRows = messageMapper.findInMailbox(mailbox, set, FetchType.Full, -1);
Iterator<MessageMetaData> ids = to.move(originalRows, session);
while (ids.hasNext()) {
MessageMetaData data = ids.next();
movedMessages.put(data.getUid(), data);
}

return movedMessages;
}


/**
* Return the count of unseen messages
*
Expand Down
@@ -0,0 +1,71 @@
/****************************************************************
* 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.store;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MessageRange;
import org.junit.Test;

import com.google.common.collect.Lists;

public class MessageBatcherTest {

private MessageBatcher.BatchedOperation incrementBatcher = new MessageBatcher.BatchedOperation() {
@Override
public List<MessageRange> execute(MessageRange messageRange) throws MailboxException {
return Lists.<MessageRange>newArrayList(MessageRange.range(messageRange.getUidFrom() + 1, messageRange.getUidTo() + 1));
}
};

@Test
public void batchMessagesShouldWorkOnSingleRangeMode() throws Exception {
MessageBatcher messageBatcher = new MessageBatcher(0);

assertThat(messageBatcher.batchMessages(MessageRange.range(1, 10), incrementBatcher)).containsOnly(MessageRange.range(2, 11));
}

@Test
public void batchMessagesShouldWorkWithNonZeroBatchedSize() throws Exception {
MessageBatcher messageBatcher = new MessageBatcher(5);

assertThat(messageBatcher.batchMessages(MessageRange.range(1, 10), incrementBatcher)).containsOnly(MessageRange.range(2, 6), MessageRange.range(7, 11));
}

@Test(expected = MailboxException.class)
public void batchMessagesShouldPropagateExceptions() throws Exception {
MessageBatcher messageBatcher = new MessageBatcher(0);

messageBatcher.batchMessages(MessageRange.range(1, 10), new MessageBatcher.BatchedOperation() {
public List<MessageRange> execute(MessageRange messageRange) throws MailboxException {
throw new MailboxException();
}
});
}

@Test(expected = IllegalArgumentException.class)
public void messageBatcherShouldThrowOnNegativeBatchSize() throws Exception {
MessageBatcher messageBatcher = new MessageBatcher(-1);
}

}

0 comments on commit b06992f

Please sign in to comment.