Android Open Source - spring-sync Diff Sync






From Project

Back to project page spring-sync.

License

The source code is released under:

Apache License

If you think the Android project spring-sync listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2014 the original author or authors.
 *//  w w  w .j  a  va2  s  .  c  o  m
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.sync.diffsync;

import java.util.List;

import org.springframework.sync.Diff;
import org.springframework.sync.Patch;
import org.springframework.sync.util.DeepCloneUtils;

/**
 * <p>
 * Implements essential steps of the Differential Synchronization routine as described in Neil Fraser's paper at https://neil.fraser.name/writing/sync/eng047-fraser.pdf.
 * </p>
 * 
 * <p>
 * The Differential Synchronization routine can be summarized as follows (with two nodes, A and B):
 * </p>
 * 
 * <ol>
 *   <li>Node A compares a resource with its local shadow of that resource to produce a patch describing the differences</li>
 *   <li>Node A replaces the shadow with the resource.</li>
 *   <li>Node A sends the difference patch to Node B.</li>
 *   <li>Node B applies the patch to its copy of the resource as well as its local shadow of the resource.</li>
 * </ol>
 * 
 * <p>
 * The routine then repeats with Node A and B swapping roles, forming a continuous loop.
 * </p>
 * 
 * <p>
 * To fully understand the Differential Synchronization routine, it's helpful to recognize that a shadow can only be changed by applying a patch or by producing a 
 * difference patch; a resource may be changed by applying a patch or by operations performed outside of the loop.
 * </p>
 * 
 * <p>
 * This class implements the handling of an incoming patch separately from the producing of the outgoing difference patch.
 * It performs no persistence of the patched resources, which is the responsibility of the caller.
 * </p>
 * 
 * @author Craig Walls
 *
 * @param <T> The entity type to perform differential synchronization against.
 */
public class DiffSync<T> {
  
  private ShadowStore shadowStore;

  private Class<T> entityType;

  /**
   * Constructs the Differential Synchronization routine instance.
   * @param shadowStore the shadow store
   * @param entityType the type of entity this DiffSync works with
   */
  public DiffSync(ShadowStore shadowStore, Class<T> entityType) {
    this.shadowStore = shadowStore;
    this.entityType = entityType;
  }
  
  /**
   * Applies one or more patches to a target object and the target object's shadow, per the Differential Synchronization algorithm.
   * The target object will remain unchanged and a patched copy will be returned.
   * 
   * @param target An object to apply a patch to. Will remain unchanged.
   * @param patches The patches to be applied.
   * @return a patched copy of the target.
   */
  public T apply(T target, Patch...patches) {
    T result = target;
    for (Patch patch : patches) {
      result = apply(patch, result);
    }
    return result;
  }
  
  /**
   * Applies a patch to a target object and the target object's shadow, per the Differential Synchronization algorithm.
   * The target object will remain unchanged and a patched copy will be returned.
   * 
   * @param patch The patch to be applied.
   * @param target An object to apply a patch to. Will remain unchanged.
   * @return a patched copy of the target.
   */
  public T apply(Patch patch, T target) {
    if (patch.size() == 0) {
      return target;
    }
    Shadow<T> shadow = getShadow(target);
    if (patch instanceof VersionedPatch) {
      VersionedPatch versionedPatch = (VersionedPatch) patch;
      if (versionedPatch.getServerVersion() < shadow.getServerVersion()) { // e.g., if (patch.serverVersion < shadow.serverVersion)
        shadow = getBackupShadow(target);
        putShadow(shadow);
      }
    }

    if (shouldApplyPatch(patch, shadow)) {
      shadow = new Shadow<T>(patch.apply(shadow.getResource(), entityType), shadow.getServerVersion(), shadow.getClientVersion() + 1);
      Shadow<T> backupShadow = new Shadow<T>(shadow.getResource(), shadow.getServerVersion(), shadow.getClientVersion());
      putShadow(shadow);
      putBackupShadow(backupShadow);
      return patch.apply(DeepCloneUtils.deepClone(target), entityType);
    }
    return target;
  }
  
  /**
   * Applies one or more patches to a target list and the target list's shadow, per the Differential Synchronization algorithm.
   * The target object will remain unchanged and a patched copy will be returned.
   * 
   * @param patches The patch to be applied.
   * @param target A list to apply a patch to. Will remain unchanged.
   * @return a patched copy of the target.
   */
  public List<T> apply(List<T> target, Patch...patches) {
    List<T> result = target;
    for (Patch patch : patches) {
      result = apply(patch, result);
    }
    return result;
  }

