Search in sources :

Example 1 with SyncToken

use of com.zimbra.cs.service.util.SyncToken in project zm-mailbox by Zimbra.

the class WaitSetRequest method parseAddUpdateAccounts.

/**
     * @param allowedAccountIds NULL means "all allowed" (admin)
     */
static List<WaitSetAccount> parseAddUpdateAccounts(ZimbraSoapContext zsc, Element elt, Set<MailItem.Type> defaultInterest) throws ServiceException {
    List<WaitSetAccount> toRet = new ArrayList<WaitSetAccount>();
    if (elt != null) {
        for (Iterator<Element> iter = elt.elementIterator(MailConstants.E_A); iter.hasNext(); ) {
            Element a = iter.next();
            String id;
            String name = a.getAttribute(MailConstants.A_NAME, null);
            if (name != null) {
                Account acct = Provisioning.getInstance().get(AccountBy.name, name);
                if (acct != null) {
                    id = acct.getId();
                } else {
                    WaitSetError err = new WaitSetError(name, WaitSetError.Type.NO_SUCH_ACCOUNT);
                    continue;
                }
            } else {
                id = a.getAttribute(MailConstants.A_ID);
            }
            WaitSetMgr.checkRightForAdditionalAccount(id, zsc);
            String tokenStr = a.getAttribute(MailConstants.A_TOKEN, null);
            SyncToken token = tokenStr != null ? new SyncToken(tokenStr) : null;
            Set<MailItem.Type> interests = parseInterestStr(a.getAttribute(MailConstants.A_TYPES, null), defaultInterest);
            toRet.add(new WaitSetAccount(id, token, interests));
        }
    }
    return toRet;
}
Also used : WaitSetAccount(com.zimbra.cs.session.WaitSetAccount) Account(com.zimbra.cs.account.Account) SyncToken(com.zimbra.cs.service.util.SyncToken) WaitSetAccount(com.zimbra.cs.session.WaitSetAccount) Element(com.zimbra.common.soap.Element) ArrayList(java.util.ArrayList) WaitSetError(com.zimbra.cs.session.WaitSetError)

Example 2 with SyncToken

use of com.zimbra.cs.service.util.SyncToken in project zm-mailbox by Zimbra.

the class SyncTest method testPaginatedSync.

/**
     * Test Paginations.
     * 1. Modified paged , delete paged (del cutoff modseq <  modified cutoff mod Seq)
     * 2. All modified , delete paged.
     * 3. Modified paged , delete paged (del cutoff modseq >  modified cutoff mod Seq)
     * 4. Modified paged , all deletes.
     * 5. Modified and delete unpaged.
     * @throws Exception
     */
