Alan Davis a27004cb42 Merged HEAD-BUG-FIX (5.1/Cloud) to HEAD (5.1/Cloud)
92321: Merged 5.0.N (5.0.1) to HEAD-BUG-FIX (5.1/Cloud)
      92221: Merged V4.2-BUG-FIX (4.2.5) to 5.0.N (5.0.1)
         92039: Merged V4.2.1 (4.2.1.18) to V4.2-BUG-FIX (4.2.5)
            91922: MNT-12903 : Outlook 2013: moving a file from a folder to another makes it a EML file
               - Delete delay is configurable. Logging has been added to CommandCallback


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@94866 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2015-01-31 11:47:08 +00:00

501 lines
16 KiB
Java

/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.imap;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.mail.Flags;
import javax.mail.internet.MimeMessage;
import javax.mail.search.SearchTerm;
import org.alfresco.repo.imap.exception.AlfrescoImapFolderException;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.icegreen.greenmail.foedus.util.MsgRangeFilter;
import com.icegreen.greenmail.mail.MovingMessage;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.store.FolderListener;
import com.icegreen.greenmail.store.MailFolder;
import com.icegreen.greenmail.store.SimpleStoredMessage;
/**
* Implementation of greenmail MailFolder. It represents an Alfresco content folder and handles
* appendMessage, copyMessage, expunge (delete), getMessages, getMessage and so requests.
*
* @author Ivan Rybnikov
*/
public abstract class AbstractImapFolder implements MailFolder
{
private List<FolderListener> listeners = new LinkedList<FolderListener>();
protected Log logger = LogFactory.getLog(getClass());
protected ServiceRegistry serviceRegistry;
protected static int MAX_RETRIES = 20;
public AbstractImapFolder(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/**
* Method that checks mandatory parameter.
* @param The parameter instance to check.
* @param The name of the parameter.
*/
protected void checkParameter(Object parameter, String name)
{
if (parameter == null)
{
throw new IllegalArgumentException(name + " parameter is null.");
}
}
/**
* Appends message to the folder.
*
* @param message - message.
* @param flags - message flags.
* @param internalDate - not used. Current date used instead.
* @return
*/
public long appendMessage(final MimeMessage message, final Flags flags, final Date internalDate) throws FolderException
{
if (isReadOnly())
{
throw new FolderException("Can't append message - Permission denied");
}
CommandCallback<Long> command = new CommandCallback<Long>()
{
public Long command() throws Throwable
{
return appendMessageInternal(message, flags, internalDate);
}
};
return command.runFeedback();
}
/**
* Copies message with the given UID to the specified {@link MailFolder}.
*
* @param uid - UID of the message
* @param toFolder - reference to the destination folder.
*/
public long copyMessage(final long uid, final MailFolder toFolder) throws FolderException
{
AbstractImapFolder toImapMailFolder = (AbstractImapFolder) toFolder;
if (toImapMailFolder.isReadOnly())
{
throw new FolderException(AlfrescoImapFolderException.PERMISSION_DENIED);
}
CommandCallback<Long> command = new CommandCallback<Long>()
{
public Long command() throws Throwable
{
return copyMessageInternal(uid, toFolder);
}
};
return command.runFeedback();
}
/**
* Marks all messages in the folder as deleted using {@link Flags.Flag#DELETED} flag.
*/
public void deleteAllMessages() throws FolderException
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
deleteAllMessagesInternal();
return null;
}
};
command.runFeedback();
}
/**
* Deletes messages marked with {@link Flags.Flag#DELETED}. Note that this message deletes all messages with this flag.
*/
public void expunge() throws FolderException
{
if (isReadOnly())
{
throw new FolderException("Can't expunge - Permission denied");
}
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
expungeInternal();
return null;
}
};
command.runFeedback();
}
/**
* Deletes messages marked with {@link Flags.Flag#DELETED}. Note that this message deletes the messages with current uid
*/
public void expunge(final long uid) throws FolderException
{
if (isReadOnly())
{
throw new FolderException("Can't expunge - Permission denied");
}
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
expungeInternal(uid);
return null;
}
};
command.runFeedback();
}
/**
* Returns message by its UID.
*
* @param uid - UID of the message.
* @return message.
*/
public SimpleStoredMessage getMessage(final long uid)
{
CommandCallback<SimpleStoredMessage> command = new CommandCallback<SimpleStoredMessage>()
{
public SimpleStoredMessage command() throws Throwable
{
return getMessageInternal(uid);
}
};
return command.run();
}
/**
* Returns list of all messages in the folder.
*
* @return list of {@link SimpleStoredMessage} objects.
*/
public List<SimpleStoredMessage> getMessages()
{
CommandCallback<List<SimpleStoredMessage>> command = new CommandCallback<List<SimpleStoredMessage>>()
{
public List<SimpleStoredMessage> command() throws Throwable
{
return getMessagesInternal();
}
};
return command.run();
}
/**
* Returns list of messages by filter.
*
* @param msgRangeFilter - {@link MsgRangeFilter} object representing filter.
* @return list of filtered messages.
*/
public List<SimpleStoredMessage> getMessages(final MsgRangeFilter msgRangeFilter)
{
CommandCallback <List<SimpleStoredMessage>> command = new CommandCallback <List<SimpleStoredMessage>>()
{
public List<SimpleStoredMessage> command() throws Throwable
{
return getMessagesInternal(msgRangeFilter);
}
};
return command.run();
}
/**
* Returns the list of messages that have no {@link Flags.Flag#DELETED} flag set for current user.
*
* @return the list of non-deleted messages.
*/
public List<SimpleStoredMessage> getNonDeletedMessages()
{
CommandCallback <List<SimpleStoredMessage>> command = new CommandCallback<List<SimpleStoredMessage>>()
{
public List<SimpleStoredMessage> command() throws Throwable
{
return getNonDeletedMessagesInternal();
}
};
List<SimpleStoredMessage> result = (List<SimpleStoredMessage>)command.run();
return result;
}
/**
* Replaces flags for the message with the given UID. If {@code addUid} is set to
* {@code true} {@link FolderListener} objects defined for this folder will be notified.
* {@code silentListener} can be provided - this listener wouldn't be notified.
*
* @param flags - new flags.
* @param uid - message UID.
* @param silentListener - listener that shouldn't be notified.
* @param addUid - defines whether or not listeners be notified.
*/
public void replaceFlags(final Flags flags, final long uid, final FolderListener silentListener, final boolean addUid) throws FolderException
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
replaceFlagsInternal(flags, uid, silentListener, addUid);
return null;
}
};
command.runFeedback();
}
/**
* Searches the mailbox for messages that match the given searching criteria
*
* @param searchTerm - search term that contains search criteria.
* @return UIDs of the messages
*/
public long[] search(final SearchTerm searchTerm)
{
CommandCallback<long[]> command = new CommandCallback<long[]>()
{
public long[] command() throws Throwable
{
return searchInternal(searchTerm);
}
};
return command.run();
}
/**
* Sets flags for the message with the given UID. If {@code addUid} is set to {@code true}
* {@link FolderListener} objects defined for this folder will be notified.
* {@code silentListener} can be provided - this listener wouldn't be notified.
*
* @param flags - new flags.
* @param value - flags value.
* @param uid - message UID.
* @param silentListener - listener that shouldn't be notified.
* @param addUid - defines whether or not listeners be notified.
*/
public void setFlags(
final Flags flags,
final boolean value,
final long uid,
final FolderListener silentListener,
final boolean addUid)
throws FolderException
{
CommandCallback<Object> command = new CommandCallback<Object>()
{
public Object command() throws Throwable
{
setFlagsInternal(flags, value, uid, silentListener, addUid);
return null;
}
};
command.runFeedback();
}
/**
* Not supported. Added to implement {@link MailFolder#store(MovingMessage)}.
*/
public void store(MovingMessage mail) throws Exception
{
throw new UnsupportedOperationException("Method store(MovingMessage) is not suppoted.");
}
/**
* Not supported. Added to implement {@link MailFolder#store(MimeMessage)}.
*/
public void store(MimeMessage message) throws Exception
{
throw new UnsupportedOperationException("Method store(MimeMessage) is not suppoted.");
}
/**
* Adds {@link FolderListener} to the folder.
*
* @param listener - new listener.
*/
public void addListener(FolderListener listener)
{
listeners.add(listener);
}
/**
* Removes {@link FolderListener} from the folder.
*
* @param listener - Listener to remove.
*/
public void removeListener(FolderListener listener)
{
listeners.remove(listener);
}
/**
* Method is called before the deletion of the folder. Notifies {@link FolderListener} objects with
* {@link FolderListener#mailboxDeleted()} method calls.
*/
public void signalDeletion()
{
synchronized (listeners)
{
for (int i = 0; i < listeners.size(); i++)
{
FolderListener listener = (FolderListener) listeners.get(i);
listener.mailboxDeleted();
}
}
}
protected void notifyFlagUpdate(int msn, Flags flags, Long uidNotification, FolderListener silentListener)
{
synchronized (listeners)
{
for (FolderListener listener : listeners)
{
if (listener == silentListener)
{
continue;
}
listener.flagsUpdated(msn, flags, uidNotification);
}
}
}
protected abstract boolean isReadOnly();
protected abstract long appendMessageInternal(MimeMessage message, Flags flags, Date internalDate) throws Exception;
protected abstract long copyMessageInternal(long uid, MailFolder toFolder) throws Exception;
protected abstract void deleteAllMessagesInternal() throws Exception;
protected abstract void expungeInternal() throws Exception;
protected abstract void expungeInternal(long uid) throws Exception;
protected abstract SimpleStoredMessage getMessageInternal(long uid) throws Exception;
protected abstract List<SimpleStoredMessage> getMessagesInternal();
protected abstract List<SimpleStoredMessage> getMessagesInternal(MsgRangeFilter msgRangeFilter);
protected abstract List<SimpleStoredMessage> getNonDeletedMessagesInternal();
protected abstract void replaceFlagsInternal(Flags flags, long uid, FolderListener silentListener, boolean addUid) throws Exception;
protected abstract long[] searchInternal(SearchTerm searchTerm);
protected abstract void setFlagsInternal(Flags flags, boolean value, long uid, FolderListener silentListener, boolean addUid) throws Exception;
protected abstract class CommandCallback<T>
{
public abstract T command() throws Throwable;
public T runFeedback() throws FolderException
{
return this.runFeedback(false);
}
public T runFeedback(boolean readOnly) throws FolderException
{
try
{
RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper();
txHelper.setMaxRetries(MAX_RETRIES);
txHelper.setReadOnly(readOnly);
T result = txHelper.doInTransaction(
new RetryingTransactionCallback<T>()
{
public T execute() throws Throwable
{
return command();
}
}, readOnly);
return result;
}
catch (Exception e)
{
Throwable cause = e.getCause();
if (logger.isDebugEnabled())
{
logger.debug("Exception is thrown : " + e + "\nCause : " + cause);
}
String message;
if (cause != null)
{
message = cause.getMessage();
}
else
{
message = e.getMessage();
}
throw new FolderException(message);
}
}
public T run()
{
return this.run(false);
}
public T run(boolean readOnly)
{
RetryingTransactionHelper txHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper();
txHelper.setMaxRetries(MAX_RETRIES);
txHelper.setReadOnly(readOnly);
T result = txHelper.doInTransaction(
new RetryingTransactionCallback<T>()
{
public T execute() throws Throwable
{
return command();
}
}, readOnly);
return result;
}
}
}