use of com.zimbra.cs.session.PendingModifications.Change 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;
}
use of com.zimbra.cs.session.PendingModifications.Change in project zm-mailbox by Zimbra.
the class TagTest method notifications.
@Test
public void notifications() throws Exception {
Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(MockProvisioning.DEFAULT_ACCOUNT_ID);
MockListener ml = new MockListener();
MailboxListener.register(ml);
try {
// new implicit tags should not be included in notifications
DeliveryOptions dopt = new DeliveryOptions().setFolderId(Mailbox.ID_FOLDER_INBOX).setFlags(Flag.BITMASK_UNREAD).setTags(new String[] { tag2 });
mbox.addMessage(null, ThreaderTest.getRootMessage(), dopt, null);
for (MailItem item : ml.pms.created.values()) {
Assert.assertFalse("implicit tags should not be notified", item instanceof Tag);
}
ml.clear();
// new real tags *should* be included in notifications
mbox.createTag(null, tag1, (byte) 0);
Assert.assertFalse("explicit tag create must produce notifications", ml.pms.created.isEmpty());
Assert.assertTrue("explicit tags must be notified", ml.pms.created.values().iterator().next() instanceof Tag);
ml.clear();
// changes to implicit tags should not be included in notifications
int msgId = mbox.addMessage(null, ThreaderTest.getRootMessage(), dopt, null).getId();
for (Change chg : ml.pms.modified.values()) {
Assert.assertFalse("implicit tag changes should not be notified", chg.what instanceof Tag);
}
ml.clear();
// changes to real tags *should* be included in notifications
mbox.alterTag(null, msgId, MailItem.Type.MESSAGE, tag1, true, null);
Assert.assertFalse("explicit tag apply must produce notifications", ml.pms.modified == null || ml.pms.modified.isEmpty());
boolean found = false;
for (Change chg : ml.pms.modified.values()) {
found |= chg.what instanceof Tag;
}
Assert.assertTrue("explicit tag apply must be notified", found);
} finally {
MailboxListener.unregister(ml);
}
}
use of com.zimbra.cs.session.PendingModifications.Change in project zm-mailbox by Zimbra.
the class ShareStartStopListener method notify.
@Override
public void notify(ChangeNotification notification) {
if (notification.mods.created != null) {
// A new folder with non-empty ACL means start of sharing.
for (MailItem created : notification.mods.created.values()) {
if (created instanceof Folder) {
Folder folder = (Folder) created;
if (folder.getACL() != null) {
startShare(folder);
}
}
}
}
if (notification.mods.modified != null) {
// ACL change on folder can mean start or stop of sharing.
for (Change change : notification.mods.modified.values()) {
if ((change.why & Change.ACL) != 0 && change.preModifyObj instanceof Folder && change.what instanceof Folder) {
Folder before = (Folder) change.preModifyObj;
Folder after = (Folder) change.what;
boolean beforeHasACL = before.getACL() != null;
boolean afterHasACL = after.getACL() != null;
if (!beforeHasACL && afterHasACL) {
startShare(after);
} else if (beforeHasACL && !afterHasACL) {
stopShare(after);
}
// Note: No attempt is made to start/stop share based on the folder moving into
// or out of trash/spam folder. This is because no notification is generated
// when the folder crosses the boundary by virtue of one of its parent folders moving.
}
}
}
if (notification.mods.deleted != null) {
// Deletion of a folder with non-empty ACL stops sharing.
for (Change change : notification.mods.deleted.values()) {
Folder folder = null;
if (change.what instanceof Folder) {
folder = (Folder) change.what;
} else if (change.preModifyObj instanceof Folder) {
folder = (Folder) change.preModifyObj;
}
if (folder != null && folder.getACL() != null) {
stopShare(folder);
}
}
}
}
use of com.zimbra.cs.session.PendingModifications.Change 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();
}
}
}
use of com.zimbra.cs.session.PendingModifications.Change in project zm-mailbox by Zimbra.
the class CalListCache method notifyCommittedChanges.
void notifyCommittedChanges(PendingModifications mods, int changeId) {
ChangeMap changeMap = new ChangeMap(1);
if (mods.created != null) {
for (Map.Entry<ModificationKey, MailItem> entry : mods.created.entrySet()) {
MailItem item = entry.getValue();
if (item instanceof Folder) {
Folder folder = (Folder) item;
MailItem.Type viewType = folder.getDefaultView();
if (viewType == MailItem.Type.APPOINTMENT || viewType == MailItem.Type.TASK) {
ChangedFolders changedFolders = changeMap.getAccount(entry.getKey().getAccountId());
changedFolders.created.add(folder.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 Folder) {
Folder folder = (Folder) whatChanged;
MailItem.Type viewType = folder.getDefaultView();
if (viewType == MailItem.Type.APPOINTMENT || viewType == MailItem.Type.TASK) {
ChangedFolders changedFolders = changeMap.getAccount(entry.getKey().getAccountId());
int folderId = folder.getId();
if ((change.why & Change.FOLDER) != 0) {
// moving the calendar folder to another parent folder
int parentFolder = folder.getFolderId();
changedFolders.created.add(folderId);
if (parentFolder == Mailbox.ID_FOLDER_TRASH) {
changedFolders.deleted.add(folderId);
} else {
changedFolders.created.add(folderId);
}
} else {
// not a folder move, but something else changed, either calendar's metadata
// or a child item (appointment/task)
changedFolders.modified.add(folderId);
}
}
} else if (whatChanged instanceof Message) {
Message msg = (Message) whatChanged;
if (msg.hasCalendarItemInfos()) {
if (msg.getFolderId() == Mailbox.ID_FOLDER_INBOX || (change.why & Change.FOLDER) != 0) {
// If message was moved, we don't know which folder it was moved from.
// Just invalidate the Inbox because that's the only message folder we care
// about in calendaring.
ChangedFolders changedFolders = changeMap.getAccount(entry.getKey().getAccountId());
changedFolders.modified.add(Mailbox.ID_FOLDER_INBOX);
}
}
}
}
}
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. Let's just assume it's a calendar folder id and check
// against the cached list.
ChangedFolders changedFolders = changeMap.getAccount(entry.getKey().getAccountId());
changedFolders.deleted.add(entry.getKey().getItemId());
}
// Let's not worry about hard deletes of invite/reply emails. It has no practical benefit.
}
}
try {
for (Map.Entry<String, ChangedFolders> entry : changeMap.entrySet()) {
ChangedFolders changedFolders = entry.getValue();
if (changedFolders.isEmpty())
continue;
String accountId = entry.getKey();
AccountKey key = new AccountKey(accountId);
CalList list = mMemcachedLookup.get(key);
if (list != null) {
boolean updated = false;
CalList newList = new CalList(list);
for (Integer folderId : changedFolders.created) {
if (!list.contains(folderId)) {
updated = true;
newList.add(folderId);
}
}
for (Integer folderId : changedFolders.modified) {
if (list.contains(folderId))
updated = true;
}
for (Integer folderId : changedFolders.deleted) {
if (list.contains(folderId)) {
updated = true;
newList.remove(folderId);
}
}
// There was a change. Increment the version and put back to cache.
if (updated) {
newList.incrementSeq();
mMemcachedLookup.put(key, newList);
}
}
}
} catch (ServiceException e) {
ZimbraLog.calendar.warn("Unable to notify calendar list cache. Some cached data may become stale.", e);
}
}
Aggregations