  /**
   * Applies a patch to a target list and the target list's shadow, per the Differential Synchronization algorithm.
   * The target object will remain unchanged and a patched copy will be returned.
   * 
   * @param patch The patch to be applied.
   * @param target A list to apply a patch to. Will remain unchanged.
   * @return a patched copy of the target.
   */
  public List<T> apply(Patch patch, List<T> target) {
    if (patch.size() == 0) {
      return target;
    }
    Shadow<List<T>> shadow = getShadow(target);
    if (patch instanceof VersionedPatch) {
      VersionedPatch versionedPatch = (VersionedPatch) patch;
      if (versionedPatch.getServerVersion() < shadow.getServerVersion()) {
        shadow = getBackupShadow(target);
        putListShadow(shadow);
      }
    }
    
    if (shouldApplyPatch(patch, shadow)) {
      shadow = new Shadow<List<T>>(patch.apply(shadow.getResource(), entityType), shadow.getServerVersion(), shadow.getClientVersion() + 1);
      Shadow<List<T>> backupShadow = new Shadow<List<T>>(shadow.getResource(), shadow.getServerVersion(), shadow.getClientVersion());
      putListShadow(shadow);
      putBackupListShadow(backupShadow);
      return patch.apply(DeepCloneUtils.deepClone(target), entityType);
    }
    return target;
  }
  
  /**
   * Compares a target object with its shadow, producing a patch describing the difference.
   * Upon completion, the shadow will be replaced with the target, per the Differential Synchronization algorithm.
   * @param target The target object to produce a difference patch for.
   * @return a {@link VersionedPatch} describing the differences between the target and its shadow.
   */
  public VersionedPatch diff(T target) {
    Shadow<T> shadow = getShadow(target);
    Patch diff = Diff.diff(shadow.getResource(), target);
    VersionedPatch vDiff = new VersionedPatch(diff.getOperations(), shadow.getServerVersion(), shadow.getClientVersion());
    T patched = diff.apply(shadow.getResource(), entityType);
    shadow = new Shadow<T>(patched, shadow.getServerVersion() + 1, shadow.getClientVersion());
    putShadow(shadow);
    return vDiff;
  }
  
  /**
   * Compares a target list with its shadow, producing a patch describing the difference.
   * Upon completion, the shadow will be replaced with the target, per the Differential Synchronization algorithm.
   * @param target The target list to produce a difference patch for.
   * @return a {@link VersionedPatch} describing the differences between the target and its shadow.
   */
  public VersionedPatch diff(List<T> target) {
    Shadow<List<T>> shadow = getShadow(target);
    Patch diff = Diff.diff(shadow.getResource(), target);
    VersionedPatch vDiff = new VersionedPatch(diff.getOperations(), shadow.getServerVersion(), shadow.getClientVersion());
    List<T> patched = diff.apply(shadow.getResource(), entityType);
    shadow = new Shadow<List<T>>(patched, shadow.getServerVersion() + 1, shadow.getClientVersion());
    putListShadow(shadow);
    return vDiff;
  }
  
  // private helper methods
  
  private boolean shouldApplyPatch(Patch patch, Shadow<?> shadow) {
    if (!(patch instanceof VersionedPatch)) return true;
    VersionedPatch versionedPatch = (VersionedPatch) patch;
    return versionedPatch.getServerVersion() == shadow.getServerVersion() && versionedPatch.getClientVersion() == shadow.getClientVersion();
  }
  
  @SuppressWarnings("unchecked")
  private Shadow<T> getShadow(T target) {
    String shadowStoreKey = getShadowStoreKey(target);
    Shadow<T> shadow = (Shadow<T>) shadowStore.getShadow(shadowStoreKey);
    if (shadow == null) {
      shadow = new Shadow<T>(DeepCloneUtils.deepClone(target), 0, 0); // OKAY
    }
    return shadow;
  }

  @SuppressWarnings("unchecked")
  private Shadow<T> getBackupShadow(T target) {
    String shadowStoreKey = getShadowStoreKey(target) + "_backup";
    Shadow<T> shadow = (Shadow<T>) shadowStore.getShadow(shadowStoreKey);
    if (shadow == null) {
      shadow = new Shadow<T>(DeepCloneUtils.deepClone(target), 0, 0); // OKAY
    }
    return shadow;
  }

  private void putShadow(Shadow<T> shadow) {
    String shadowStoreKey = getShadowStoreKey(shadow.getResource());
    shadowStore.putShadow(shadowStoreKey, shadow);
  }

  private void putBackupShadow(Shadow<T> shadow) {
    String shadowStoreKey = getShadowStoreKey(shadow.getResource()) + "_backup";
    shadowStore.putShadow(shadowStoreKey, shadow);
  }

