Иногда у нас есть большой объект, возможно, с множеством атрибутов или с некоторыми двоичными данными, и мы хотели бы сказать Infinispan, что он должен реплицировать только определенную часть объекта в кластере. Как правило, мы хотим воспроизвести только ту часть, которую мы только что обновили. Именно здесь вступают в игру интерфейсы DeltaAware и Delta. Предоставляя реализации этих интерфейсов, мы можем определить точную репликацию. Когда мы прилагаем некоторые усилия для такого улучшения, мы также хотели бы ускорить маршалинг объектов и демаршаллинг. Поэтому мы собираемся определить наши собственные экстернализаторы — чтобы избежать медленной сериализации Java по умолчанию.
  
Следующие фрагменты кода собраны в полный пример по адресу
  https://github.com/mgencur/infinispan-examples/tree/master/partial-state-transfer.Этот проект содержит файл readme с инструкциями о том, как создать и запустить пример. Он основан на быстром запуске кластерного кэша в Infinispan.
 
Реализация интерфейса DeltaAware
Итак, давайте посмотрим на наш основной объект. Для этого упражнения я определил класс Bicycle, который состоит из множества компонентов, таких как frame, fork, tailShock и т. Д. Этот объект хранится в кэше как значение под определенным (не важным) ключом. В нашем сценарии может случиться так, что мы обновляем только определенные компоненты велосипеда, и в этом случае мы хотим повторить только эти изменения компонентов.
  
Здесь важны следующие методы (описание взято из javadocs):
  
commit () — указывает, что все собранные на сегодняшний день дельты были извлечены (посредством
  
                  вызова delta ()) и могут быть отброшены. Часто используется в качестве оптимизации, если
  
                  дельта не нужна, но 
                  желательна очистка и сброс       
  внутреннего состояния.
 
delta () — извлекает изменения, внесенные в реализации, в эффективном формате, который
  
              можно легко и дешево сериализовать и десериализовать. Этот метод будет
  
              вызываться только один раз для каждого набора изменений, поскольку предполагается, что 
              внутренний журнал изменений любой
  реализации стирается и сбрасывается после генерации
  
              и отправки дельты вызывающей стороне.
  
          
Мы также должны определить сеттеры и геттеры для наших членов. Методы установки, помимо прочего, отвечают за регистрацию изменений в журнале изменений, который впоследствии будет использоваться для восстановления состояния объекта. Externalizer для этого класса необходим только при использовании хранилищ кэша. Ради простоты я не упоминаю об этом здесь.
 
public class Bicycle implements DeltaAware, Cloneable {
    //bicycle attributes
    String frame, fork, rearShock, crank, bottomBracket, shifters, cogSet, chain,
           frontDerailleur, rearDerailleur, rims, hubs, tires, pedals, brake, handlebar, stem;
    private BicycleDelta delta;
    @Override
    public void commit() {
        delta = null;
    }
    @Override
    public Delta delta() {
        Delta toReturn = getDelta();
        delta = null; // reset
        return toReturn;
    }
    
    ...
    
    public void setFrame(String frame) {
        getDelta().registerComponentChange("frame", frame);
        this.frame = frame;
    }
    
    public String getFrame() {
        return frame;
    }
    
    ...
}
Реализация интерфейса Delta
Фактический объект, который будет реплицирован по всему кластеру, является реализацией интерфейса Delta. Давайте посмотрим на класс. Во-первых, нам нужно поле, которое будет содержать изменения — changeLog. Во-вторых, нам нужно определить метод merge (). Этот метод должен быть реализован так, чтобы Infinispan знал, как объединить существующий объект с входящими изменениями. Параметр этого метода представляет объект, который уже хранится в кэше, входящие изменения будут применены к этому объекту. Мы используем отражение здесь, чтобы применить изменения к реальному объекту, но это не обязательно. Мы могли бы легко вызвать методы установки. Преимущество использования отражения в том, что мы можем установить эти поля в цикле.
 
Другой частью является метод registerComponentChange (). Это вызывается объектом класса Bicycle — для записи изменений этого объекта. Название этого метода не важно.
 
Определение нашего собственного экстерьера
Хорошо, так что остаётся определение экстерьера для реализации Delta. Мы реализуем интерфейс AdvancedExternalizer и говорим, что только объект changeLog должен быть маршалирован и демаршаллирован при передаче данных по проводам. Полная (почти) реализация интерфейса Delta заключается в следующем.
public class BicycleDelta implements Delta {
    private HashMap<String, String> changeLog = new HashMap<String, String>();
    