@Test
public void testPaginatedSync() throws Exception {
    Account acct = Provisioning.getInstance().get(Key.AccountBy.name, "test@zimbra.com");
    Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(acct);
    mbox.beginTrackingSync();
    SyncRequest request = new SyncRequest();
    request.setFolderId("2");
    Map<String, Object> context = new HashMap<String, Object>();
    context.put(SoapEngine.ZIMBRA_CONTEXT, new ZimbraSoapContext(AuthProvider.getAuthToken(acct), acct.getId(), SoapProtocol.Soap12, SoapProtocol.Soap12));
    Element response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    SyncResponse syncRes = JaxbUtil.elementToJaxb(response);
    Set<Integer> itemsDeleted = new HashSet<Integer>();
    Set<Integer> itemsAddedOrModified = new HashSet<Integer>();
    String token = syncRes.getToken();
    int msgId1 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId2 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId3 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId4 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId5 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId6 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    itemsAddedOrModified.add(msgId1);
    itemsAddedOrModified.add(msgId2);
    itemsAddedOrModified.add(msgId3);
    itemsAddedOrModified.add(msgId4);
    itemsAddedOrModified.add(msgId5);
    itemsAddedOrModified.add(msgId6);
    request = new SyncRequest();
    request.setChangeLimit(100);
    request.setFolderId("2");
    request.setToken(token);
    response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    syncRes = JaxbUtil.elementToJaxb(response);
    List<Object> listObj = syncRes.getItems();
    removeItemsFromList(itemsAddedOrModified, listObj);
    Assert.assertTrue(itemsAddedOrModified.isEmpty());
    token = syncRes.getToken();
    // Modified paged , delete paged (del cutoff modseq < modified cutoff mod Seq);
    mbox.move(null, msgId1, Type.MESSAGE, 5);
    mbox.move(null, msgId2, Type.MESSAGE, 5);
    mbox.delete(null, msgId3, Type.MESSAGE);
    mbox.alterTag(null, new int[] { msgId5, msgId6, msgId4 }, MailItem.Type.MESSAGE, Flag.FlagInfo.UNREAD, true, null);
    itemsDeleted.add(msgId1);
    itemsDeleted.add(msgId2);
    itemsDeleted.add(msgId3);
    itemsAddedOrModified.add(msgId4);
    itemsAddedOrModified.add(msgId5);
    itemsAddedOrModified.add(msgId6);
    request = new SyncRequest();
    request.setToken(token);
    request.setFolderId("2");
    request.setChangeLimit(2);
    request.setDeleteLimit(2);
    response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    syncRes = JaxbUtil.elementToJaxb(response);
    token = syncRes.getToken();
    //Expected 2 deletes and 2 added message. and hasMore=true
    SyncDeletedInfo sdi1 = syncRes.getDeleted();
    String[] deletes = sdi1.getIds().split(",");
    Assert.assertEquals(2, deletes.length);
    removeDeleteFromList(itemsDeleted, deletes);
    //pending to sync.
    Assert.assertEquals(1, itemsDeleted.size());
    listObj = syncRes.getItems();
    Assert.assertEquals(2, listObj.size());
    removeItemsFromList(itemsAddedOrModified, listObj);
    //pending to sync.
    Assert.assertEquals(1, itemsAddedOrModified.size());
    Assert.assertTrue(syncRes.getMore());
    SyncToken syncToken = new SyncToken(token);
    int lastChange = syncToken.getChangeId();
    int lastDel = syncToken.getDeleteModSeq();
    Assert.assertTrue(lastDel < lastChange);
    // Test3: previous interrupted sync. All modified and delete paged.
    mbox.move(null, msgId5, Type.MESSAGE, 5);
    mbox.delete(null, msgId4, Type.MESSAGE);
    itemsDeleted.add(msgId4);
    itemsDeleted.add(msgId5);
    request = new SyncRequest();
    request.setToken(token);
    request.setFolderId("2");
    request.setDeleteLimit(2);
    request.setChangeLimit(2);
    response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    syncRes = JaxbUtil.elementToJaxb(response);
    token = syncRes.getToken();
    //Expected all modified and 2 deleted message. and hasMore=true
    SyncDeletedInfo sdi2 = syncRes.getDeleted();
    deletes = sdi2.getIds().split(",");
    Assert.assertEquals(2, deletes.length);
    removeDeleteFromList(itemsDeleted, deletes);
    //pending to sync.
    Assert.assertEquals(1, itemsDeleted.size());
    listObj = syncRes.getItems();
    Assert.assertEquals(1, listObj.size());
    removeItemsFromList(itemsAddedOrModified, listObj);
    //All synced.
    Assert.assertEquals(0, itemsAddedOrModified.size());
    Assert.assertTrue(syncRes.getMore());
    syncToken = new SyncToken(token);
    lastChange = syncToken.getChangeId();
    lastDel = syncToken.getDeleteModSeq();
    Assert.assertTrue(lastDel < lastChange);
    Assert.assertTrue(syncRes.getMore());
    // Test3: previous interrupted sync. modified and delete paged.(del cutoff modseq > modified cutoff mod Seq)
    int msgId7 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId8 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId9 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    mbox.alterTag(null, new int[] { msgId9 }, MailItem.Type.MESSAGE, Flag.FlagInfo.UNREAD, true, null);
    mbox.delete(null, msgId6, Type.MESSAGE);
    itemsAddedOrModified.add(msgId7);
    itemsAddedOrModified.add(msgId8);
    itemsAddedOrModified.add(msgId9);
    itemsDeleted.add(msgId6);
    request = new SyncRequest();
    request.setToken(token);
    request.setFolderId("2");
    request.setChangeLimit(2);
    request.setDeleteLimit(1);
    response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    syncRes = JaxbUtil.elementToJaxb(response);
    token = syncRes.getToken();
    //Expected 1 deletes and 2 added message. and hasMore=true
    SyncDeletedInfo sdi3 = syncRes.getDeleted();
    deletes = sdi3.getIds().split(",");
    Assert.assertEquals(1, deletes.length);
    removeDeleteFromList(itemsDeleted, deletes);
    //pending to sync.
    Assert.assertEquals(1, itemsDeleted.size());
    listObj = syncRes.getItems();
    Assert.assertEquals(2, listObj.size());
    removeItemsFromList(itemsAddedOrModified, listObj);
    //pending to sync.
    Assert.assertEquals(1, itemsAddedOrModified.size());
    Assert.assertTrue(syncRes.getMore());
    syncToken = new SyncToken(token);
    lastChange = syncToken.getChangeId();
    lastDel = syncToken.getDeleteModSeq();
    Assert.assertTrue(lastDel > lastChange);
    Assert.assertTrue(syncRes.getMore());
    // Test4: previous interrupted sync. Modified paged and All Deletes.
    int msgId10 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId11 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    int msgId12 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    mbox.alterTag(null, new int[] { msgId9 }, MailItem.Type.MESSAGE, Flag.FlagInfo.UNREAD, true, null);
    mbox.delete(null, msgId9, Type.MESSAGE);
    itemsAddedOrModified.add(msgId10);
    itemsAddedOrModified.add(msgId11);
    itemsAddedOrModified.add(msgId12);
    itemsAddedOrModified.remove(msgId9);
    itemsDeleted.add(msgId9);
    request = new SyncRequest();
    request.setToken(token);
    request.setFolderId("2");
    request.setDeleteLimit(2);
    request.setChangeLimit(2);
    response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    syncRes = JaxbUtil.elementToJaxb(response);
    token = syncRes.getToken();
    //Expected 2 deletes and 2 added message. and hasMore=true.
    //trick: deleted the remaining to sync modified message from last sync.
    SyncDeletedInfo sdi4 = syncRes.getDeleted();
    deletes = sdi4.getIds().split(",");
    Assert.assertEquals(2, deletes.length);
    removeDeleteFromList(itemsDeleted, deletes);
    //All synced.
    Assert.assertEquals(0, itemsDeleted.size());
    listObj = syncRes.getItems();
    Assert.assertEquals(2, listObj.size());
    removeItemsFromList(itemsAddedOrModified, listObj);
    //pending to sync.
    Assert.assertEquals(1, itemsAddedOrModified.size());
    Assert.assertTrue(syncRes.getMore());
    syncToken = new SyncToken(token);
    lastChange = syncToken.getChangeId();
    lastDel = syncToken.getDeleteModSeq();
    Assert.assertTrue(lastDel == mbox.getLastChangeID());
    Assert.assertTrue(syncRes.getMore());
    //Test5: All deletes and all Modified.
    int msgId13 = mbox.addMessage(null, MailboxTestUtil.generateMessage("test subject"), MailboxTest.STANDARD_DELIVERY_OPTIONS, null).getId();
    mbox.alterTag(null, new int[] { msgId11 }, MailItem.Type.MESSAGE, Flag.FlagInfo.UNREAD, true, null);
    mbox.delete(null, msgId10, Type.MESSAGE);
    itemsAddedOrModified.add(msgId13);
    itemsAddedOrModified.add(msgId11);
    itemsDeleted.add(msgId10);
    request = new SyncRequest();
    request.setToken(token);
    request.setFolderId("2");
    request.setDeleteLimit(0);
    request.setChangeLimit(10);
    response = new Sync().handle(JaxbUtil.jaxbToElement(request), context);
    syncRes = JaxbUtil.elementToJaxb(response);
    token = syncRes.getToken();
    //Expected 2 deletes and 2 added message. and hasMore=true.
    //trick: deleted the remaining to sync modified message from last sync.
    SyncDeletedInfo sdi5 = syncRes.getDeleted();
    deletes = sdi5.getIds().split(",");
    Assert.assertEquals(1, deletes.length);
    removeDeleteFromList(itemsDeleted, deletes);
    // All synced.
    Assert.assertEquals(0, itemsDeleted.size());
    listObj = syncRes.getItems();
    Assert.assertEquals(3, listObj.size());
    removeItemsFromList(itemsAddedOrModified, listObj);
    //All synced.
    Assert.assertEquals(0, itemsAddedOrModified.size());
    syncToken = new SyncToken(token);
    lastChange = syncToken.getChangeId();
    lastDel = syncToken.getDeleteModSeq();
    Assert.assertEquals(lastChange, mbox.getLastChangeID());
    Assert.assertTrue(lastDel == -1);
    Assert.assertNull(syncRes.getMore());
}
Also used : Account(com.zimbra.cs.account.Account) HashMap(java.util.HashMap) Element(com.zimbra.common.soap.Element) SyncToken(com.zimbra.cs.service.util.SyncToken) Mailbox(com.zimbra.cs.mailbox.Mailbox) SyncRequest(com.zimbra.soap.mail.message.SyncRequest) SyncResponse(com.zimbra.soap.mail.message.SyncResponse) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) SyncDeletedInfo(com.zimbra.soap.mail.type.SyncDeletedInfo) HashSet(java.util.HashSet) Test(org.junit.Test) MailboxTest(com.zimbra.cs.mailbox.MailboxTest)

