/** * When a post was changed on both the server and the client /after/ synchronization, this method * resolved the corresponding conflict. * * @param clientPost * @param serverPost * @param conflictResolutionStrategy * @param direction */ private void resolveConflict( final SynchronizationPost clientPost, final SynchronizationPost serverPost, final ConflictResolutionStrategy conflictResolutionStrategy, final SynchronizationDirection direction) { switch (conflictResolutionStrategy) { case CLIENT_WINS: if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_SERVER); else clientPost.setAction(SynchronizationAction.OK); break; case SERVER_WINS: if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_CLIENT); else clientPost.setAction(SynchronizationAction.OK); break; // case ASK_USER: temporary disabled // clientPost.setAction(SynchronizationAction.ASK); // break; case FIRST_WINS: if (clientPost.getChangeDate().before(serverPost.getChangeDate())) { if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_SERVER); else clientPost.setAction(SynchronizationAction.OK); } else { if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_CLIENT); else clientPost.setAction(SynchronizationAction.OK); } break; case LAST_WINS: if (clientPost.getChangeDate().after(serverPost.getChangeDate())) { if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_SERVER); else clientPost.setAction(SynchronizationAction.OK); } else { if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_CLIENT); else clientPost.setAction(SynchronizationAction.OK); } break; default: clientPost.setAction(SynchronizationAction.UNDEFINED); break; } }
/** * Computes the synchronization plan. * * @param serverPosts - Note: this map is modified by this method - posts are removed. * @param clientPosts - Note: this list is modified by this method - posts are added. It's the * same list that is returned by this method. * @param lastSyncDate * @param conflictResolutionStrategy * @return The clientPosts with {@link SynchronizationAction}'s and posts from the server added. */ public List<SynchronizationPost> getSyncPlan( final Map<String, SynchronizationPost> serverPosts, final List<SynchronizationPost> clientPosts, final Date lastSyncDate, final ConflictResolutionStrategy conflictResolutionStrategy, final SynchronizationDirection direction) { // is there something to synchronize? if (!present(serverPosts) && !present(clientPosts)) { throw new IllegalArgumentException("both serverPosts and clientPosts must be given"); } if (!present(lastSyncDate)) { throw new IllegalArgumentException("lastSyncDate not present"); } /* * check all client posts */ for (final SynchronizationPost clientPost : clientPosts) { final SynchronizationPost serverPost = serverPosts.get(clientPost.getIntraHash()); if (!present(serverPost)) { /* * no such post on server */ if (clientPost.getCreateDate().before(lastSyncDate)) { /* * post was created before last sync, but when was it changed? */ if (clientPost.getChangeDate().before(lastSyncDate)) { /* * client post was created and last changed before last synchronization * -> post was deleted on server */ if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) clientPost.setAction(SynchronizationAction.DELETE_CLIENT); else clientPost.setAction(SynchronizationAction.OK); } else { /* * CONFLICT! (we can't solve, currently :-( * * Post was changed after last sync but does not exist on server * --> either it was deleted on server, or it's hash has changed * Since it is neither simple to find out if the post has been deleted * or its hash has changed, we create the post on the server. * FIXME: This can result in * a) a duplicate post (if the hash has changed on the client but the * post still exists on the server), or * b) an unwanted post (if the post has been deleted on the server, but * according to the strategy this deletion should be carried out on * the client, too). */ if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) clientPost.setAction(SynchronizationAction.CREATE_SERVER); else clientPost.setAction(SynchronizationAction.OK); } } else { /* * post was created on client after last sync */ if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) clientPost.setAction(SynchronizationAction.CREATE_SERVER); else clientPost.setAction(SynchronizationAction.OK); } continue; } if (!present(serverPost.getChangeDate())) { log.error("post on server has no changedate"); // FIXME what is to do in this case? } if (serverPost.getChangeDate().after(lastSyncDate)) { /* * changed on server since last sync */ if (clientPost.getChangeDate().after(lastSyncDate)) { if (clientPost.getChangeDate().equals(serverPost.getChangeDate())) { /* * both have the same change date -> do nothing */ clientPost.setAction(SynchronizationAction.OK); } else { /* * changed on client, too -> conflict! */ resolveConflict(clientPost, serverPost, conflictResolutionStrategy, direction); } } else { /* * must be updated on client */ if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) clientPost.setAction(SynchronizationAction.UPDATE_CLIENT); else clientPost.setAction(SynchronizationAction.OK); } } else { /* * post is in sync on the server */ if (clientPost.getChangeDate().after(lastSyncDate) && !SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) { /* * ... but not on the client -> update */ clientPost.setAction(SynchronizationAction.UPDATE_SERVER); } else { clientPost.setAction(SynchronizationAction.OK); } } /* * In the next loop we go over all *remaining* server posts and * compare them. To not handle this post twice, we remove it from * the server posts list. */ serverPosts.remove(clientPost.getIntraHash()); } /* * handle the remaining posts that do not exist on the client */ for (final SynchronizationPost serverPost : serverPosts.values()) { if (serverPost.getCreateDate().before(lastSyncDate)) { /* * post is older than lastSyncDate but does not exist on client */ if (serverPost.getChangeDate().before(lastSyncDate)) { /* * post was deleted on client and must now be deleted on server */ if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) serverPost.setAction(SynchronizationAction.DELETE_SERVER); else serverPost.setAction(SynchronizationAction.OK); } else { /* * CONFLICT (see above! FIXME: currently, we can't resolve this) * * we create the post on the client */ if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) serverPost.setAction(SynchronizationAction.CREATE_CLIENT); else serverPost.setAction(SynchronizationAction.OK); } } else { /* * post was created after last sync -> create on client */ if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction)) serverPost.setAction(SynchronizationAction.CREATE_CLIENT); else serverPost.setAction(SynchronizationAction.OK); } /* * add post to list of client posts */ clientPosts.add(serverPost); } /* * FIXME posts with OK-state could be omitted. */ return clientPosts; }