Merged V3.3-BUG-FIX to HEAD

23080: Fix for ALF-3815 - Error occur on creating user (Active Directory +LDAP authentication)
   23084: MERGED DEV to V3.3-BUG-FIX
      22839 :  ALF-4920 - IMAP server UID failure
   23102: Checked in file with my Hostname!
   23141: Merged PATCHES/V3.2.0 to V3.3-BUG-FIX
      22977: ALF-5057: Don't use lucene to locate tag nodes - unreliable in a cluster
         - CategoryService extended with root category retrieval method using node service
      23043: ALF-5057: Merged V3.2 to PATCHES/V3.2.0 (partial)
         18052: Merged DEV/REPO-DOCLIB to V3.2
            17653: Checkpoint Repo DocLib prototype work
   23142: Merged PATCHES/V3.2.0 to V3.3-BUG-FIX
      22981: ALF-5141: Need to limit webscript response times and reject traffic at high load
         - server.web.transaction.max-duration-ms property now specifies a maximum time for repository webscript transaction execution. Default is 10 seconds.
         - transaction retrying will not continue when the projected time is greater than this
         - Once a transaction hits this execution time the number of concurrently executing transactions at the time it was started becomes the ‘ceiling’ for the number of concurrent transactions
         - The ceiling will dynamically rise and fall, based on transaction execution times
         - When a transaction is started ‘above’ the current ceiling a TooBusyException is thrown, which is mapped to an immediate status 503 response
         - New unit test added for this
      23006: ALF-5141: Reverting IndexInfo changes
         - 'fairness' flag on ReentrantReadWriteLock appears to cause deadlock on JDK 1.5 in IndexInfoTest
         - lucene.indexer.maxMergeWait property and associated throttling 'back off' behaviour abandoned as it has the risk of leaving indexes in incomplete uncommited state
         - transaction limiter feature should be enough to avoid excessive wait times
      23011: ALF-5141: Reintroduce fair locking to IndexInfo and fix RetryingTransactionHelperTest
         - Bugs surrounding ReentrantReadWriteLock in old JVMs mean that it's not safe to make fair locking the default
         - However, it would be useful in new JVMs as it should guarantee that we don't lock out waiting writers indefinitely under high load
         - Now controlled by lucene.indexer.fairLocking property. Default value is false in V3.2.0 but true in V3.3.4 onwards.
         - RetryingTransactionHelperTest now uses latches to ensure test threads start up in strict sequential order
      23014: ALF-5141: Correct error that could allow transaction ceiling to be lowered to zero
   23146: (RECORD ONLY) ALF-5028: Merged HEAD to V3.3-BUG-FIX
      21471: SAIL-240 (SAIL-294) AuditDAO: AuditService enhancements
         - Added isAuditEnabled and enableAudit for global case (system-wide)
         - Some neatening up of Audit SQL (common WHERE and ORDER BY clauses)
         - AuditService enforces 'admin' role for all methods
      22109: ALF-4106: Added entry deletion count return value for clear()
      22726: Coding standards
      22857: Fix typo in javadoc
      22980: Added AuditService.clearAudit(List<Long>)
      22986: ALF-5028 - Tagging Service Update - Use the audit service as a persisted event log, so that tag scope updates can occur in batches and without contention issues. (Further tests and post-startup executor still needed)
         This commit enables the Audit Service by default, but turns off all the audit applications except tagging by default, so there shouldn't be any noticable changes
      22997: ALF-5028 - More tag scope updates and unit tests. Shortly after the system is started, check for un-applied tag scope updates, and apply them.
      23015: ALF-5028: Tagging test mods
         - Join onto first-level threads to be sure that first round of tagging has been done
         - Double-checks for transaction leaks (found 1)
         - Some formatting (new test only, but should be applied to file)
   23148: Merged PATCHES/V3.2.0 to V3.3-BUG-FIX
      23133: ALF-5221: Fixed file handle leaks in TaggingService
   23149: Merged V3.2 to V3.3-BUG-FIX
      23070: Part-fix ALF-5134: Performance of Alfresco cluster less than performance of single node
         - Prevent cache being updated even when there are no changes
         - Prevents some cache invalidation messages during read operations
      23071: ALF-5134: Performance of Alfresco cluster less than performance of single node
         - Removed null-equivalence check in TransactionalCache
         - Avoids cache update messages when running against empty caches
   23150: (RECORD ONLY) ALF-5235: Merged HEAD to V3.3-BUG-FIX
      22695: ALF-3800 "File is uploaded to the Document Library when its size more than user quota": make sure the exception is thrown back up to the transaction machinery to perform a rollback
   23156: Merged V3.3 to V3.3-BUG-FIX
      22913: Add jars back into Tomcat bundles
      23028: Merged DEV to V33:
         23022: ALF-4760 : XAM post-retention cleanup job: XAMArchiveJob
            1. Post-retention xam cleanup job was implemented according to requirements provided by Derek.
            2. Unit tests was added for new functionality.
      23125: Merged HEAD to V3.3
         20752: BatchProcessor is fed work by a BatchProcessWorkProvider
         22297: Fixed ALF-4676: WorkProviderIterator over BatchProcessWorkProvider does not fetch all results
      23126: (RECORD ONLY) Merged BRANCHES/DEV/V3.3-BUG-FIX to BRANCHES/V3.3:
         22883: ALF-4800 - AVM - intermittent test failure (layered file delete)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@23161 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Dave Ward
