Search in sources :

Example 1 with PendingModifications

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

the class Mailbox method isItemModified.

/** Returns whether the given item has been marked dirty for a particular
     *  reason during the current transaction.  Outside of a transaction, this
     *  method will return {@code false}.
     * @param item  The {@code MailItem} we're interested in.
     * @param how   A bitmask of {@link Change} "why" flags (e.g. {@code
     *              Change#MODIFIED_FLAGS}).
     * @see Change */
public boolean isItemModified(MailItem item, int how) {
    PendingModifications dirty = currentChange().dirty;
    if (!dirty.hasNotifications()) {
        return false;
    }
    PendingModifications.ModificationKey mkey = new PendingModifications.ModificationKey(item);
    if (dirty.created != null && dirty.created.containsKey(mkey)) {
        return true;
    }
    Change chg = dirty.modified == null ? null : dirty.modified.get(mkey);
    return chg != null && (chg.why & how) != 0;
}
Also used : PendingModifications(com.zimbra.cs.session.PendingModifications) Change(com.zimbra.cs.session.PendingModifications.Change)

Example 2 with PendingModifications

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

the class MemcachedCacheManager method notify.

@Override
public void notify(ChangeNotification notification) {
    PendingModifications mods = notification.mods;
    int changeId = notification.lastChangeId;
    // We have to notify calendar cache before checking memcached connectedness
    // because a portion of calendar cache is not memcached-based.
    CalendarCacheManager.getInstance().notifyCommittedChanges(mods, changeId);
    if (MemcachedConnector.isConnected()) {
        EffectiveACLCache.getInstance().notifyCommittedChanges(mods, changeId);
    }
}
Also used : PendingModifications(com.zimbra.cs.session.PendingModifications)

Example 3 with PendingModifications

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

the class Mailbox method commitCache.