Example 3 with SyncToken

use of com.zimbra.cs.service.util.SyncToken in project zm-mailbox by Zimbra.

the class MailboxUpgrade method migrateHighestIndexed.

private static void migrateHighestIndexed(Mailbox mbox) throws ServiceException {
    DbConnection conn = DbPool.getConnection(mbox);
    PreparedStatement stmt;
    ResultSet rs;
    try {
        // fetch highest_indexed
        String highestIndexed = null;
        stmt = conn.prepareStatement("SELECT highest_indexed FROM " + DbMailbox.qualifyZimbraTableName(mbox, "mailbox") + " WHERE id = ?");
        stmt.setInt(1, mbox.getId());
        rs = stmt.executeQuery();
        if (rs.next()) {
            highestIndexed = rs.getString(1);
        }
        rs.close();
        stmt.close();
        if (Strings.isNullOrEmpty(highestIndexed)) {
            return;
        }
        SyncToken token;
        try {
            token = new SyncToken(highestIndexed);
        } catch (ServiceException e) {
            return;
        }
        // update index_id where mod_content/mod_metadata > highest_indexed
        stmt = conn.prepareStatement("UPDATE " + DbMailItem.getMailItemTableName(mbox) + " SET index_id = 0 WHERE " + DbMailItem.IN_THIS_MAILBOX_AND + "mod_content > ? AND mod_metadata > ? AND index_id IS NOT NULL");
        int pos = DbMailItem.setMailboxId(stmt, mbox, 1);
        stmt.setInt(pos++, token.getChangeId());
        stmt.setInt(pos++, token.getChangeId());
        stmt.executeUpdate();
        stmt.close();
        // clear highest_indexed
        stmt = conn.prepareStatement("UPDATE " + DbMailbox.qualifyZimbraTableName(mbox, "mailbox") + " SET highest_indexed = NULL WHERE id = ?");
        stmt.setInt(1, mbox.getId());
        stmt.executeUpdate();
        stmt.close();
        conn.commit();
    } catch (SQLException e) {
        conn.rollback();
        throw ServiceException.FAILURE("Failed to migrate highest_indexed", e);
    } finally {
        conn.closeQuietly();
    }
}
Also used : SyncToken(com.zimbra.cs.service.util.SyncToken) ServiceException(com.zimbra.common.service.ServiceException) SQLException(java.sql.SQLException) ResultSet(java.sql.ResultSet) PreparedStatement(java.sql.PreparedStatement) DbConnection(com.zimbra.cs.db.DbPool.DbConnection)