2010-10-15 15:43:21 +00:00
parent 4240e7d2dc
commit e0b40d177f
19 changed files with 1340 additions and 252 deletions

View File

@@ -281,6 +281,10 @@ public abstract class AbstractMimeMessage extends MimeMessage
return model;
}
protected void updateMessageID() throws MessagingException
{
setHeader("Message-ID", this.messageFileInfo.getNodeRef().getId());
}
}

View File

@@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -127,6 +128,35 @@ public class AlfrescoImapFolder extends AbstractImapFolder
private Map<Long, SimpleStoredMessage> messages = new TreeMap<Long, SimpleStoredMessage>();
private Map<Long, Integer> msnCache = new HashMap<Long, Integer>();
private Map<Long, CacheItem> messagesCache = Collections.synchronizedMap(new MaxSizeMap<Long, CacheItem>(10, CACHE_SIZE));
/**
* Map that ejects the last recently used element(s) to keep the size to a
* specified maximum
*
* @param <K> Key
* @param <V> Value
*/
private class MaxSizeMap<K,V> extends LinkedHashMap<K,V>
{
private int maxSize;
public MaxSizeMap(int initialSize, int maxSize)
{
super(initialSize, 0.75f, true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest)
{
boolean remove = super.size() > this.maxSize;
return remove;
}
}
private final static int CACHE_SIZE = 20;
private static final Flags PERMANENT_FLAGS = new Flags();
@@ -441,10 +471,40 @@ public class AlfrescoImapFolder extends AbstractImapFolder
*/
@Override
protected SimpleStoredMessage getMessageInternal(long uid) throws MessagingException
{
{
AbstractMimeMessage mes = (AbstractMimeMessage) messages.get(uid).getMimeMessage();
FileInfo mesInfo = mes.getMessageInfo();
return createImapMessage(mesInfo, uid, true);
Date modified = (Date) serviceRegistry.getNodeService().getProperty(mesInfo.getNodeRef(), ContentModel.PROP_MODIFIED);
if(modified != null)
{
CacheItem cached = messagesCache.get(uid);
if (cached != null)
{
if (logger.isDebugEnabled())
{
logger.debug("retrieved message from cache uid: " + uid);
}
if (cached.getModified().equals(modified))
{
return cached.getMessage();
}
}
SimpleStoredMessage message = createImapMessage(mesInfo, uid, true);
messagesCache.put(uid, new CacheItem(modified, message));
if (logger.isDebugEnabled())
{
logger.debug("caching message uid: " + uid + " cacheSize: " + messagesCache.size());
}
return message;
}
else
{
SimpleStoredMessage message = createImapMessage(mesInfo, uid, true);
return message;
}
}
/**
@@ -1055,4 +1115,36 @@ public class AlfrescoImapFolder extends AbstractImapFolder
FileCopyUtils.copy(part.getInputStream(), os);
}
class CacheItem
{
private Date modified;
private SimpleStoredMessage message;
public CacheItem(Date modified, SimpleStoredMessage message)
{
this.setMessage(message);
this.setModified(modified);
}
public void setModified(Date modified)
{
this.modified = modified;
}
public Date getModified()
{
return modified;
}
public void setMessage(SimpleStoredMessage message)
{
this.message = message;
}
public SimpleStoredMessage getMessage()
{
return message;
}
}
}

View File

@@ -25,6 +25,7 @@ import java.util.Map;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
@@ -93,7 +94,7 @@ public class ContentModelMessage extends AbstractMimeMessage
*/
private Multipart buildContentModelMultipart() throws MessagingException
{
MimeMultipart rootMultipart = new MimeMultipart("alternative");
MimeMultipart rootMultipart = new AlfrescoMimeMultipart("alternative", this.messageFileInfo);
// Cite MOB-395: "email agent will be used to select an appropriate template" - we are not able to
// detect an email agent so we use a default template for all messages.
// See AlfrescoImapConst to see the possible templates to use.
@@ -113,4 +114,25 @@ public class ContentModelMessage extends AbstractMimeMessage
return result;
}
class AlfrescoMimeMultipart extends MimeMultipart
{
public AlfrescoMimeMultipart(String subtype, FileInfo messageFileInfo)
{
super();
String boundary = getBoundaryValue(messageFileInfo);
ContentType cType = new ContentType("multipart", subtype, null);
cType.setParameter("boundary", boundary);
contentType = cType.toString();
}
public String getBoundaryValue(FileInfo messageFileInfo)
{
StringBuffer s = new StringBuffer();
s.append("----=_Part_").append(messageFileInfo.getNodeRef().getId());
return s.toString();
}
}
}

View File

@@ -0,0 +1,523 @@
/*
* 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.io.BufferedInputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.config.RepositoryFolderConfigBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.sun.mail.iap.ProtocolException;
import com.sun.mail.iap.Response;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.protocol.BODY;
import com.sun.mail.imap.protocol.FetchResponse;
import com.sun.mail.imap.protocol.IMAPProtocol;
import com.sun.mail.imap.protocol.UID;
import junit.framework.TestCase;
public class ImapMessageTest extends TestCase
{
private static Log logger = LogFactory.getLog(ImapMessageTest.class);
// IMAP client settings
private static final String PROTOCOL = "imap";
private static final String HOST = "localhost";
private static final int PORT = 143;
private static final String ADMIN_USER_NAME = "admin";
private static final String ADMIN_USER_PASSWORD = "admin";
private static final String IMAP_FOLDER_NAME = "test";
private Session session = null;
private Store store = null;
private IMAPFolder folder = null;
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
private NodeService nodeService;
private ImporterService importerService;
private PersonService personService;
private SearchService searchService;
private NamespaceService namespaceService;
private FileFolderService fileFolderService;
private MutableAuthenticationService authenticationService;
String anotherUserName;
private NodeRef testImapFolderNodeRef;
private NodeRef storeRootNodeRef;
private final String storePath = "workspace://SpacesStore";
private final String companyHomePathInStore = "/app:company_home";
private static final String TEST_FOLDER = "Alfresco IMAP/" + IMAP_FOLDER_NAME + "/___-___folder_a/" + "___-___folder_a_a";
private static final String TEST_FILE = "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME + "/" + NamespaceService.CONTENT_MODEL_PREFIX
+ ":___-___folder_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___folder_a_a/" + NamespaceService.CONTENT_MODEL_PREFIX + ":___-___file_a_a";
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
transactionService = serviceRegistry.getTransactionService();
nodeService = serviceRegistry.getNodeService();
importerService = serviceRegistry.getImporterService();
personService = serviceRegistry.getPersonService();
authenticationService = serviceRegistry.getAuthenticationService();
searchService = serviceRegistry.getSearchService();
namespaceService = serviceRegistry.getNamespaceService();
fileFolderService = serviceRegistry.getFileFolderService();
// start the transaction
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
authenticationService.authenticate(ADMIN_USER_NAME, ADMIN_USER_PASSWORD.toCharArray());
// downgrade integrity
IntegrityChecker.setWarnInTransaction();
anotherUserName = "user" + System.currentTimeMillis();
PropertyMap testUser = new PropertyMap();
testUser.put(ContentModel.PROP_USERNAME, anotherUserName);
testUser.put(ContentModel.PROP_FIRSTNAME, anotherUserName);
testUser.put(ContentModel.PROP_LASTNAME, anotherUserName);
testUser.put(ContentModel.PROP_EMAIL, anotherUserName + "@alfresco.com");
testUser.put(ContentModel.PROP_JOBTITLE, "jobTitle");
personService.createPerson(testUser);
// create the ACEGI Authentication instance for the new user
authenticationService.createAuthentication(anotherUserName, anotherUserName.toCharArray());
StoreRef storeRef = new StoreRef(storePath);
storeRootNodeRef = nodeService.getRootNode(storeRef);
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false);
NodeRef companyHomeNodeRef = nodeRefs.get(0);
nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
namespaceService, false);
if (nodeRefs != null && nodeRefs.size() > 0)
{
fileFolderService.delete(nodeRefs.get(0));
}
ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap");
ApplicationContext imapCtx = imap.getApplicationContext();
ImapServiceImpl imapServiceImpl = (ImapServiceImpl) imapCtx.getBean("imapService");
// Creating IMAP test folder for IMAP root
LinkedList<String> folders = new LinkedList<String>();
folders.add(IMAP_FOLDER_NAME);
FileFolderUtil.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER);
// Setting IMAP root
RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean();
imapHome.setStore(storePath);
imapHome.setRootPath(companyHomePathInStore);
imapHome.setFolderPath(IMAP_FOLDER_NAME);
imapServiceImpl.setImapHome(imapHome);
// Starting IMAP
imapServiceImpl.startup();
nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
namespaceService, false);
testImapFolderNodeRef = nodeRefs.get(0);
/*
* Importing test folders: Test folder contains: "___-___folder_a" "___-___folder_a" contains: "___-___folder_a_a", "___-___file_a", "Message_485.eml" (this is IMAP
* Message) "___-___folder_a_a" contains: "____-____file_a_a"
*/
importInternal("imap/imapservice_test_folder_a.acp", testImapFolderNodeRef);
txn.commit();
// Init mail client session
Properties props = new Properties();
props.setProperty("mail.imap.partialfetch", "false");
this.session = Session.getDefaultInstance(props, null);
// Get the store
this.store = session.getStore(PROTOCOL);
this.store.connect(HOST, PORT, anotherUserName, anotherUserName);
// Get folder
folder = (IMAPFolder) store.getFolder(TEST_FOLDER);
folder.open(Folder.READ_ONLY);
}
private void importInternal(String acpName, NodeRef space) throws IOException
{
// Importing IMAP test acp
ClassPathResource acpResource = new ClassPathResource(acpName);
ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null);
Location importLocation = new Location(space);
importerService.importView(acpHandler, importLocation, null, null);
}
public void testMessageModifiedBetweenReads() throws Exception
{
// Get test message UID
final Long uid = getMessageUid(folder, 1);
// Get Message size
final int count = getMessageSize(folder, uid);
// Get first part
BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
// Modify message. The size of letter describing the node may change
// These changes should be committed because it should be visible from client
NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
ContentWriter writer = fileFolderService.getWriter(contentNode);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 2000; i++)
{
sb.append("test string");
}
writer.putContent(sb.toString());
txn.commit();
// Read second message part
BODY bodyRest = getMessageBodyPart(folder, uid, count - 10, 10);
// Creating and parsing message from 2 parts
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
new BufferedInputStream(bodyRest.getByteArrayInputStream())));
// Reading first part - should be successful
MimeMultipart content = (MimeMultipart) message.getContent();
assertNotNull(content.getBodyPart(0).getContent());
try
{
// Reading second part cause error
content.getBodyPart(1).getContent();
fail("Should raise an IOException");
}
catch (IOException e)
{
}
}
public void testMessageRenamedBetweenReads() throws Exception
{
// Get test message UID
final Long uid = getMessageUid(folder, 1);
// Get Message size
final int count = getMessageSize(folder, uid);
// Get first part
BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
// Rename message. The size of letter describing the node will change
// These changes should be committed because it should be visible from client
NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
fileFolderService.rename(contentNode, "testtesttesttesttesttesttesttesttesttest");
txn.commit();
// Read second message part
BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100);
// Creating and parsing message from 2 parts
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
new BufferedInputStream(bodyRest.getByteArrayInputStream())));
// Reading first part - should be successful
MimeMultipart content = (MimeMultipart) message.getContent();
assertNotNull(content.getBodyPart(0).getContent());
try
{
// Reading second part cause error
content.getBodyPart(1).getContent();
fail("Should raise an IOException");
}
catch (IOException e)
{
}
}
public void testMessageCache() throws Exception
{
// Create messages
NodeRef contentNode = findNode(companyHomePathInStore + TEST_FILE);
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
// Create messages more than cache capacity
for (int i = 0; i < 51; i++)
{
FileInfo fi = fileFolderService.create(nodeService.getParentAssocs(contentNode).get(0).getParentRef(), "test" + i, ContentModel.TYPE_CONTENT);
ContentWriter writer = fileFolderService.getWriter(fi.getNodeRef());
writer.putContent("test");
}
txn.commit();
// Reload folder
folder.close(false);
folder = (IMAPFolder) store.getFolder(TEST_FOLDER);
folder.open(Folder.READ_ONLY);
// Read all messages
for (int i = 1; i < 51; i++)
{
// Get test message UID
final Long uid = getMessageUid(folder, i);
// Get Message size
final int count = getMessageSize(folder, uid);
// Get first part
BODY body = getMessageBodyPart(folder, uid, 0, count - 100);
// Read second message part
BODY bodyRest = getMessageBodyPart(folder, uid, count - 100, 100);
// Creating and parsing message from 2 parts
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
new BufferedInputStream(bodyRest.getByteArrayInputStream())));
// Reading first part - should be successful
MimeMultipart content = (MimeMultipart) message.getContent();
assertNotNull(content.getBodyPart(0).getContent());
assertNotNull(content.getBodyPart(1).getContent());
}
}
public void testUnmodifiedMessage() throws Exception
{
// Get test message UID
final Long uid = getMessageUid(folder, 1);
// Get Message size
final int count = getMessageSize(folder, uid);
// Make multiple message reading
for (int i = 0; i < 100; i++)
{
// Get random offset
int n = (int) ((int) 100 * Math.random());
// Get first part
BODY body = getMessageBodyPart(folder, uid, 0, count - n);
// Read second message part
BODY bodyRest = getMessageBodyPart(folder, uid, count - n, n);
// Creating and parsing message from 2 parts
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), new SequenceInputStream(new BufferedInputStream(body.getByteArrayInputStream()),
new BufferedInputStream(bodyRest.getByteArrayInputStream())));
MimeMultipart content = (MimeMultipart) message.getContent();
// Reading first part - should be successful
assertNotNull(content.getBodyPart(0).getContent());
// Reading second part - should be successful
assertNotNull(content.getBodyPart(1).getContent());
}
}
/**
* Returns BODY object containing desired message fragment
*
* @param folder Folder containing the message
* @param uid Message UID
* @param from starting byte
* @param count bytes to read
* @return BODY containing desired message fragment
* @throws MessagingException
*/
private static BODY getMessageBodyPart(IMAPFolder folder, final Long uid, final Integer from, final Integer count) throws MessagingException
{
return (BODY) folder.doCommand(new IMAPFolder.ProtocolCommand()
{
public Object doCommand(IMAPProtocol p) throws ProtocolException
{
Response[] r = p.command("UID FETCH " + uid + " (FLAGS BODY.PEEK[]<" + from + "." + count + ">)", null);
logResponse(r);
Response response = r[r.length - 1];
// Grab response
if (!response.isOK())
{
throw new ProtocolException("Unable to retrieve message part <" + from + "." + count + ">");
}
FetchResponse fetchResponse = (FetchResponse) r[0];
BODY body = (BODY) fetchResponse.getItem(com.sun.mail.imap.protocol.BODY.class);
return body;
}
});
}
/**
* Finds node by its path
*
* @param path
* @return NodeRef
*/
private NodeRef findNode(String path)
{
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, path, null, namespaceService, false);
return nodeRefs.size() > 0 ? nodeRefs.get(0) : null;
}
/**
* Returns the UID of the first message in folder
*
* @param folder Folder containing the message
* @param msn message sequence number
* @return UID of the first message
* @throws MessagingException
*/
private static Long getMessageUid(IMAPFolder folder, final int msn) throws MessagingException
{
return (Long) folder.doCommand(new IMAPFolder.ProtocolCommand()
{
public Object doCommand(IMAPProtocol p) throws ProtocolException
{
Response[] r = p.command("FETCH " + msn + " (UID)", null);
logResponse(r);
Response response = r[r.length - 1];
// Grab response
if (!response.isOK())
{
throw new ProtocolException("Unable to retrieve message UID");
}
FetchResponse fetchResponse = (FetchResponse) r[0];
UID uid = (UID) fetchResponse.getItem(UID.class);
return uid.uid;
}
});
}
/**
* Returns size of the message
*
* @param folder Folder containing the message
* @param uid Message UID
* @return Returns size of the message
* @throws MessagingException
*/
private static Integer getMessageSize(IMAPFolder folder, final Long uid) throws MessagingException
{
return (Integer) folder.doCommand(new IMAPFolder.ProtocolCommand()
{
public Object doCommand(IMAPProtocol p) throws ProtocolException
{
Response[] r = p.command("UID FETCH " + uid + " (FLAGS BODY.PEEK[])", null);
logResponse(r);
Response response = r[r.length - 1];
// Grab response
if (!response.isOK())
{
throw new ProtocolException("Unable to retrieve message size");
}
FetchResponse fetchResponse = (FetchResponse) r[0];
BODY body = (BODY) fetchResponse.getItem(BODY.class);
return body.data.getCount();
}
});
}
/**
* Simple util for logging response
*
* @param r response
*/
private static void logResponse(Response[] r)
{
for (int i = 0; i < r.length; i++)
{
logger.debug(r[i]);
//logger.info(r[i]);
}
}
@Override
public void tearDown() throws Exception
{
// Deleting created test environment
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME, null,
namespaceService, false);
if (nodeRefs != null && nodeRefs.size() > 0)
{
fileFolderService.delete(nodeRefs.get(0));
}
authenticationService.deleteAuthentication(anotherUserName);
personService.deletePerson(anotherUserName);
txn.commit();
// Closing client connection
folder.close(false);
store.close();
}
}