示例#1
0
 /*
  * Reset address book data. Replace local with remote contact data, but
  * preserve non-Yahoo related contact fields. Also, remove all local
  * contacts which have been deleted remotely, and reset sync state.
  *
  * Reset is done after a metadata version change or irrecoverable sync
  * error. Reset does not depend on metadata format since the data is
  * completely replaced.
  */
 private void reset() throws IOException, YabException, ServiceException {
   // Fetch all remote contacts and merge with local data
   Mailbox mbox = localData.getMailbox();
   contactChanges = Collections.emptyMap();
   SyncRequest req = getSyncRequest(0);
   SyncResponse res = (SyncResponse) req.send();
   mbox.lock.lock();
   try {
     processContactResults(req.getEvents());
     List<SyncResponseEvent> events = res.getEvents();
     if (!events.isEmpty()) {
       // Categories are not returned unless there are contact changes
       processEvents(events);
       processCategories(res.getCategories());
     }
     // Remove contacts which no longer exist remotely
     localData.deleteMissingContacts(getRemoteIds(events));
     SyncState ss = new SyncState();
     // Save new sync state
     ss.setLastRevision(String.valueOf(res.getRevision()));
     ss.setLastModSequence(mbox.getLastChangeID());
     localData.saveState(ss);
   } finally {
     mbox.lock.release();
   }
 }
示例#2
0
 private void processCategories(List<Category> categories) throws ServiceException {
   Stats stats = new Stats();
   // Get all existing contact groups
   Map<Integer, ContactGroup> groups = new HashMap<Integer, ContactGroup>();
   for (Category cat : categories) {
     groups.put(cat.getId(), new ContactGroup(cat.getName()));
   }
   // Get all contact mappings and update contact group dlist information
   Collection<DataSourceItem> mappings = localData.getAllContactMappings();
   for (DataSourceItem dsi : mappings) {
     if (dsi.remoteId != null) {
       RemoteId rid = RemoteId.parse(dsi.remoteId);
       if (rid.isContact()) {
         Contact contact = getContact(dsi);
         if (contact != null) {
           List<Category> cats = contact.getCategories();
           if (!cats.isEmpty()) {
             List<String> emails = getEmailAddresses(contact);
             if (!emails.isEmpty()) {
               for (Category cat : cats) {
                 ContactGroup group = groups.get(cat.getId());
                 if (group != null) {
                   group.addEmail(emails.get(0));
                 }
               }
             }
           }
         }
       }
     }
   }
   // Remove contact groups deleted remotely
   for (DataSourceItem dsi : mappings) {
     if (dsi.remoteId != null) {
       RemoteId rid = RemoteId.parse(dsi.remoteId);
       if (rid.isCategory() && !groups.containsKey(rid.getId())) {
         localData.deleteContactGroup(dsi.itemId);
         groups.remove(rid.getId());
         stats.deleted++;
       }
     }
   }
   // Create new or update existring contact groups
   for (Map.Entry<Integer, ContactGroup> me : groups.entrySet()) {
     RemoteId rid = RemoteId.categoryId(me.getKey());
     updateGroup(rid.toString(), me.getValue(), stats);
   }
   LOG.debug("Processed remote category changes: %s", stats);
 }
示例#3
0
 private void updateContact(Contact contact, DataSourceItem dsi, Stats stats)
     throws ServiceException {
   int cid = contact.getId();
   ContactData cd = new ContactData(contact);
   if (!cd.isEmpty()) {
     ParsedContact pc = new ParsedContact(localData.getContact(dsi.itemId));
     cd.modifyParsedContact(pc);
     localData.modifyContact(dsi.itemId, pc);
     updateContactMapping(dsi.itemId, contact);
     stats.updated++;
     LOG.debug("Modified local contact: itemId=%d, cid=%d", dsi.itemId, cid);
   } else {
     LOG.debug(
         "Removing contact with cid %d since changes would " + "result in an empty contact", cid);
     deleteContact(dsi.itemId, stats);
   }
 }
示例#4
0
 private void updateGroup(String remoteId, ContactGroup group, Stats stats)
     throws ServiceException {
   DataSourceItem dsi = localData.getReverseMapping(remoteId);
   if (dsi.itemId > 0) {
     if (group.isEmpty()) {
       localData.deleteContactGroup(dsi.itemId);
       stats.deleted++;
     } else {
       ContactGroup oldGroup = localData.getContactGroup(dsi.itemId);
       if (!group.equals(oldGroup)) {
         localData.modifyContactGroup(dsi.itemId, group);
         stats.updated++;
       }
     }
   } else if (!group.isEmpty()) {
     localData.createContactGroup(remoteId, group);
     stats.added++;
   }
 }
示例#5
0
 private SyncRequestEvent getContactEvent(Change change) throws ServiceException {
   int itemId = change.getItemId();
   if (change.isAdd() || change.isUpdate()) {
     com.zimbra.cs.mailbox.Contact zcontact = localData.getContact(itemId);
     if (ContactGroup.isContactGroup(zcontact)) {
       // Delete mapping so contact group will no longer be sync'd
       localData.deleteMapping(itemId);
     } else {
       ContactData cd = new ContactData(zcontact);
       if (change.isAdd()) {
         Contact contact = cd.getContact();
         pushedContacts.put(itemId, contact);
         return SyncRequestEvent.addContact(contact);
       } else {
         // LOG.debug("count = " + cd.getCategories().size());
         Contact newContact = cd.getContact();
         Contact oldContact = getContact(localData.getMapping(itemId));
         // LOG.debug("oldContact: " + oldContact);
         // LOG.debug("newContact: " + newContact);
         newContact.setId(oldContact.getId());
         // Add categories so we can track updates
         for (Category category : oldContact.getCategories()) {
           newContact.addCategory(category);
         }
         pushedContacts.put(itemId, newContact);
         ContactChange cc = cd.getContactChange(oldContact);
         return cc.isEmpty() ? null : SyncRequestEvent.updateContact(cc);
       }
     }
   } else if (change.isDelete()) {
     DataSourceItem dsi = localData.getMapping(itemId);
     RemoteId rid = RemoteId.parse(dsi.remoteId);
     if (rid.isContact()) {
       pushedContacts.put(itemId, new Contact(rid.getId()));
       return SyncRequestEvent.removeContact(rid.getId());
     } else {
       // Remove mapping for deleted contact group
       localData.deleteMapping(rid.getId());
     }
   }
   return null;
 }