Example 4 with SyncToken

use of com.zimbra.cs.service.util.SyncToken in project zm-mailbox by Zimbra.

the class Sync method handle.

@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
    ZimbraSoapContext zsc = getZimbraSoapContext(context);
    Mailbox mbox = getRequestedMailbox(zsc);
    OperationContext octxt = getOperationContext(zsc, context);
    ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
    SyncRequest syncRequest = JaxbUtil.elementToJaxb(request);
    String token = syncRequest.getToken();
    Element response = zsc.createElement(MailConstants.SYNC_RESPONSE);
    response.addAttribute(MailConstants.A_CHANGE_DATE, System.currentTimeMillis() / 1000);
    // the sync token is of the form "last fully synced change id" (e.g. "32425") or
    // last fully synced change id-last item synced in next change id" (e.g. "32425-99213") or
    // last fully synced change id-last item synced in next change id and last fully synced delete change id" (e.g. "32425-99213:d1231232") or
    // last fully synced change id-last item synced in next change id and 
    // last fully synced delete id-last item synced in next delete id (e.g. "32425-99213:d12312-82134")
    SyncToken syncToken = null;
    int tokenInt = 0;
    if (!StringUtil.isNullOrEmpty(token)) {
        syncToken = new SyncToken(token);
        tokenInt = syncToken.getChangeId();
    }
    if (syncToken == null) {
        syncToken = new SyncToken(0);
    }
    int deleleLimit = syncRequest.getDeleteLimit();
    int changeLimit = syncRequest.getChangeLimit();
    // server can apply delete pagination through debugconfig/localconfig.
    if (deleleLimit <= 0) {
        deleleLimit = DebugConfig.syncMaximumDeleteCount;
    }
    // client specify more than DebugConfig.syncMaximumChangeCount It will use DebugConfig.syncMaximumChangeCount
    if (changeLimit <= 0 || changeLimit > DebugConfig.syncMaximumChangeCount) {
        changeLimit = DebugConfig.syncMaximumChangeCount;
    }
    boolean initialSync = tokenInt <= 0;
    // permit the caller to restrict initial sync only to calendar items with a recurrence after a given date
    long calendarStart = (syncRequest.getCalendarCutoff() != null) ? syncRequest.getCalendarCutoff() : -1;
    int messageSyncStart = (syncRequest.getMsgCutoff() != null) ? syncRequest.getMsgCutoff() : -1;
    // if the sync is constrained to a folder subset, we need to first figure out what can be seen
    Folder root = null;
    ItemId iidFolder = null;
    try {
        iidFolder = new ItemId(request.getAttribute(MailConstants.A_FOLDER, DEFAULT_FOLDER_ID + ""), zsc);
        OperationContext octxtOwner = new OperationContext(mbox);
        root = mbox.getFolderById(octxtOwner, iidFolder.getId());
    } catch (MailServiceException.NoSuchItemException nsie) {
    }
    Set<Folder> visible = octxt.isDelegatedRequest(mbox) ? mbox.getVisibleFolders(octxt) : null;
    FolderNode rootNode = null;
    if (root == null || iidFolder == null) {
        // resolve grantee names of all ACLs on the mailbox
        rootNode = mbox.getFolderTree(octxt, null, true);
    } else {
        // resolve grantee names of all ACLs on all sub-folders of the requested folder
        rootNode = mbox.getFolderTree(octxt, iidFolder, true);
    }
    OperationContextData.addGranteeNames(octxt, rootNode);
    // actually perform the sync
    mbox.lock.lock();
    try {
        mbox.beginTrackingSync();
        if (initialSync) {
            response.addAttribute(MailConstants.A_TOKEN, mbox.getLastChangeID());
            response.addAttribute(MailConstants.A_SIZE, mbox.getSize());
            boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, calendarStart, messageSyncStart, SyncPhase.INITIAL);
            // if no folders are visible, add an empty "<folder/>" as a hint
            if (!anyFolders) {
                response.addElement(MailConstants.E_FOLDER);
            }
        } else {
            boolean typedDeletes = request.getAttributeBool(MailConstants.A_TYPED_DELETES, false);
            String newToken = deltaSync(response, octxt, ifmt, mbox, syncToken, deleleLimit, changeLimit, typedDeletes, root, visible, messageSyncStart);
            response.addAttribute(MailConstants.A_TOKEN, newToken);
        }
    } finally {
        mbox.lock.release();
    }
    return response;
}
Also used : OperationContext(com.zimbra.cs.mailbox.OperationContext) FolderNode(com.zimbra.cs.mailbox.Mailbox.FolderNode) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) Element(com.zimbra.common.soap.Element) Folder(com.zimbra.cs.mailbox.Folder) ItemId(com.zimbra.cs.service.util.ItemId) SyncToken(com.zimbra.cs.service.util.SyncToken) Mailbox(com.zimbra.cs.mailbox.Mailbox) SyncRequest(com.zimbra.soap.mail.message.SyncRequest) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) MailServiceException(com.zimbra.cs.mailbox.MailServiceException)

