Search in sources :

Example 6 with ModificationKey

use of com.zimbra.cs.session.PendingModifications.ModificationKey in project zm-mailbox by Zimbra.

the class MailboxTest method notifications.

@Test
public void notifications() throws Exception {
    Account acct = Provisioning.getInstance().getAccount("test@zimbra.com");
    Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(acct);
    MockListener ml = new MockListener();
    MailboxListener.register(ml);
    try {
        Folder f = mbox.createFolder(null, "foo", new Folder.FolderOptions().setDefaultView(MailItem.Type.MESSAGE));
        Folder fParent = (Folder) f.getParent();
        ModificationKey fkey = new ModificationKey(f);
        ModificationKey fParentKey = new ModificationKey(fParent);
        Assert.assertNull("no deletes after create", ml.getPms().deleted);
        Assert.assertNotNull("creates aren't null", ml.getPms().created);
        Assert.assertEquals("one created folder", 1, ml.getPms().created.size());
        Assert.assertNotNull("created folder has entry", ml.getPms().created.get(fkey));
        Assert.assertEquals("created folder matches created entry", f.getId(), ml.getPms().created.get(fkey).getIdInMailbox());
        Assert.assertNotNull("modifications aren't null", ml.getPms().modified);
        Assert.assertEquals("one modified folder", 1, ml.getPms().modified.size());
        PendingModifications.Change pModification = ml.getPms().modified.get(fParentKey);
        Assert.assertNotNull("parent folder modified", pModification);
        Assert.assertEquals("parent folder matches modified entry", fParent.getId(), ((Folder) pModification.what).getId());
        Assert.assertNotNull("preModifyObj is not null", pModification.preModifyObj);
        Assert.assertEquals("preModifyObj is a snapshot of parent folder", fParent.getId(), ((Folder) pModification.preModifyObj).getId());
        DeliveryOptions dopt = new DeliveryOptions().setFolderId(f.getId());
        Message m = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), dopt, null);
        ModificationKey mkey = new ModificationKey(m);
        ml.clear();
        mbox.delete(null, f.getId(), MailItem.Type.FOLDER);
        Assert.assertNull("no creates after delete", ml.getPms().created);
        Assert.assertNotNull("deletes aren't null", ml.getPms().deleted);
        Assert.assertEquals("1 deleted folder, 1 deleted message, 1 deleted vconv", 3, ml.getPms().deleted.size());
        PendingModifications.Change fDeletion = ml.getPms().deleted.get(fkey);
        Assert.assertNotNull("deleted folder has entry", fDeletion);
        Assert.assertTrue("deleted folder matches deleted entry", f.getType() == fDeletion.what && f.getId() == ((Folder) fDeletion.preModifyObj).getId());
        PendingModifications.Change mDeletion = ml.getPms().deleted.get(mkey);
        Assert.assertNotNull("deleted message has entry", mDeletion);
        // Note that preModifyObj may be null for the deleted message, so just check for the type
        Assert.assertTrue("deleted message matches deleted entry", m.getType() == mDeletion.what);
        Assert.assertNotNull("modifications aren't null", ml.getPms().modified);
        // Bug 80980 "folder size modified" notification present because folder delete is now a 2 stage operation.
        // Empty folder, then delete it.
        Assert.assertEquals("parent folder modified, mailbox size modified, folder size modified", 3, ml.getPms().modified.size());
        pModification = ml.getPms().modified.get(fParentKey);
        Assert.assertNotNull("parent folder modified", pModification);
        Assert.assertEquals("parent folder matches modified entry", fParent.getId(), ((Folder) pModification.what).getId());
        Assert.assertNotNull("preModifyObj is not null", pModification.preModifyObj);
        Assert.assertEquals("preModifyObj is a snapshot of parent folder", fParent.getId(), ((Folder) pModification.preModifyObj).getId());
    } finally {
        MailboxListener.unregister(ml);
    }
}
Also used : Account(com.zimbra.cs.account.Account) PendingModifications(com.zimbra.cs.session.PendingModifications) ParsedMessage(com.zimbra.cs.mime.ParsedMessage) ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) Test(org.junit.Test)

Example 7 with ModificationKey

use of com.zimbra.cs.session.PendingModifications.ModificationKey in project zm-mailbox by Zimbra.

the class EffectiveACLCache method notifyCommittedChanges.