private void commitCache(MailboxChange change) {
    if (change == null) {
        return;
    }
    ChangeNotification notification = null;
    // save for notifications (below)
    PendingModifications dirty = null;
    if (change.dirty != null && change.dirty.hasNotifications()) {
        assert (lock.isWriteLockedByCurrentThread());
        assert (currentChange().writeChange);
        dirty = change.dirty;
        change.dirty = new PendingModifications();
    }
    Session source = change.octxt == null ? null : change.octxt.getSession();
    assert (!change.hasChanges() || lock.isWriteLockedByCurrentThread());
    try {
        // the mailbox data has changed, so commit the changes
        if (change.sync != null) {
            mData.trackSync = change.sync;
        }
        if (change.imap != null) {
            mData.trackImap = change.imap;
        }
        if (change.size != MailboxChange.NO_CHANGE) {
            mData.size = change.size;
        }
        if (change.itemId != MailboxChange.NO_CHANGE) {
            mData.lastItemId = change.itemId;
        }
        if (change.contacts != MailboxChange.NO_CHANGE) {
            mData.contacts = change.contacts;
        }
        if (change.changeId != MailboxChange.NO_CHANGE && change.changeId > mData.lastChangeId) {
            mData.lastChangeId = change.changeId;
            mData.lastChangeDate = change.timestamp;
        }
        if (change.accessed != MailboxChange.NO_CHANGE) {
            mData.lastWriteDate = change.accessed;
        }
        if (change.recent != MailboxChange.NO_CHANGE) {
            mData.recentMessages = change.recent;
        }
        if (change.config != null) {
            if (change.config.getSecond() == null) {
                if (mData.configKeys != null) {
                    mData.configKeys.remove(change.config.getFirst());
                }
            } else {
                if (mData.configKeys == null) {
                    mData.configKeys = new HashSet<String>(1);
                }
                mData.configKeys.add(change.config.getFirst());
            }
        }
        if (change.deletes != null && change.deletes.blobs != null) {
            // remove cached messages
            for (String digest : change.deletes.blobDigests) {
                MessageCache.purge(digest);
            }
        }
        // committed changes, so notify any listeners
        if (dirty != null && dirty.hasNotifications()) {
            try {
                // try to get a copy of the changeset that *isn't* live
                dirty = snapshotModifications(dirty);
            } catch (ServiceException e) {
                ZimbraLog.mailbox.warn("error copying notifications; will notify with live set", e);
            }
            try {
                notification = new ChangeNotification(getAccount(), dirty, change.octxt, mData.lastChangeId, change.getOperation(), change.timestamp);
            } catch (ServiceException e) {
                ZimbraLog.mailbox.warn("error getting account for the mailbox", e);
            }
        }
    } catch (RuntimeException e) {
        ZimbraLog.mailbox.error("ignoring error during cache commit", e);
    } finally {
        // keep our MailItem cache at a reasonable size
        trimItemCache();
        // make sure we're ready for the next change
        change.reset();
    }
    if (notification != null) {
        for (Session session : mListeners) {
            try {
                session.notifyPendingChanges(notification.mods, notification.lastChangeId, source);
            } catch (RuntimeException e) {
                ZimbraLog.mailbox.error("ignoring error during notification", e);
            }
        }
        // send to the message channel
        DbConnection conn = null;
        try {
            if (Zimbra.isAlwaysOn()) {
                conn = DbPool.getConnection();
                List<String> serverids = DbSession.get(conn, getId());
                for (String serverid : serverids) {
                    Server server = Provisioning.getInstance().getServerById(serverid);
                    if (server.isLocalServer()) {
                        continue;
                    }
                    MailboxNotification ntfn = MailboxNotification.create(getAccountId(), mData.lastChangeId, dirty.getSerializedBytes());
                    MessageChannel.getInstance().sendMessage(server, ntfn);
                }
            }
        } catch (ServiceException e) {
            ZimbraLog.session.warn("unable to get target server", e);
        } catch (MessageChannelException e) {
            ZimbraLog.session.warn("unable to create MailboxNotification", e);
            return;
        } catch (IOException e) {
            ZimbraLog.session.warn("unable to create MailboxNotification", e);
            return;
        } finally {
            if (conn != null) {
                conn.closeQuietly();
            }
        }
        MailboxListener.notifyListeners(notification);
    }
}
Also used : MessageChannelException(com.zimbra.cs.iochannel.MessageChannelException) Server(com.zimbra.cs.account.Server) MailboxNotification(com.zimbra.cs.iochannel.MailboxNotification) ChangeNotification(com.zimbra.cs.mailbox.MailboxListener.ChangeNotification) IOException(java.io.IOException) DbConnection(com.zimbra.cs.db.DbPool.DbConnection) PendingModifications(com.zimbra.cs.session.PendingModifications) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) DbSession(com.zimbra.cs.db.DbSession) Session(com.zimbra.cs.session.Session) SoapSession(com.zimbra.cs.session.SoapSession)

Example 4 with PendingModifications

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

the class Mailbox method snapshotModifications.

/** Makes a deep copy of the {@code PendingModifications} object with
     *  {@link Flag#BITMASK_UNCACHED} set on each {@code MailItem} present in
     *  the {@code created} and {@code modified} hashes.  These copied {@code
     *  MailItem}s are not linked to their {@code Mailbox} and thus will not
     *  change when modifications are subsequently made to the contents of the
     *  {@code Mailbox}.  The original {@code PendingModifications} object and
     *  the {@code MailItem}s it references are unchanged.
     *  <p>
     *  This method should only be called <i>immediately</i> before notifying
     *  listeners of the changes from the currently-ending transaction. */
