Search in sources :

Example 1 with Change

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

the class SoapSession method putQueuedNotifications.

/** Write a single instance of the PendingModifications 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.addElement(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();
    PendingModifications 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();
    if (hasLocalCreates || hasRemoteCreates) {
        Element eCreated = eNotify.addUniqueElement(ZimbraNamespace.E_CREATED);
        if (hasLocalCreates) {
            for (MailItem item : pms.created.values()) {
                ItemIdFormatter ifmt = new ItemIdFormatter(mAuthenticatedAccountId, item.getMailbox(), false);
                try {
                    Element elem = ToXML.encodeItem(eCreated, ifmt, octxt, item, ToXML.NOTIFY_FIELDS);
                    // special-case notifications for new mountpoints in the authenticated user's mailbox
                    if (item instanceof Mountpoint && mbox == item.getMailbox()) {
                        Map<ItemId, Pair<Boolean, Element>> mountpoints = new HashMap<ItemId, Pair<Boolean, Element>>(2);
                        expandLocalMountpoint(octxt, (Mountpoint) item, eCreated.getFactory(), mountpoints);
                        expandRemoteMountpoints(octxt, zsc, mountpoints);
                        transferMountpointContents(elem, octxt, mountpoints);
                    }
                } catch (ServiceException e) {
                    ZimbraLog.session.warn("error encoding item " + item.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);
    boolean hasLocalModifies = pms != null && pms.modified != null && !pms.modified.isEmpty();
    boolean hasRemoteModifies = rns != null && rns.modified != null && !rns.modified.isEmpty();
    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 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 : OperationContext(com.zimbra.cs.mailbox.OperationContext) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) HashMap(java.util.HashMap) Element(com.zimbra.common.soap.Element) ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) Change(com.zimbra.cs.session.PendingModifications.Change) ItemId(com.zimbra.cs.service.util.ItemId) MailItem(com.zimbra.cs.mailbox.MailItem) ServiceException(com.zimbra.common.service.ServiceException) Mailbox(com.zimbra.cs.mailbox.Mailbox) Mountpoint(com.zimbra.cs.mailbox.Mountpoint) Pair(com.zimbra.common.util.Pair)

Example 2 with Change

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

the class CalSummaryCache method notifyCommittedChanges.

void notifyCommittedChanges(PendingModifications mods, int changeId) {
    if (mods.created != null) {
        for (Map.Entry<ModificationKey, MailItem> entry : mods.created.entrySet()) {
            MailItem item = entry.getValue();
            if (item instanceof CalendarItem) {
                int folderId = item.getFolderId();
                invalidateItem(item.getMailbox(), folderId, item.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 : 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 3 with Change

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

the class CalSummaryMemcachedCache method notifyCommittedChanges.

void notifyCommittedChanges(PendingModifications mods, int changeId) {
    Set<CalSummaryKey> keysToInvalidate = new HashSet<CalSummaryKey>();
    if (mods.modified != null) {
        for (Map.Entry<ModificationKey, Change> entry : mods.modified.entrySet()) {
            Change change = entry.getValue();
            Object whatChanged = change.what;
            if (whatChanged instanceof Folder) {
                Folder folder = (Folder) whatChanged;
                MailItem.Type viewType = folder.getDefaultView();
                if (viewType == MailItem.Type.APPOINTMENT || viewType == MailItem.Type.TASK) {
                    CalSummaryKey key = new CalSummaryKey(folder.getMailbox().getAccountId(), folder.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) {
                // We only have item id.  Assume it's a folder id and issue a delete.
                String acctId = entry.getKey().getAccountId();
                if (acctId == null)
                    // just to be safe
                    continue;
                CalSummaryKey key = new CalSummaryKey(acctId, entry.getKey().getItemId());
                keysToInvalidate.add(key);
            }
        // Let's not worry about hard deletes of invite/reply emails.  It has no practical benefit.
        }
    }
    try {
        mMemcachedLookup.removeMulti(keysToInvalidate);
    } catch (ServiceException e) {
        ZimbraLog.calendar.warn("Unable to notify ctag info 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) BigByteArrayMemcachedMap(com.zimbra.common.util.memcached.BigByteArrayMemcachedMap) HashSet(java.util.HashSet)

Example 4 with Change

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

the class FilterListener method notify.

@Override
public void notify(ChangeNotification notification) {
    if (notification.mods.modified != null && EVENTS.contains(notification.op)) {
        for (PendingModifications.Change change : notification.mods.modified.values()) {
            if (change.what instanceof Folder) {
                if ((change.why & Change.PARENT) == 0 && (change.why & Change.NAME) == 0) {
                    continue;
                }
                Folder folder = (Folder) change.what;
                Folder oldFolder = (Folder) change.preModifyObj;
                if (oldFolder == null) {
                    ZimbraLog.filter.warn("Cannot determine the old folder name for %s.", folder.getName());
                    continue;
                }
                updateFilterRules(notification.mailboxAccount, folder, oldFolder.getPath());
            } else if (change.what instanceof Tag) {
                if ((change.why & Change.NAME) == 0) {
                    continue;
                }
                Tag tag = (Tag) change.what;
                Tag oldTag = (Tag) change.preModifyObj;
                if (oldTag == null) {
                    ZimbraLog.filter.warn("Cannot determine the old tag name for %s.", tag.getName());
                    continue;
                }
                updateFilterRules(notification.mailboxAccount, tag, oldTag.getName());
            }
        }
    }
    if (notification.mods.deleted != null) {
        for (Map.Entry<ModificationKey, Change> entry : notification.mods.deleted.entrySet()) {
            MailItem.Type type = (MailItem.Type) entry.getValue().what;
            if (type == MailItem.Type.FOLDER || type == MailItem.Type.MOUNTPOINT) {
                Folder oldFolder = (Folder) entry.getValue().preModifyObj;
                if (oldFolder == null) {
                    ZimbraLog.filter.warn("Cannot determine the old folder name for %s.", entry.getKey());
                    continue;
                }
                updateFilterRules(notification.mailboxAccount, (Folder) null, oldFolder.getPath());
            } else if (type == MailItem.Type.TAG) {
                Tag oldTag = (Tag) entry.getValue().preModifyObj;
                updateFilterRules(notification.mailboxAccount, oldTag);
            }
        }
    }
}
Also used : MailItem(com.zimbra.cs.mailbox.MailItem) PendingModifications(com.zimbra.cs.session.PendingModifications) ModificationKey(com.zimbra.cs.session.PendingModifications.ModificationKey) Change(com.zimbra.cs.session.PendingModifications.Change) Tag(com.zimbra.cs.mailbox.Tag) Change(com.zimbra.cs.session.PendingModifications.Change) Folder(com.zimbra.cs.mailbox.Folder) Map(java.util.Map)

Example 5 with Change

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

the class EffectiveACLCache method notifyCommittedChanges.

public void notifyCommittedChanges(PendingModifications 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)

Aggregations

Change (com.zimbra.cs.session.PendingModifications.Change)15 MailItem (com.zimbra.cs.mailbox.MailItem)10 ModificationKey (com.zimbra.cs.session.PendingModifications.ModificationKey)10 Map (java.util.Map)10 ServiceException (com.zimbra.common.service.ServiceException)8 Folder (com.zimbra.cs.mailbox.Folder)6 HashMap (java.util.HashMap)5 ZFolder (com.zimbra.client.ZFolder)3 MemcachedMap (com.zimbra.common.util.memcached.MemcachedMap)3 DbTag (com.zimbra.cs.db.DbTag)3 Mailbox (com.zimbra.cs.mailbox.Mailbox)3 PendingModifications (com.zimbra.cs.session.PendingModifications)3 HashSet (java.util.HashSet)3 Account (com.zimbra.cs.account.Account)2 DbMailItem (com.zimbra.cs.db.DbMailItem)2 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)2 Message (com.zimbra.cs.mailbox.Message)2 AlterItemTag (com.zimbra.cs.redolog.op.AlterItemTag)2 CreateFolder (com.zimbra.cs.redolog.op.CreateFolder)2 CreateTag (com.zimbra.cs.redolog.op.CreateTag)2