    @Override
    public DeltaAware merge(DeltaAware d) {
        Bicycle other;
        if (d != null && (d instanceof Bicycle)) {
            other = (Bicycle) d;
        }
        else {
            other = new Bicycle();
        }
        Class<?> hugeClass = other.getClass();
        try {
            if (changeLog != null) {
                for (Entry<String, String> o : changeLog.entrySet()) {
                    Field x = hugeClass.getDeclaredField(o.getKey());
                    if (!x.isAccessible()) x.setAccessible(true);
                    x.set(other, o.getValue());
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return other;
    }
    
    public void registerComponentChange(String compName, String compDescription) {
        changeLog.put(compName, compDescription);
    }
    
    ...
    
    public static class Externalizer implement AdvancedExternalizer<BicycleDelta> {
        @Override
        public Set<Class<? extends BicycleDelta>> getTypeClasses() {
            return Util.<Class<? extends BicycleDelta>>asSet(BicycleDelta.class);
        }
        @Override
        public void writeObject(ObjectOutput output, BicycleDelta object) throws IOException {
            output.writeObject(object.changeLog);
        }
        @Override
        public BicycleDelta readObject(ObjectInput input) throws IOException, ClassNotFoundException {
            BicycleDelta delta = new BicycleDelta();
            delta.changeLog = (HashMap<String, String>) input.readObject();
            return delta;
        }
        
        @Override
        public Integer getId() {
           return 33; //put some random value here to identify the externalizer (must not be in reserved value ranges)
        }
    }
}
Расскажите Infinispan о дополнительном экстерьере
Нам также необходимо настроить Infinispan для использования нашего специального экстерьера для маршалинга / демаршаллинга наших объектов. Мы можем сделать это, например, программно, вызвав .addAdvancedExternalizer () в построителе конфигурации сериализации.
 
new DefaultCacheManager( GlobalConfigurationBuilder.defaultClusteredBuilder() .serialization() .addAdvancedExternalizer(new BicycleDelta.Externalizer()) .build(), new ConfigurationBuilder() .clustering() .cacheMode(CacheMode.REPL_SYNC) .transaction() .transactionMode(TransactionMode.TRANSACTIONAL) .autoCommit(true) .lockingMode(LockingMode.OPTIMISTIC) .transactionManagerLookup(new JBossStandaloneJTAManagerLookup()) .locking() .isolationLevel(IsolationLevel.READ_COMMITTED) .build() );
Вы можете видеть, что мы также настраиваем транзакции здесь. Это не обязательно, однако. Мы просто стремимся предоставить более богатый пример, исключить транзакционное поведение очень легко.
И тут начинается часть «использования». Заключите вызовы кеша транзакцией, извлеките объект велосипеда из кеша, внесите некоторые изменения и зафиксируйте их. 
try {
tm.begin();
//retrieve the bicycle from the cache, we suppose the bicycle already exists and
//only trying to apply some changes to it, only these changes will be replicated
Bicycle toChange = cache.get(BIKE1);
System.out.println("Updating components: frame, fork");
toChange.setFrame("New Frame");
toChange.setFork("New Fork");
//store the bicycle back to the cache
cache.put(BIKE1, toChange);
tm.commit();
} catch (Exception e) {
try {
if (tm != null) {
tm.rollback();
}
} catch (Exception ex) {
}
}
Вот и все. То, что в итоге передается по проводам, это просто объект changeLog. Фактический объект велосипеда реконструирован из поступающих обновлений.
 
Если все это кажется вам слишком сложным, у меня есть хорошие новости. Infinispan предоставляет одну реализацию интерфейса DeltaAware, которая называется AtomicHashMap (пакет org.infinispan.atomic). Если эта карта используется в качестве значения в парах ключ / значение, хранящихся в кэше, то только операции добавления / получения / удаления этой карты во время транзакции копируются на другие узлы. Такие классы, как Bicycle и BicycleDelta, тогда не нужны. Даже регистрация внешнего устройства для AtomicHashMap не требуется, это делается автоматически при регистрации внутренних внешних устройств. Однако может потребоваться класс, имитирующий объект реального мира, а не просто карту. Это тот случай, когда ваши собственные реализации интерфейсов DeltaAware и Delta являются единственным способом.