private PendingModifications snapshotModifications(PendingModifications pms) throws ServiceException {
    if (pms == null) {
        return null;
    }
    assert (currentChange().depth == 0);
    ItemCache cache = mItemCache.get();
    FolderCache folders = mFolderCache == null || Collections.disjoint(pms.changedTypes, FOLDER_TYPES) ? mFolderCache : snapshotFolders();
    PendingModifications snapshot = new PendingModifications();
    if (pms.deleted != null && !pms.deleted.isEmpty()) {
        snapshot.recordDeleted(pms.deleted);
    }
    if (pms.created != null && !pms.created.isEmpty()) {
        for (MailItem item : pms.created.values()) {
            if (item instanceof Folder && folders != null) {
                Folder folder = folders.get(item.getId());
                if (folder == null) {
                    ZimbraLog.mailbox.warn("folder missing from snapshotted folder set: %d", item.getId());
                    folder = (Folder) item;
                }
                snapshot.recordCreated(folder);
            } else if (item instanceof Tag) {
                if (((Tag) item).isListed()) {
                    snapshot.recordCreated(snapshotItem(item));
                }
            } else {
                // NOTE: if the folder cache is null, folders fall down here and should always get copy == false
                if (cache != null && cache.contains(item)) {
                    item = snapshotItem(item);
                }
                snapshot.recordCreated(item);
            }
        }
    }
    if (pms.modified != null && !pms.modified.isEmpty()) {
        for (Map.Entry<PendingModifications.ModificationKey, Change> entry : pms.modified.entrySet()) {
            Change chg = entry.getValue();
            if (!(chg.what instanceof MailItem)) {
                snapshot.recordModified(entry.getKey(), chg);
                continue;
            }
            MailItem item = (MailItem) chg.what;
            if (item instanceof Folder && folders != null) {
                Folder folder = folders.get(item.getId());
                if (folder == null) {
                    ZimbraLog.mailbox.warn("folder missing from snapshotted folder set: %d", item.getId());
                    folder = (Folder) item;
                }
                snapshot.recordModified(folder, chg.why, (MailItem) chg.preModifyObj);
            } else if (item instanceof Tag) {
                if (((Tag) item).isListed()) {
                    snapshot.recordModified(snapshotItem(item), chg.why, (MailItem) chg.preModifyObj);
                }
            } else {
                // NOTE: if the folder cache is null, folders fall down here and should always get copy == false
                if (cache != null && cache.contains(item)) {
                    item = snapshotItem(item);
                }
                snapshot.recordModified(item, chg.why, (MailItem) chg.preModifyObj);
            }
        }
    }
    return snapshot;
}
Also used : DbMailItem(com.zimbra.cs.db.DbMailItem) PendingModifications(com.zimbra.cs.session.PendingModifications) AlterItemTag(com.zimbra.cs.redolog.op.AlterItemTag) CreateTag(com.zimbra.cs.redolog.op.CreateTag) DbTag(com.zimbra.cs.db.DbTag) Change(com.zimbra.cs.session.PendingModifications.Change) CreateFolder(com.zimbra.cs.redolog.op.CreateFolder) ZFolder(com.zimbra.client.ZFolder) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentLinkedHashMap(com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap) IcalXmlStrMap(com.zimbra.cs.mailbox.calendar.IcalXmlStrMap) HashMap(java.util.HashMap) TimeZoneMap(com.zimbra.common.calendar.TimeZoneMap)

Aggregations

PendingModifications (com.zimbra.cs.session.PendingModifications)4 Change (com.zimbra.cs.session.PendingModifications.Change)2 ConcurrentLinkedHashMap (com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap)1 ZFolder (com.zimbra.client.ZFolder)1 TimeZoneMap (com.zimbra.common.calendar.TimeZoneMap)1 ServiceException (com.zimbra.common.service.ServiceException)1 AccountServiceException (com.zimbra.cs.account.AccountServiceException)1 Server (com.zimbra.cs.account.Server)1 DbMailItem (com.zimbra.cs.db.DbMailItem)1 DbConnection (com.zimbra.cs.db.DbPool.DbConnection)1 DbSession (com.zimbra.cs.db.DbSession)1 DbTag (com.zimbra.cs.db.DbTag)1 MailboxNotification (com.zimbra.cs.iochannel.MailboxNotification)1 MessageChannelException (com.zimbra.cs.iochannel.MessageChannelException)1 ChangeNotification (com.zimbra.cs.mailbox.MailboxListener.ChangeNotification)1 IcalXmlStrMap (com.zimbra.cs.mailbox.calendar.IcalXmlStrMap)1 AlterItemTag (com.zimbra.cs.redolog.op.AlterItemTag)1 CreateFolder (com.zimbra.cs.redolog.op.CreateFolder)1 CreateTag (com.zimbra.cs.redolog.op.CreateTag)1 Session (com.zimbra.cs.session.Session)1