use of com.zimbra.cs.session.PendingModifications.Change 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;
}
use of com.zimbra.cs.session.PendingModifications.Change in project zm-mailbox by Zimbra.
the class DataSourceFolderListener method notify.
@Override
public void notify(ChangeNotification notification) {
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) {
Folder oldFolder = (Folder) entry.getValue().preModifyObj;
if (oldFolder == null) {
ZimbraLog.datasource.warn("Cannot determine the old folder name for %s.", entry.getKey());
continue;
}
try {
ZimbraLog.datasource.info("Deleting datasources that reference %s.", oldFolder.getPath());
Account account = oldFolder.getAccount();
List<DataSource> datasources = account.getAllDataSources();
for (DataSource datasource : datasources) {
if (datasource.getFolderId() == oldFolder.getId()) {
ZimbraLog.datasource.debug("Deleting datasource %s.", datasource.getName());
account.deleteDataSource(datasource.getId());
}
}
} catch (ServiceException e) {
ZimbraLog.datasource.warn("Could not delete datasources for folder.", oldFolder.getPath());
}
}
}
}
}
use of com.zimbra.cs.session.PendingModifications.Change in project zm-mailbox by Zimbra.
the class CalItemReminderService method notify.
@Override
public void notify(ChangeNotification notification) {
Account account = notification.mailboxAccount;
if (notification.mods.created != null) {
for (Map.Entry<ModificationKey, MailItem> entry : notification.mods.created.entrySet()) {
MailItem item = entry.getValue();
if (item instanceof CalendarItem) {
ZimbraLog.scheduler.debug("Handling creation of calendar item (id=%s,mailboxId=%s)", item.getId(), item.getMailboxId());
scheduleNextReminders((CalendarItem) item, true, true);
}
}
}
if (notification.mods.modified != null) {
for (Map.Entry<ModificationKey, Change> entry : notification.mods.modified.entrySet()) {
Change change = entry.getValue();
if (change.what instanceof CalendarItem) {
CalendarItem calItem = (CalendarItem) change.what;
ZimbraLog.scheduler.debug("Handling modification of calendar item (id=%s,mailboxId=%s)", calItem.getId(), calItem.getMailboxId());
boolean calItemCanceled = false;
try {
if ((change.why & Change.FOLDER) != 0 && calItem.inTrash()) {
calItemCanceled = true;
}
} catch (ServiceException e) {
ZimbraLog.scheduler.error("Error in fetching calendar item's folder", e);
}
// cancel any existing reminders and schedule new ones if cal item not canceled
if (cancelExistingReminders(calItem) && !calItemCanceled)
scheduleNextReminders(calItem, true, true);
}
}
}
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.APPOINTMENT || type == MailItem.Type.TASK) {
Mailbox mbox = null;
try {
mbox = MailboxManager.getInstance().getMailboxByAccount(account, MailboxManager.FetchMode.DO_NOT_AUTOCREATE);
} catch (ServiceException e) {
ZimbraLog.scheduler.error("Error looking up the mailbox of account %s", account.getId(), e);
}
if (mbox != null) {
cancelExistingReminders(entry.getKey().getItemId(), mbox.getId());
}
}
}
}
}
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 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);
}
}
Aggregations