Example 5 with SyncToken

use of com.zimbra.cs.service.util.SyncToken in project zm-mailbox by Zimbra.

the class Sync method deltaSync.

private static String deltaSync(Element response, OperationContext octxt, ItemIdFormatter ifmt, Mailbox mbox, SyncToken syncToken, int deleteLimit, int changeLimit, boolean typedDeletes, Folder root, Set<Folder> visible, int messageSyncStart) throws ServiceException {
    int begin = syncToken.getChangeId();
    int deleteModSeqCutoff = syncToken.getDeleteModSeq();
    deleteModSeqCutoff = deleteModSeqCutoff <= 0 ? begin : deleteModSeqCutoff;
    int mboxLastChangeId = mbox.getLastChangeID();
    SyncToken newSyncToken = new SyncToken(mboxLastChangeId);
    if (begin >= mboxLastChangeId && deleteModSeqCutoff >= mboxLastChangeId) {
        return newSyncToken.toString();
    }
    int changeItemIdCutoff = syncToken.getOffsetInNext();
    int deleteItemIdCutoff = syncToken.getDeleteOffsetInNext();
    // first, fetch deleted items
    TypedIdList tombstones = mbox.getTombstones(deleteModSeqCutoff);
    Element eDeleted = response.addElement(MailConstants.E_DELETED);
    // then, put together the requested folder hierarchy in 2 different flavors
    List<Folder> hierarchy = (root == null || root.getId() == Mailbox.ID_FOLDER_USER_ROOT ? null : root.getSubfolderHierarchy());
    Set<Integer> targetIds = (root != null && root.getId() == Mailbox.ID_FOLDER_USER_ROOT ? null : new HashSet<Integer>(hierarchy == null ? 0 : hierarchy.size()));
    if (hierarchy != null) {
        for (Folder folder : hierarchy) {
            targetIds.add(folder.getId());
        }
    }
    // then, handle created/modified folders
    if (octxt.isDelegatedRequest(mbox)) {
        // first, make sure that something changed...
        if (!mbox.getModifiedFolders(begin).isEmpty() || !Collections.disjoint(tombstones.types(), FOLDER_TYPES)) {
            // special-case the folder hierarchy for delegated delta sync
            boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, -1, messageSyncStart, SyncPhase.DELTA);
            // if no folders are visible, add an empty "<folder/>" as a hint
            if (!anyFolders) {
                response.addElement(MailConstants.E_FOLDER);
            }
        }
    } else {
        for (Folder folder : mbox.getModifiedFolders(begin)) {
            // no case of "synthetic tombstone" (item falling out of the tree being synced)
            if (targetIds == null || targetIds.contains(folder.getId())) {
                ToXML.encodeFolder(response, ifmt, octxt, folder, Change.ALL_FIELDS);
            } else {
                tombstones.add(folder.getType(), folder.getId(), folder.getUuid(), folder.getModifiedSequence());
            }
        }
    }
    // next, handle created/modified tags
    for (Tag tag : mbox.getModifiedTags(octxt, begin)) {
        ToXML.encodeTag(response, ifmt, octxt, tag, Change.ALL_FIELDS);
    }
    // finally, handle created/modified "other items"
    int itemCount = 0;
    Pair<List<Integer>, TypedIdList> changed = mbox.getModifiedItems(octxt, Math.min(begin, deleteModSeqCutoff), messageSyncStart, MailItem.Type.UNKNOWN, targetIds, deleteModSeqCutoff);
    List<Integer> modified = changed.getFirst();
    // items that have been altered in non-visible folders will be returned as "deleted" in order to handle moves
    if (changed.getSecond() != null) {
        tombstones.addAll(changed.getSecond());
    }
    delta: while (!modified.isEmpty()) {
        List<Integer> batch = modified.subList(0, Math.min(modified.size(), FETCH_BATCH_SIZE));
        for (MailItem item : mbox.getItemById(octxt, batch, MailItem.Type.UNKNOWN)) {
            // detect interrupted sync and resume from the appropriate place
            if ((item.getModifiedSequence() == begin + 1 && item.getId() < changeItemIdCutoff) || item.getModifiedSequence() <= begin) {
                //if interrupted delete and un-interrupted modifications.
                continue;
            }
            // if we've overflowed this sync response, set things up so that a subsequent sync starts from where we're cutting off
            if (itemCount >= changeLimit) {
                response.addAttribute(MailConstants.A_QUERY_MORE, true);
                newSyncToken.setChangeModSeq((item.getModifiedSequence() - 1));
                newSyncToken.setChangeItemId(item.getId());
                newSyncToken.setDeleteModSeq(mboxLastChangeId);
                break delta;
            }
            // For items in the system, if the content has changed since the user last sync'ed
            // (because it was edited or created), just send back the folder ID and saved date --
            // the client will request the whole object out of band -- potentially using the
            // content servlet's "include metadata in headers" hack.
            // If it's just the metadata that changed, send back the set of mutable attributes.
            boolean created = item.getSavedSequence() > begin;
            ToXML.encodeItem(response, ifmt, octxt, item, created ? Change.FOLDER | Change.CONFLICT | Change.DATE | Change.PARENT : MUTABLE_FIELDS);
            itemCount++;
        }
        batch.clear();
    }
    // cleanup: only return a <deleted> element if we're sending back deleted item ids
    if ((deleteLimit > 0 && tombstones.size() > deleteLimit) || deleteItemIdCutoff > 0) {
        PagedDelete pgDel = new PagedDelete(tombstones, typedDeletes);
        pgDel.removeBeforeCutoff(deleteItemIdCutoff, deleteModSeqCutoff);
        if (deleteLimit > 0) {
            pgDel.trimDeletesTillPageLimit(deleteLimit);
        }
        encodePagedDelete(eDeleted, pgDel, newSyncToken, tombstones, typedDeletes);
        if (pgDel.isDeleteOverFlow()) {
            response.addAttribute(MailConstants.A_QUERY_MORE, true);
            response.addAttribute(MailConstants.A_QUERY_MORE, true);
        }
    } else {
        encodeUnpagedDelete(eDeleted, tombstones, typedDeletes);
    }
    return newSyncToken.toString();
}
Also used : PagedDelete(com.zimbra.cs.mailbox.util.PagedDelete) Element(com.zimbra.common.soap.Element) Folder(com.zimbra.cs.mailbox.Folder) TypedIdList(com.zimbra.cs.mailbox.util.TypedIdList) SyncToken(com.zimbra.cs.service.util.SyncToken) MailItem(com.zimbra.cs.mailbox.MailItem) TypedIdList(com.zimbra.cs.mailbox.util.TypedIdList) List(java.util.List) Tag(com.zimbra.cs.mailbox.Tag) HashSet(java.util.HashSet)

