/* * Copyright 2012-2015, the original author or authors. * * 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 com.flipkart.aesop.serializer.stateengine; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.avro.generic.GenericRecord; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.trpr.platform.core.impl.logging.LogFactory; import org.trpr.platform.core.spi.logging.Logger; import com.flipkart.aesop.runtime.producer.ReadEventCycleSummary; import com.netflix.zeno.diff.DiffSerializationFramework; import com.netflix.zeno.diff.TypeDiff; import com.netflix.zeno.diff.TypeDiffInstruction; import com.netflix.zeno.diff.TypeDiffOperation; import com.netflix.zeno.fastblob.FastBlobStateEngine; import com.netflix.zeno.fastblob.state.TypeDeserializationStateListener; import com.netflix.zeno.serializer.SerializerFactory; /** * The <code>DiffInterpreter</code> loads serialized data snapshots and deltas onto a {@link FastBlobStateEngine} and provides methods to listen-in on the * change that the state engine goes through. * * @author Regunath B * @version 1.0, 17 March 2014 */ public abstract class DiffInterpreter<T, S extends GenericRecord> implements InitializingBean { /** The Logger interface*/ private static final Logger LOGGER = LogFactory.getLogger(DiffInterpreter.class); /** The StateTransitioner instance */ protected SerializerFactory serializerFactory; /** The file location for reading snapshots and deltas */ protected String serializedDataLocation; /** Directory location derived from serialized data location */ protected File serializedDataLocationDir; /** The DiffChangeEventMapper instance for mapping state engine changes to change events*/ protected DiffChangeEventMapper<T,S> diffChangeEventMapper; /** The FastBlobStateEngine that this DiffInterpreter creates and manages */ private FastBlobStateEngine stateEngine; /** * Interface method implementation. Checks for mandatory dependencies * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { Assert.notNull(this.serializerFactory,"'serializerFactory' cannot be null. This diff interpreter will not be initialized"); Assert.notNull(this.serializedDataLocation,"'serializedDataLocation' cannot be null. This diff interpreter will not be initialized"); Assert.notNull(this.diffChangeEventMapper,"'diffChangeEventMapper' cannot be null. This diff interpreter will not be initialized"); this.stateEngine = new FastBlobStateEngine(this.serializerFactory); this.serializedDataLocationDir = new File(this.serializedDataLocation); } /** * Gets change events that occurred after the specified SCN * @param sinceSCN the last seen SCN * @return ReadEventCycleSummary containing change events and the new SCN */ public ReadEventCycleSummary<S> getChangeEvents(long sinceSCN) { if (this.stateEngine.getLatestVersion() == null || Long.valueOf(this.stateEngine.getLatestVersion()) < sinceSCN) { this.readSnapshotAndDeltasForSCN(this.stateEngine, sinceSCN); // bring up the state engine to the SCN } DiffTypeDeserializationStateListener diffTypeDeserializationStateListener = new DiffTypeDeserializationStateListener(); this.stateEngine.setTypeDeserializationStateListener(this.diffChangeEventMapper.getNFTypeName(),diffTypeDeserializationStateListener); this.readSnapshotAndDeltasAfterSCN(this.stateEngine, sinceSCN); DiffSerializationFramework diffSerializationFramework = new DiffSerializationFramework(this.serializerFactory); TypeDiffInstruction<T> diffInstruction = new TypeDiffInstruction<T>() { public String getSerializerName() { return diffChangeEventMapper.getNFTypeName(); } public Object getKey(T object) { return diffChangeEventMapper.getTypeKey(object); } }; TypeDiffOperation<T> diffOperation = new TypeDiffOperation<T>(diffInstruction); LOGGER.debug("Sizes of removed and added objects post diff : " + diffTypeDeserializationStateListener.getRemovedObjectsList().size() + "," + diffTypeDeserializationStateListener.getAddedObjectsList().size()); TypeDiff<T> typeDiff = diffOperation.performDiff(diffSerializationFramework, diffTypeDeserializationStateListener.getRemovedObjectsList(), diffTypeDeserializationStateListener.getAddedObjectsList()); return new ReadEventCycleSummary<S>(this.diffChangeEventMapper.getChangeEvents(typeDiff, this.serializerFactory), (this.stateEngine.getLatestVersion() == null ? 0L : Long.valueOf(this.stateEngine.getLatestVersion()))); } /** * Reads snapshots and deltas that bring up the stateEngine version to the specified SCN * @param stateEngine the FastBlobStateEngine that is to be initialized with data reflecting the specified SCN * @param sinceSCN the last seen SCN */ protected abstract void readSnapshotAndDeltasForSCN(FastBlobStateEngine stateEngine, long sinceSCN); /** * Appends snapshots and deltas to the stateEngine for state changes that occurred after specified SCN * @param stateEngine the FastBlobStateEngine to append snapshots and deltas to * @param sinceSCN the last seen SCN */ protected abstract void readSnapshotAndDeltasAfterSCN(FastBlobStateEngine stateEngine, long sinceSCN); /** Getter/Setter methods */ public void setSerializerFactory(SerializerFactory serializerFactory) { this.serializerFactory = serializerFactory; } public void setSerializedDataLocation(String serializedDataLocation) { this.serializedDataLocation = serializedDataLocation; } public String getSerializedDataLocation() { return serializedDataLocation; } public void setDiffChangeEventMapper(DiffChangeEventMapper<T, S> diffChangeEventMapper) { this.diffChangeEventMapper = diffChangeEventMapper; } public DiffChangeEventMapper<T, S> getDiffChangeEventMapper() { return diffChangeEventMapper; } /** * Listens into the Type deserialization state as snapshots and deltas are loaded on to the state engine. Keeps tracks of state changes * that is then used to create change events */ private class DiffTypeDeserializationStateListener extends TypeDeserializationStateListener<T> { /** Collections holding added and removed objects during de-serialization*/ private List<T> addedObjectsList = new ArrayList<T>(); private List<T> removedObjectsList = new ArrayList<T>(); /** * Interface call-back method implementation. Adds the object to the added objects collection * @see com.netflix.zeno.fastblob.state.TypeDeserializationStateListener#addedObject(java.lang.Object, int) */ public void addedObject(T object, int ordinal) { this.addedObjectsList.add(object); } /** * Interface call-back method implementation. Adds the object to the removed objects collection * @see com.netflix.zeno.fastblob.state.TypeDeserializationStateListener#removedObject(java.lang.Object, int) */ public void removedObject(T object, int ordinal) { this.removedObjectsList.add(object); } /** * Interface call-back method implementation. Does nothing * @see com.netflix.zeno.fastblob.state.TypeDeserializationStateListener#reassignedObject(java.lang.Object, int, int) */ public void reassignedObject(T object, int oldOrdinal, int newOrdinal) { // no op } /** Getter methods for the added, removed object collections*/ public List<T> getAddedObjectsList() { return addedObjectsList; } public List<T> getRemovedObjectsList() { return removedObjectsList; } } }