public void notifyCommittedChanges(PendingLocalModifications mods, int changeId) {
    Set<EffectiveACLCacheKey> keysToInvalidate = new HashSet<EffectiveACLCacheKey>();
    if (mods.modified != null) {
        for (Map.Entry<ModificationKey, Change> entry : mods.modified.entrySet()) {
            Change change = entry.getValue();
            Object whatChanged = change.what;
            // permission change or move to a new parent folder.
            if (whatChanged instanceof Folder && (change.why & (Change.ACL | Change.FOLDER)) != 0) {
                Folder folder = (Folder) whatChanged;
                // Invalidate all child folders because their inherited ACL will need to be recomputed.
                String acctId = folder.getMailbox().getAccountId();
                // includes "folder" folder
                List<Folder> subfolders = folder.getSubfolderHierarchy();
                for (Folder subf : subfolders) {
                    EffectiveACLCacheKey key = new EffectiveACLCacheKey(acctId, subf.getId());
                    keysToInvalidate.add(key);
                }
            }
        }
    }
    if (mods.deleted != null) {
        for (Map.Entry<ModificationKey, Change> entry : mods.deleted.entrySet()) {
            MailItem.Type type = (MailItem.Type) entry.getValue().what;
            if (type == MailItem.Type.FOLDER) {
                String acctId = entry.getKey().getAccountId();
                if (acctId == null)
                    // just to be safe
                    continue;
                EffectiveACLCacheKey key = new EffectiveACLCacheKey(acctId, entry.getKey().getItemId());
                keysToInvalidate.add(key);
            }
        }
    }
    try {
        mMemcachedLookup.removeMulti(keysToInvalidate);
    } catch (ServiceException e) {
        ZimbraLog.calendar.warn("Unable to notify folder acl cache.  Some cached data may become stale.", e);
    }
}
Also used : ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) Change(com.zimbra.cs.session.PendingModifications.Change) Folder(com.zimbra.cs.mailbox.Folder) MailItem(com.zimbra.cs.mailbox.MailItem) ServiceException(com.zimbra.common.service.ServiceException) Map(java.util.Map) MemcachedMap(com.zimbra.common.util.memcached.MemcachedMap) HashSet(java.util.HashSet)

Example 8 with ModificationKey

use of com.zimbra.cs.session.PendingModifications.ModificationKey in project zm-mailbox by Zimbra.

the class CalSummaryCache method notifyCommittedChanges.

void notifyCommittedChanges(PendingLocalModifications mods, int changeId) {
    if (mods.created != null) {
        for (Map.Entry<ModificationKey, BaseItemInfo> entry : mods.created.entrySet()) {
            BaseItemInfo item = entry.getValue();
            if (item instanceof CalendarItem) {
                CalendarItem calItem = (CalendarItem) item;
                int folderId = calItem.getFolderId();
                invalidateItem(calItem.getMailbox(), folderId, calItem.getId());
            }
        }
    }
    if (mods.modified != null) {
        for (Map.Entry<ModificationKey, Change> entry : mods.modified.entrySet()) {
            Change change = entry.getValue();
            Object whatChanged = change.what;
            if (whatChanged instanceof CalendarItem) {
                CalendarItem item = (CalendarItem) whatChanged;
                Mailbox mbox = item.getMailbox();
                int folderId = item.getFolderId();
                int itemId = item.getId();
                invalidateItem(mbox, folderId, itemId);
                // If this is a folder move, invalidate the item from the old folder too.
                if ((change.why & Change.FOLDER) != 0) {
                    String accountId = mbox.getAccountId();
                    int prevFolderId;
                    synchronized (mSummaryCache) {
                        prevFolderId = mSummaryCache.getFolderForItem(accountId, itemId);
                    }
                    if (prevFolderId != folderId && prevFolderId != SummaryLRU.FOLDER_NOT_FOUND) {
                        invalidateItem(mbox, prevFolderId, itemId);
                    }
                }
            }
        }
    }
    if (mods.deleted != null) {
        String lastAcctId = null;
        Mailbox lastMbox = null;
        for (Map.Entry<ModificationKey, Change> entry : mods.deleted.entrySet()) {
            MailItem.Type type = (MailItem.Type) entry.getValue().what;
            if (type == MailItem.Type.APPOINTMENT || type == MailItem.Type.TASK) {
                // We only have item id.  Look up the folder id of the item in the cache.
                Mailbox mbox = null;
                String acctId = entry.getKey().getAccountId();
                // just to be safe
                if (acctId == null)
                    continue;
                if (acctId.equals(lastAcctId)) {
                    // Deletion by id list usually happens because of a folder getting emptied.
                    // It's highly likely the items all belong to the same mailbox, let alone folder.
                    mbox = lastMbox;
                } else {
                    try {
                        mbox = MailboxManager.getInstance().getMailboxByAccountId(acctId, FetchMode.DO_NOT_AUTOCREATE);
                    } catch (ServiceException e) {
                        ZimbraLog.calendar.error("Error looking up the mailbox of account in delete notification: account=" + acctId, e);
                        continue;
                    }
                }
                if (mbox != null) {
                    lastAcctId = acctId;
                    lastMbox = mbox;
                    int itemId = entry.getKey().getItemId();
                    String accountId = mbox.getAccountId();
                    int folderId;
                    synchronized (mSummaryCache) {
                        folderId = mSummaryCache.getFolderForItem(accountId, itemId);
                    }
                    if (folderId != SummaryLRU.FOLDER_NOT_FOUND) {
                        invalidateItem(mbox, folderId, itemId);
                    }
                }
            }
        }
    }
    if (MemcachedConnector.isConnected()) {
        mMemcachedCache.notifyCommittedChanges(mods, changeId);
    }
}
Also used : BaseItemInfo(com.zimbra.common.mailbox.BaseItemInfo) ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) Change(com.zimbra.cs.session.PendingModifications.Change) CalendarItem(com.zimbra.cs.mailbox.CalendarItem) MailItem(com.zimbra.cs.mailbox.MailItem) Mailbox(com.zimbra.cs.mailbox.Mailbox) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map)