Aggregations

SyncToken (com.zimbra.cs.service.util.SyncToken)5 Element (com.zimbra.common.soap.Element)4 Account (com.zimbra.cs.account.Account)2 Folder (com.zimbra.cs.mailbox.Folder)2 Mailbox (com.zimbra.cs.mailbox.Mailbox)2 ZimbraSoapContext (com.zimbra.soap.ZimbraSoapContext)2 SyncRequest (com.zimbra.soap.mail.message.SyncRequest)2 HashSet (java.util.HashSet)2 ServiceException (com.zimbra.common.service.ServiceException)1 DbConnection (com.zimbra.cs.db.DbPool.DbConnection)1 MailItem (com.zimbra.cs.mailbox.MailItem)1 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)1 FolderNode (com.zimbra.cs.mailbox.Mailbox.FolderNode)1 MailboxTest (com.zimbra.cs.mailbox.MailboxTest)1 OperationContext (com.zimbra.cs.mailbox.OperationContext)1 Tag (com.zimbra.cs.mailbox.Tag)1 PagedDelete (com.zimbra.cs.mailbox.util.PagedDelete)1 TypedIdList (com.zimbra.cs.mailbox.util.TypedIdList)1 ItemId (com.zimbra.cs.service.util.ItemId)1 ItemIdFormatter (com.zimbra.cs.service.util.ItemIdFormatter)1