org.alfresco.repo.imap.ImapMessageTest.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.imap.ImapMessageTest.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * 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/>.
 * #L%
 */
package org.alfresco.repo.imap;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.transaction.UserTransaction;

import junit.framework.TestCase;

import org.alfresco.model.ContentModel;
import org.alfresco.model.ImapModel;
import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
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.QueryParameterDefinition;
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.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
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.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.MimeMessageHelper;

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.IMAPResponse;
import com.sun.mail.imap.protocol.RFC822DATA;
import com.sun.mail.imap.protocol.UID;
import com.sun.mail.util.ASCIIUtility;

@Category(OwnJVMTestsCategory.class)
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 = 7143;

    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 ServiceRegistry serviceRegistry;
    private TransactionService transactionService;
    private NodeService nodeService;
    private ImporterService importerService;
    private PersonService personService;
    private SearchService searchService;
    private NamespaceService namespaceService;
    private FileFolderService fileFolderService;
    private MutableAuthenticationService authenticationService;
    private AlfrescoImapServer imapServer;

    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 {
        logger.debug("In SetUp");
        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");
        imapServer = (AlfrescoImapServer) imapCtx.getBean("imapServer");

        if (!imapServer.isImapServerEnabled()) {
            imapServer.setImapServerEnabled(true);
            imapServer.setHost(HOST);
            imapServer.setPort(PORT);
            imapServer.startup();
        }

        // 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(NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME);
        imapServiceImpl.setImapHome(imapHome);

        // Starting IMAP
        imapServiceImpl.startupInTxn(true);

        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);

        logger.debug("End SetUp");

    }

    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 unmodified message
        BODY body = getMessageBody(folder, uid);

        // Parse the multipart MIME message
        MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()),
                new BufferedInputStream(body.getByteArrayInputStream()));

        // Reading first part - should be successful
        MimeMultipart content = (MimeMultipart) message.getContent();
        assertNotNull(content.getBodyPart(0).getContent());
        // Reading second part - should be successful
        assertNotNull(content.getBodyPart(1).getContent());

        // 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 updated message part
        BODY bodyNew = getMessageBody(folder, uid);

        // The body should be updated
        assertFalse(Arrays.equals(bodyNew.getByteArray().getBytes(), body.getByteArray().getBytes()));

        // Parse the multipart MIME message
        message = new MimeMessage(Session.getDefaultInstance(new Properties()),
                new BufferedInputStream(bodyNew.getByteArrayInputStream()));

        // Reading first part - should be successful
        content = (MimeMultipart) message.getContent();
        assertNotNull(content.getBodyPart(0).getContent());
        // Reading second part - should be successful
        assertNotNull(content.getBodyPart(1).getContent());
    }

    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
        // Split the message into 2 part using a non multiple of 4 - 103 is a prime number
        // as the BASE64Decoder may not throw the IOException
        // see MNT-12995
        BODY body = getMessageBodyPart(folder, uid, 0, count - 103);

        // 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 - 103, 103);

        // 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 dontTestMessageCache() 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());
        }
    }

    public void testEncodedFromToAddresses() throws Exception {
        // RFC1342
        String addressString = "ars.kov@gmail.com";
        String personalString = "?? ";
        InternetAddress address = new InternetAddress(addressString, personalString, "UTF-8");

        // Following method returns the address with quoted personal aka <["?? "] <ars.kov@gmail.com>>
        // NOTE! This should be coincided with RFC822MetadataExtracter. Would 'addresses' be quoted or not? 
        // String decodedAddress = address.toUnicodeString();
        // So, just using decode, for now
        String decodedAddress = MimeUtility.decodeText(address.toString());

        // InternetAddress.toString(new Address[] {address}) - is used in the RFC822MetadataExtracter
        // So, compare with that
        assertFalse("Non ASCII characters in the address should be encoded",
                decodedAddress.equals(InternetAddress.toString(new Address[] { address })));

        MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));

        MimeMessageHelper messageHelper = new MimeMessageHelper(message, false, "UTF-8");

        messageHelper.setText("This is a sample message for ALF-5647");
        messageHelper.setSubject("This is a sample message for ALF-5647");
        messageHelper.setFrom(address);
        messageHelper.addTo(address);
        messageHelper.addCc(address);

        // Creating the message node in the repository
        String name = AlfrescoImapConst.MESSAGE_PREFIX + GUID.generate();
        FileInfo messageFile = fileFolderService.create(testImapFolderNodeRef, name, ContentModel.TYPE_CONTENT);
        // Writing a content.
        new IncomingImapMessage(messageFile, serviceRegistry, message);

        // Getting the transformed properties from the repository
        // cm:originator, cm:addressee, cm:addressees, imap:messageFrom, imap:messageTo, imap:messageCc
        Map<QName, Serializable> properties = nodeService.getProperties(messageFile.getNodeRef());

        String cmOriginator = (String) properties.get(ContentModel.PROP_ORIGINATOR);
        String cmAddressee = (String) properties.get(ContentModel.PROP_ADDRESSEE);
        @SuppressWarnings("unchecked")
        List<String> cmAddressees = (List<String>) properties.get(ContentModel.PROP_ADDRESSEES);
        String imapMessageFrom = (String) properties.get(ImapModel.PROP_MESSAGE_FROM);
        String imapMessageTo = (String) properties.get(ImapModel.PROP_MESSAGE_TO);
        String imapMessageCc = (String) properties.get(ImapModel.PROP_MESSAGE_CC);

        assertNotNull(cmOriginator);
        assertEquals(decodedAddress, cmOriginator);
        assertNotNull(cmAddressee);
        assertEquals(decodedAddress, cmAddressee);
        assertNotNull(cmAddressees);
        assertEquals(1, cmAddressees.size());
        assertEquals(decodedAddress, cmAddressees.get(0));
        assertNotNull(imapMessageFrom);
        assertEquals(decodedAddress, imapMessageFrom);
        assertNotNull(imapMessageTo);
        assertEquals(decodedAddress, imapMessageTo);
        assertNotNull(imapMessageCc);
        assertEquals(decodedAddress, imapMessageCc);
    }

    public void testEightBitMessage() throws Exception {

        Store lstore = session.getStore(PROTOCOL);
        lstore.connect(imapServer.getHost(), imapServer.getPort(), ADMIN_USER_NAME, ADMIN_USER_PASSWORD);

        String folderName = "Alfresco IMAP/" + IMAP_FOLDER_NAME;

        IMAPFolder lfolder = (IMAPFolder) lstore.getFolder(folderName);
        lfolder.open(Folder.READ_WRITE);

        InputStream messageFileInputStream1 = null;
        InputStream messageFileInputStream2 = null;
        try {
            ClassPathResource fileResource = new ClassPathResource("imap/test-8bit-message.eml");
            messageFileInputStream1 = new FileInputStream(fileResource.getFile());
            Message message = new MimeMessage(Session.getDefaultInstance(new Properties()),
                    messageFileInputStream1);
            String subject = message.getSubject();

            // get original bytes for further comparation
            messageFileInputStream2 = new FileInputStream(fileResource.getFile());
            byte[] original = ASCIIUtility.getBytes(messageFileInputStream2);

            Message[] messages = { message };

            lfolder.appendMessages(messages);

            // The search is not implemented. 
            // SearchTerm term = new HeaderTerm("X-Alfresco-Unique", "test8bit");
            // messages = folder.search(term);

            // So wee need to get our test message's UID from the repo

            String messageXPath = companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":"
                    + IMAP_FOLDER_NAME + "/*[like(@cm:title, $cm:title, true)]";

            QueryParameterDefinition[] params = new QueryParameterDefinition[1];
            params[0] = new QueryParameterDefImpl(ContentModel.PROP_TITLE,
                    serviceRegistry.getDictionaryService().getDataType(DataTypeDefinition.TEXT), true, subject);

            List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, messageXPath, params,
                    namespaceService, true);

            // does the message exist
            assertEquals(1, nodeRefs.size());

            NodeRef messageNodeRef = nodeRefs.get(0);

            // get message UID
            Long dbid = (Long) nodeService.getProperty(messageNodeRef, ContentModel.PROP_NODE_DBID);

            // fetch the massage
            RFC822DATA data = getRFC822Message(lfolder, dbid);

            assertNotNull("Can't fetch a message from the repositiry", data);

            byte[] processed = ASCIIUtility.getBytes(data.getByteArrayInputStream());

            assertTrue("Original message doesn't coincide to the message processed by the repository",
                    Arrays.equals(original, processed));
        } finally {
            if (messageFileInputStream1 != null)
                messageFileInputStream1.close();
            if (messageFileInputStream2 != null)
                messageFileInputStream2.close();
        }

        // close connection
        lfolder.close(true);
        lstore.close();

    }

    private static RFC822DATA getRFC822Message(final IMAPFolder folder, final long uid) throws MessagingException {
        return (RFC822DATA) folder.doCommand(new IMAPFolder.ProtocolCommand() {
            public Object doCommand(IMAPProtocol p) throws ProtocolException {
                Response[] r = p.command("UID FETCH " + uid + " (RFC822)", null);
                logResponse(r);
                Response response = r[r.length - 1];
                if (!response.isOK()) {
                    throw new ProtocolException("Unable to retrieve message in RFC822 format");
                }

                FetchResponse fetchResponse = (FetchResponse) r[0];
                return fetchResponse.getItem(RFC822DATA.class);
            }
        });

    }

    /**
     * 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 String
     * @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 {
                String command = "FETCH " + msn + " (UID)";
                Response[] r = p.command(command, null);
                logResponse(r);
                Response response = r[r.length - 1];

                // Grab response
                if (!response.isOK()) {
                    throw new ProtocolException("Unable to retrieve message UID");
                }

                for (int i = 0; i < r.length; i++) {
                    if (r[i] instanceof FetchResponse) {
                        FetchResponse fetchResponse = (FetchResponse) r[0];
                        UID uid = (UID) fetchResponse.getItem(UID.class);
                        logger.debug("SECNUM=" + uid.seqnum + ", UID=" + uid.uid);
                        return uid.uid;
                    }
                }

                /**
                  * Uh-oh - this is where we would intermittently fall over with a class cast exception.
                  * The following code probes why we don't have a FetchResponse
                  */
                StringBuffer sb = new StringBuffer();
                sb.append("command=" + command);
                sb.append('\n');
                sb.append("resp length=" + r.length);
                sb.append('\n');
                for (int i = 0; i < r.length; i++) {
                    logger.error(r[i]);
                    sb.append("class=" + r[i].getClass().getName());
                    IMAPResponse unexpected = (IMAPResponse) r[i];
                    sb.append("key=" + unexpected.getKey());
                    sb.append("number=" + unexpected.getNumber());
                    sb.append("rest=" + unexpected.getRest());

                    sb.append("r[" + i + "]=" + r[i] + '\n');
                }
                throw new ProtocolException("getMessageUid: " + sb.toString());
            }
        });
    }

    /**
     * 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 getMessageBody(folder, uid).getByteArray().getCount();
    }

    /**
     * Returns a full message body
     * 
     * @param folder Folder containing the message
     * @param uid Message UID
     * @return Returns size of the message
     * @throws MessagingException
     */
    private static BODY getMessageBody(IMAPFolder folder, final Long uid) 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[])", 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;
            }
        });
    }

    /**
     * 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]);
        }
    }

    @Override
    public void tearDown() throws Exception {
        // Deleting created test environment
        logger.debug("tearDown ");

        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();
        logger.debug("tearDown end");
    }

}