Example 9 with ModificationKey

use of com.zimbra.cs.session.PendingModifications.ModificationKey in project zm-mailbox by Zimbra.

the class SoapSession method putQueuedNotifications.

/**
 * Write a single instance of the PendingLocalModifications structure into the
 *  passed-in <ctxt> block.
 */
protected void putQueuedNotifications(Mailbox mbox, QueuedNotifications ntfn, Element parent, ZimbraSoapContext zsc) {
    // create the base "notify" block:  <notify seq="6"/>
    Element eNotify = parent.addNonUniqueElement(ZimbraNamespace.E_NOTIFY);
    if (ntfn.getSequence() > 0) {
        eNotify.addAttribute(HeaderConstants.A_SEQNO, ntfn.getSequence());
    }
    OperationContext octxt = null;
    try {
        octxt = DocumentHandler.getOperationContext(zsc, this);
    } catch (ServiceException e) {
        ZimbraLog.session.warn("error fetching operation context for: " + zsc.getAuthtokenAccountId(), e);
        return;
    }
    boolean debug = ZimbraLog.session.isDebugEnabled();
    PendingLocalModifications pms = ntfn.mMailboxChanges;
    RemoteNotifications rns = ntfn.mRemoteChanges;
    Element eDeleted = eNotify.addUniqueElement(ZimbraNamespace.E_DELETED);
    StringBuilder deletedIds = new StringBuilder();
    if (pms != null && pms.deleted != null && pms.deleted.size() > 0) {
        for (ModificationKey mkey : pms.deleted.keySet()) {
            addDeletedNotification(mkey, deletedIds);
        }
    }
    if (rns != null && rns.deleted != null) {
        deletedIds.append(deletedIds.length() == 0 ? "" : ",").append(rns.deleted);
    }
    boolean hasLocalCreates = pms != null && pms.created != null && !pms.created.isEmpty();
    boolean hasRemoteCreates = rns != null && rns.created != null && !rns.created.isEmpty();
    boolean hasLocalModifies = pms != null && pms.modified != null && !pms.modified.isEmpty();
    boolean hasRemoteModifies = rns != null && rns.modified != null && !rns.modified.isEmpty();
    if (SoapTransport.NotificationFormat.valueOf(zsc.getNotificationFormat()) == SoapTransport.NotificationFormat.IMAP) {
        try {
            AccountWithModifications info = new AccountWithModifications(zsc.getAuthtokenAccountId(), mbox.getLastChangeID());
            Map<Integer, PendingFolderModifications> folderMods = PendingModifications.encodeIMAPFolderModifications(pms);
            info.setPendingFolderModifications(folderMods.values());
            eNotify.addUniqueElement(JaxbUtil.jaxbToElement(info, eNotify.getFactory()));
        } catch (ContainerException | ServiceException e) {
            ZimbraLog.session.error("Failed to encode IMAP notifications for a SOAP session ", e);
        }
    }
    if (hasLocalCreates || hasRemoteCreates) {
        Element eCreated = eNotify.addUniqueElement(ZimbraNamespace.E_CREATED);
        if (hasLocalCreates) {
            for (BaseItemInfo item : pms.created.values()) {
                if (item instanceof MailItem) {
                    MailItem mi = (MailItem) item;
                    ItemIdFormatter ifmt = new ItemIdFormatter(mAuthenticatedAccountId, mi.getMailbox(), false);
                    try {
                        Element elem = ToXML.encodeItem(eCreated, ifmt, octxt, mi, ToXML.NOTIFY_FIELDS);
                        // special-case notifications for new mountpoints in the authenticated user's mailbox
                        if (item instanceof Mountpoint && mbox == mi.getMailbox()) {
                            Map<ItemId, Pair<Boolean, Element>> mountpoints = new HashMap<ItemId, Pair<Boolean, Element>>(2);
                            expandLocalMountpoint(octxt, (Mountpoint) mi, eCreated.getFactory(), mountpoints);
                            expandRemoteMountpoints(octxt, zsc, mountpoints);
                            transferMountpointContents(elem, octxt, mountpoints);
                        }
                    } catch (ServiceException e) {
                        ZimbraLog.session.warn("error encoding item " + mi.getId(), e);
                        return;
                    }
                }
            }
            // sanity-check the returned element
            if (!eCreated.hasChildren() && debug) {
                ZimbraLog.session.debug("no serialied creates for item set: %s", pms.created.keySet());
            }
        }
        if (hasRemoteCreates) {
            if (debug) {
                ZimbraLog.session.debug("adding %d proxied creates", rns.created.size());
            }
            for (Element elt : rns.created) {
                if (encodingMatches(parent, elt)) {
                    eCreated.addElement(elt.clone().detach());
                } else {
                    ZimbraLog.session.warn("unable to add remote notification due to mismatched SOAP protocol");
                }
            }
        }
    }
    ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
    if (hasLocalModifies || hasRemoteModifies) {
        Element eModified = eNotify.addUniqueElement(ZimbraNamespace.E_MODIFIED);
        if (hasLocalModifies) {
            for (Change chg : pms.modified.values()) {
                if (chg.why != 0 && chg.what instanceof MailItem) {
                    MailItem item = (MailItem) chg.what;
                    try {
                        Element elt = ToXML.encodeItem(eModified, ifmt, octxt, item, chg.why);
                        if (elt == null) {
                            ModificationKey mkey = new PendingLocalModifications.ModificationKey(item);
                            addDeletedNotification(mkey, deletedIds);
                            if (debug) {
                                ZimbraLog.session.debug("marking nonserialized item as a delete: %s", mkey);
                            }
                        }
                    } catch (ServiceException e) {
                        ZimbraLog.session.warn("error encoding item " + item.getId(), e);
                        return;
                    }
                } else if (chg.why != 0 && chg.what instanceof Mailbox) {
                    ToXML.encodeMailbox(eModified, octxt, (Mailbox) chg.what, chg.why);
                }
            }
            // sanity-check the returned element
            if (!eModified.hasChildren() && debug) {
                ZimbraLog.session.debug("no serialied modifies for item set: %s", pms.modified.keySet());
            }
        }
        if (hasRemoteModifies) {
            if (debug) {
                ZimbraLog.session.debug("adding %d proxied modifies", rns.modified.size());
            }
            for (Element elt : rns.modified) {
                if (encodingMatches(parent, elt)) {
                    eModified.addElement(elt.clone().detach());
                } else {
                    ZimbraLog.session.warn("unable to add remote notification due to mismatched SOAP protocol");
                }
            }
        }
    }
    if (rns != null && rns.activities != null && !rns.activities.isEmpty()) {
        for (Element elt : rns.activities) {
            if (encodingMatches(parent, elt)) {
                eNotify.addElement(elt.clone().detach());
            } else {
                ZimbraLog.session.warn("unable to add remote notification due to mismatched SOAP protocol");
            }
        }
    }
    putExtraNotifications(ntfn, eNotify, ifmt);
    if (deletedIds == null || deletedIds.length() == 0) {
        eDeleted.detach();
    } else {
        eDeleted.addAttribute(A_ID, deletedIds.toString());
    }
}
Also used : AccountWithModifications(com.zimbra.soap.type.AccountWithModifications) BaseItemInfo(com.zimbra.common.mailbox.BaseItemInfo) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) HashMap(java.util.HashMap) Element(com.zimbra.common.soap.Element) ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) ItemId(com.zimbra.cs.service.util.ItemId) Mailbox(com.zimbra.cs.mailbox.Mailbox) ContainerException(com.zimbra.common.soap.Element.ContainerException) Mountpoint(com.zimbra.cs.mailbox.Mountpoint) Pair(com.zimbra.common.util.Pair) OperationContext(com.zimbra.cs.mailbox.OperationContext) Change(com.zimbra.cs.session.PendingModifications.Change) MailItem(com.zimbra.cs.mailbox.MailItem) ServiceException(com.zimbra.common.service.ServiceException) PendingFolderModifications(com.zimbra.soap.mail.type.PendingFolderModifications)

