Dave Ward 043c9419dc Merged V3.3-BUG-FIX to HEAD
23167: Removed hard-coded "localhost" from ImapMessageTest
   23179: (RECORD ONLY) Merged HEAD to V3.3-BUG-FIX
      23064: Quick build fix (fallout from audit)
      23069: Do not attempt to audit InputStream property
   23190: Merged V3.3 to V3.3-BUG-FIX
      23189: Merged PATCHES/V3.2.0 to V3.3
         23173: ALF-5249: RepositoryContainer needs an 'unlimited' txn helper in order to generate the error response.
      23186: (RECORD ONLY) Merged V3.3-BUG-FIX to V3.3
         23180: Added flag for strict audit config loading: audit.config.strict=false
      23184: (RECORD ONLY) Merged V3.3-BUG-FIX to V3.3
         23179: Merged HEAD to V3.3-BUG-FIX
            23064: Quick build fix (fallout from audit)
            23069: Do not attempt to audit InputStream property
      23171: Fixed ALF-1990: Trashcan "Delete All Items" button might not delete all items
         - NodeArchiveService implements batching and locking for purgeAll... and restoreAll...
         - One user can do this operation at a time.  Other users get 'busy' error.
         - Items will disappear from the trashcan as the process progresses.
      23169: Merged HEAD to V3.3
         22933: Fixed getLock implementation to handle zero retry parameters


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@23192 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2010-10-18 09:07:11 +00:00

527 lines
21 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.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");
AlfrescoImapServer imapServer = (AlfrescoImapServer) imapCtx.getBean("imapServer");
// 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);
this.store.connect(imapServer.getHost(), imapServer.getPort(), 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();
}
}