  private void putListShadow(Shadow<List<T>> shadow) {
    String shadowStoreKey = getShadowStoreKey(shadow.getResource());
    shadowStore.putShadow(shadowStoreKey, shadow);
  }

  private void putBackupListShadow(Shadow<List<T>> shadow) {
    String shadowStoreKey = getShadowStoreKey(shadow.getResource()) + "_backup";
    shadowStore.putShadow(shadowStoreKey, shadow);
  }

  @SuppressWarnings("unchecked")
  private Shadow<List<T>> getShadow(List<T> target) {
    String shadowStoreKey = getShadowStoreKey(target);
    Shadow<List<T>> shadow = (Shadow<List<T>>) shadowStore.getShadow(shadowStoreKey);
    if (shadow == null) {
      shadow = new Shadow<List<T>>(DeepCloneUtils.deepClone(target), 0, 0); // OKAY
    }
    return shadow;
  }

  @SuppressWarnings("unchecked")
  private Shadow<List<T>> getBackupShadow(List<T> target) {
    String shadowStoreKey = getShadowStoreKey(target) + "_backup";
    Shadow<List<T>> shadow = (Shadow<List<T>>) shadowStore.getShadow(shadowStoreKey);
    if (shadow == null) {
      shadow = new Shadow<List<T>>(DeepCloneUtils.deepClone(target), 0, 0); // OKAY
    }
    return shadow;
  }

  private String getShadowStoreKey(T t) {
    return "shadow/" + entityType.getSimpleName();
  }
  
  private String getShadowStoreKey(List<T> t) {
    return "shadow/" + entityType.getSimpleName() + "List";
  }

}




Java Source Code List

org.springframework.sync.AddOperationTest.java
org.springframework.sync.AddOperation.java
org.springframework.sync.CopyOperationTest.java
org.springframework.sync.CopyOperation.java
org.springframework.sync.DiffTest.java
org.springframework.sync.Diff.java
org.springframework.sync.FromOperation.java
org.springframework.sync.InverseTest.java
org.springframework.sync.JsonPatchTest.java
org.springframework.sync.LateObjectEvaluator.java
org.springframework.sync.MoveOperationTest.java
org.springframework.sync.MoveOperation.java
org.springframework.sync.PatchException.java
org.springframework.sync.PatchOperation.java
org.springframework.sync.Patch.java
org.springframework.sync.PathToSpEL.java
org.springframework.sync.PathToSpelTest.java
org.springframework.sync.RemoveOperationTest.java
org.springframework.sync.RemoveOperation.java
org.springframework.sync.ReplaceOperationTest.java
org.springframework.sync.ReplaceOperation.java
org.springframework.sync.TestOperationTest.java
org.springframework.sync.TestOperation.java
org.springframework.sync.Todo.java
org.springframework.sync.diffsync.AbstractShadowStore.java
org.springframework.sync.diffsync.DiffSync.java
org.springframework.sync.diffsync.Equivalency.java
org.springframework.sync.diffsync.IdPropertyEquivalency.java
org.springframework.sync.diffsync.PersistenceCallbackRegistry.java
org.springframework.sync.diffsync.PersistenceCallback.java
org.springframework.sync.diffsync.PersistenceStrategy.java
org.springframework.sync.diffsync.ShadowStore.java
org.springframework.sync.diffsync.Shadow.java
org.springframework.sync.diffsync.VersionedPatch.java
org.springframework.sync.diffsync.config.DiffSyncConfigurerAdapter.java
org.springframework.sync.diffsync.config.DiffSyncConfigurer.java
org.springframework.sync.diffsync.config.DifferentialSynchronizationRegistrar.java
org.springframework.sync.diffsync.config.EnableDifferentialSynchronization.java
org.springframework.sync.diffsync.config.package-info.java
org.springframework.sync.diffsync.shadowstore.GemfireShadowStore.java
org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore.java
org.springframework.sync.diffsync.shadowstore.RedisShadowStore.java
org.springframework.sync.diffsync.shadowstore.package-info.java
org.springframework.sync.diffsync.web.DiffSyncController.java
org.springframework.sync.diffsync.web.JsonPatchHttpMessageConverter.java
org.springframework.sync.diffsync.web.package-info.java
org.springframework.sync.diffsync.package-info.java
org.springframework.sync.json.JsonLateObjectEvaluator.java
org.springframework.sync.json.JsonPatchPatchConverter.java
org.springframework.sync.json.PatchConverter.java
org.springframework.sync.json.package-info.java
org.springframework.sync.util.DeepCloneUtils.java
org.springframework.sync.package-info.java