Example 10 with ModificationKey

use of com.zimbra.cs.session.PendingModifications.ModificationKey in project zm-mailbox by Zimbra.

the class ImapSession method notifyPendingChanges.

@Override
public void notifyPendingChanges(PendingModifications pns, int changeId, Session source) {
    if (!pns.hasNotifications()) {
        return;
    }
    ImapHandler i4handler = handler;
    try {
        synchronized (this) {
            AddedItems added = new AddedItems();
            if (pns.deleted != null) {
                for (Map.Entry<ModificationKey, Change> entry : pns.deleted.entrySet()) {
                    handleDelete(changeId, entry.getKey().getItemId(), entry.getValue());
                }
            }
            if (pns.created != null) {
                for (MailItem item : pns.created.values()) {
                    handleCreate(changeId, item, added);
                }
            }
            if (pns.modified != null) {
                for (Change chg : pns.modified.values()) {
                    handleModify(changeId, chg, added);
                }
            }
            // add new messages to the currently selected mailbox
            if (!added.isEmpty()) {
                mFolder.handleAddedMessages(changeId, added);
            }
            mFolder.finishNotification(changeId);
        }
        if (i4handler != null && i4handler.isIdle()) {
            i4handler.sendNotifications(true, true);
        }
    } catch (IOException e) {
        //   which calls Session.doCleanup, which calls Mailbox.removeListener
        if (ZimbraLog.imap.isDebugEnabled()) {
            // with stack trace
            ZimbraLog.imap.debug("Failed to notify, closing %s", this, e);
        } else {
            // without stack trace
            ZimbraLog.imap.info("Failed to notify (%s), closing %s", e.toString(), this);
        }
        if (i4handler != null) {
            i4handler.close();
        }
    }
}
Also used : MailItem(com.zimbra.cs.mailbox.MailItem) ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) Change(com.zimbra.cs.session.PendingModifications.Change) IOException(java.io.IOException) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) TreeMap(java.util.TreeMap)

Aggregations

ModificationKey (com.zimbra.cs.session.PendingModifications.ModificationKey)18 Change (com.zimbra.cs.session.PendingModifications.Change)17 MailItem (com.zimbra.cs.mailbox.MailItem)15 Map (java.util.Map)15 ServiceException (com.zimbra.common.service.ServiceException)13 Folder (com.zimbra.cs.mailbox.Folder)10 HashMap (java.util.HashMap)7 BaseItemInfo (com.zimbra.common.mailbox.BaseItemInfo)6 MemcachedMap (com.zimbra.common.util.memcached.MemcachedMap)6 HashSet (java.util.HashSet)6 Mailbox (com.zimbra.cs.mailbox.Mailbox)4 Message (com.zimbra.cs.mailbox.Message)4 Account (com.zimbra.cs.account.Account)3 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)3 ZFolder (com.zimbra.client.ZFolder)2 BigByteArrayMemcachedMap (com.zimbra.common.util.memcached.BigByteArrayMemcachedMap)2 Type (com.zimbra.cs.mailbox.MailItem.Type)2 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)2 PendingModifications (com.zimbra.cs.session.PendingModifications)2 IOException (java.io.IOException)2