use of com.zimbra.common.mailbox.BaseItemInfo in project zm-mailbox by Zimbra.
the class PendingRemoteNotificationsTest method testPendingRemoteNotifications.
@Test
public void testPendingRemoteNotifications() throws Exception {
String acctId = "12aa345b-2b47-44e6-8cb8-7fdfa18c1a9f";
ImapMessageInfo imapMsg1 = new ImapMessageInfo(123, 123, Type.MESSAGE.toString(), 0, null);
BaseItemInfo msg1 = ModificationItem.itemUpdate(imapMsg1, Mailbox.ID_FOLDER_INBOX, acctId);
ImapMessageInfo imapMsg2 = new ImapMessageInfo(456, 456, Type.MESSAGE.toString(), 0, null);
BaseItemInfo msg2 = ModificationItem.itemUpdate(imapMsg2, Mailbox.ID_FOLDER_INBOX, acctId);
PendingRemoteModifications prm = new PendingRemoteModifications();
prm.recordCreated(msg1);
assertTrue(!prm.created.isEmpty());
BaseItemInfo newItem = prm.created.get(new ModificationKey(msg1));
assertEquals(imapMsg1.getId(), newItem.getIdInMailbox());
assertEquals(imapMsg1.getImapUid(), newItem.getImapUid());
assertEquals(acctId, newItem.getAccountId());
// rename a tag
ZimbraTag tag = ModificationItem.tagRename(2, "tagname");
prm.recordModified(tag, acctId, Change.NAME);
Change tagChange = prm.modified.get(new ModificationKey(acctId, tag.getTagId()));
assertNotNull(tagChange);
assertEquals(Change.NAME, tagChange.why);
assertEquals(tag, tagChange.what);
// rename a folder
ModificationItem folder = ModificationItem.folderRename(3, "/newpath", acctId);
prm.recordModified(folder, Change.NAME);
Change folderChange = prm.modified.get(new ModificationKey(folder));
assertNotNull(folderChange);
assertEquals(Change.NAME, folderChange.why);
assertEquals(folder, folderChange.what);
// modify an item
BaseItemInfo updateItem = ModificationItem.itemUpdate(imapMsg2, Mailbox.ID_FOLDER_INBOX, acctId);
prm.recordModified(updateItem, Change.FLAGS);
Change itemChange = prm.modified.get(new ModificationKey(updateItem));
assertNotNull(itemChange);
assertEquals(Change.FLAGS, itemChange.why);
assertEquals(updateItem, itemChange.what);
// adding a delete notification for the previously added message
// should remove it from the created map, and NOT add it to the deleted map
prm.recordDeleted(Type.MESSAGE, acctId, imapMsg1.getId());
assertTrue(prm.created.isEmpty());
assertTrue(prm.deleted == null);
// adding a delete notification for a previously modified message
// should remove it from the modified map AND add it to the deleted map
prm.recordDeleted(Type.MESSAGE, acctId, imapMsg2.getId());
assertNull(prm.modified.get(new ModificationKey(msg2)));
assertEquals(1, prm.deleted.size());
Change deletionChange = prm.deleted.get(new ModificationKey(msg2));
assertEquals(Change.NONE, deletionChange.why);
assertEquals(MailItem.Type.MESSAGE, deletionChange.what);
}
use of com.zimbra.common.mailbox.BaseItemInfo in project zm-mailbox by Zimbra.
the class ImapFolder method handleItemUpdate.
@Override
public void handleItemUpdate(int changeId, Change chg, ImapSession.AddedItems added) {
ImapMailboxStore mboxStore = mailboxStore;
if (mboxStore == null) {
if (ZimbraLog.imap.isDebugEnabled()) {
ZimbraLog.imap.debug("handleItemUpdate called when mailboxStore=null. ImapFolder=%s\n%s", this.path, ZimbraLog.getStackTrace(20));
}
// restore set mailboxStore to null
return;
}
BaseItemInfo item = (BaseItemInfo) chg.what;
int itemId;
int fId;
try {
itemId = item.getIdInMailbox();
fId = item.getFolderIdInMailbox();
} catch (ServiceException e) {
ZimbraLog.imap.warn("unable to get item or folder ID during handling item update", e);
return;
}
if ((folderIdentifier.accountId != null) && !folderIdentifier.accountId.equals(mboxStore.getAccountId())) {
ZimbraLog.imap.debug("handleItemUpdate unexpectedly called with folder=%s when local mailbox is %s", folderIdentifier, mboxStore);
return;
}
boolean inFolder = isVirtual() || fId == folderIdentifier.id;
ImapMessage i4msg = getById(itemId);
if (i4msg == null) {
if (inFolder && !isVirtual()) {
added.add(item);
ZimbraLog.imap.debug(" ** moved (ntfn) {id: %d UID: %d}", itemId, item.getImapUid());
}
} else if (!inFolder && !isVirtual()) {
markMessageExpunged(i4msg);
} else if ((chg.why & Change.IMAP_UID) != 0) {
// if the IMAP uid changed, need to bump it to the back of the sequence!
if (item.getImapUid() > 0 && i4msg.imapUid > item.getImapUid()) {
// this update was the result of renumber which occurred in other session
// we need to ignore it or we end up expunging the newest copy of the message (with current UID) and replacing it with an older UID
ZimbraLog.imap.debug("IMAP UID changed (ntfn) {id: %d UID: %d} but sequence already contains higher UID %s", itemId, item.getImapUid(), i4msg);
return;
}
markMessageExpunged(i4msg);
if (!isVirtual()) {
added.add(item);
}
ZimbraLog.imap.debug(" ** imap uid changed (ntfn) {id: %d UID: %d}", itemId, item.getImapUid());
} else if ((chg.why & (Change.TAGS | Change.FLAGS | Change.UNREAD)) != 0) {
i4msg.setPermanentFlags(item.getFlagBitmask(), item.getTags(), changeId, this);
}
}
use of com.zimbra.common.mailbox.BaseItemInfo 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);
}
}
use of com.zimbra.common.mailbox.BaseItemInfo in project zm-mailbox by Zimbra.
the class PendingLocalModifications method deserialize.
@SuppressWarnings("unchecked")
public static PendingLocalModifications deserialize(Mailbox mbox, byte[] data) throws IOException, ClassNotFoundException, ServiceException {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
PendingLocalModifications pms = new PendingLocalModifications();
try (ObjectInputStream ois = new SecureObjectInputStream(bis, Type.class.getName())) {
pms.changedTypes = (Set<Type>) ois.readObject();
pms.addChangedParentFolderIds((Set<Integer>) ois.readObject());
LinkedHashMap<ModificationKeyMeta, String> metaCreated = (LinkedHashMap<ModificationKeyMeta, String>) ois.readObject();
if (metaCreated != null) {
pms.created = new LinkedHashMap<PendingModifications.ModificationKey, BaseItemInfo>();
Iterator<Entry<ModificationKeyMeta, String>> iter = metaCreated.entrySet().iterator();
while (iter.hasNext()) {
Entry<ModificationKeyMeta, String> entry = iter.next();
Metadata meta = new Metadata(entry.getValue());
MailItem.UnderlyingData ud = new MailItem.UnderlyingData();
ud.deserialize(meta);
MailItem item = MailItem.constructItem(mbox, ud, true);
if (item instanceof Folder) {
Folder folder = ((Folder) item);
folder.setParent(mbox.getFolderById(null, folder.getFolderId()));
}
PendingModifications.ModificationKey key = new PendingModifications.ModificationKey(entry.getKey().accountId, entry.getKey().itemId);
pms.created.put(key, item);
}
}
Map<ModificationKeyMeta, ChangeMeta> metaModified = (Map<ModificationKeyMeta, ChangeMeta>) ois.readObject();
pms.modified = getOriginal(mbox, metaModified);
Map<ModificationKeyMeta, ChangeMeta> metaDeleted = (Map<ModificationKeyMeta, ChangeMeta>) ois.readObject();
pms.deleted = getOriginal(mbox, metaDeleted);
}
return pms;
}
use of com.zimbra.common.mailbox.BaseItemInfo 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());
}
}
Aggregations