示例#6
0
 private Document getDocument(DataSourceItem dsi) throws ServiceException {
   String xml = localData.getData(dsi);
   if (xml != null) {
     try {
       return session.parseDocument(xml);
     } catch (IOException e) {
       throw new SyncException("Unable to parse entry data for item id: " + dsi.itemId, null);
     }
   }
   throw new SyncException("Missing data source item for id: " + dsi.itemId);
 }
示例#7
0
 public void sync() throws IOException, YabException, ServiceException {
   SyncState ss = localData.loadState();
   if (ss == null) {
     LOG.info("Sync state version change - resetting contact data");
     reset();
     return;
   }
   Mailbox mbox = localData.getMailbox();
   SyncRequest req;
   mbox.lock.lock();
   try {
     getLocalChanges(ss);
     ss.setLastModSequence(mbox.getLastChangeID());
     req = getSyncRequest(getLastRevision(ss));
   } finally {
     mbox.lock.release();
   }
   while (req != null) {
     SyncResponse res = (SyncResponse) req.send();
     mbox.lock.lock();
     try {
       getLocalChanges(ss);
       ss.setLastRevision(String.valueOf(res.getRevision()));
       processContactResults(req.getEvents());
       List<SyncResponseEvent> events = res.getEvents();
       if (!events.isEmpty()) {
         // Categories are not returned unless there are contact changes
         processEvents(events);
         processCategories(res.getCategories());
       }
       ss.setLastModSequence(mbox.getLastChangeID());
       localData.saveState(ss);
       req = hasLocalChanges() ? getSyncRequest(res.getRevision()) : null;
     } finally {
       mbox.lock.release();
     }
   }
 }
示例#8
0
 private void addContact(Contact contact, Stats stats) throws ServiceException {
   int cid = contact.getId();
   ContactData cd = new ContactData(contact);
   if (!cd.isEmpty()) {
     ParsedContact pc = cd.getParsedContact();
     int itemId = localData.createContact(pc).getId();
     updateContactMapping(itemId, contact);
     stats.added++;
     LOG.debug("Created new local contact: itemId=%d, cid=%d", itemId, cid);
   } else {
     LOG.debug(
         "Not adding contact with cid %d since it would " + "result in an empty contact", cid);
   }
 }
示例#9
0
 private boolean checkErrorResult(SyncRequestEvent event, int itemId) throws ServiceException {
   Result result = event.getResult();
   if (result.isError()) {
     ErrorResult error = (ErrorResult) result;
     if (!ignoreError(error, event)) {
       String msg =
           String.format(
               "Error code: %s\nError description: %s\nRequest event:\n%s",
               error.getCode(), error.getUserMessage(), event);
       localData.syncContactFailed(new SyncException("Sync request event failed"), itemId, msg);
       return false;
     }
   }
   return true;
 }
示例#10
0
 private void processEvents(List<SyncResponseEvent> events) throws ServiceException {
   Stats stats = new Stats();
   int errors = 0;
   for (SyncResponseEvent event : events) {
     if (!event.isAddressBookReset()) {
       try {
         processEvent(event, stats);
       } catch (ServiceException e) {
         String msg = String.format("Contact event:\n%s", event);
         localData.syncContactFailed(e, -1, msg);
         errors++;
       }
     }
   }
   LOG.debug("Processed %d remote contact changes: %s", stats.total(), stats);
   if (errors > 0) {
     LOG.debug("%d contact changes could not be processed due to errors", errors);
   }
 }
示例#11
0
 private void processEvent(SyncResponseEvent event, Stats stats) throws ServiceException {
   Contact contact = event.getContact();
   RemoteId rid = RemoteId.contactId(contact.getId());
   DataSourceItem dsi = localData.getReverseMapping(rid.toString());
   if (event.isAddContact() || event.isUpdateContact()) {
     if (dsi.itemId > 0) {
       // Don't update contact if it has been modified locally since
       // sync request was sent. Wait until we send the new changes
       // before updating the local contact.
       if (!contactChanges.containsKey(dsi.itemId)) {
         updateContact(contact, dsi, stats);
       }
     } else {
       addContact(contact, stats);
     }
   } else if (event.isRemoveContact()) {
     if (dsi.itemId > 0) {
       deleteContact(dsi.itemId, stats);
     }
   }
 }
示例#12
0
 private void updateContactMapping(int itemId, Contact contact) throws ServiceException {
   RemoteId rid = RemoteId.contactId(contact.getId());
   localData.updateMapping(itemId, rid.toString(), toXml(contact));
 }
示例#13
0
 private void deleteContact(int itemId, Stats stats) throws ServiceException {
   localData.deleteContact(itemId);
   localData.deleteMapping(itemId);
   stats.deleted++;
   LOG.debug("Deleted contact with item id %d", itemId);
 }
示例#14
0
 private void getLocalChanges(SyncState ss) throws ServiceException {
   contactChanges = localData.getContactChanges(ss.getLastModSequence());
   LOG.debug("Found %d local contact changes", contactChanges.size());
 }