com.gemstone.gemfire.internal.cache.GatewayEventImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal.cache;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.InternalGemFireError;
import com.gemstone.gemfire.SerializationException;
import com.gemstone.gemfire.cache.CacheEvent;
import com.gemstone.gemfire.cache.EntryEvent;
import com.gemstone.gemfire.cache.Operation;
import com.gemstone.gemfire.cache.util.GatewayEvent;
import com.gemstone.gemfire.cache.util.ObjectSizer;
import com.gemstone.gemfire.cache.wan.GatewayQueueEvent;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.DataSerializableFixedID;
import com.gemstone.gemfire.internal.cache.EntryEventImpl.NewValueImporter;
import com.gemstone.gemfire.internal.cache.lru.Sizeable;
import com.gemstone.gemfire.internal.cache.tier.sockets.CacheServerHelper;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.shared.Version;
/**
* Class GatewayEventImpl
represents an event sent between
* Gateway
*
* @author Barry Oglesby
*
* @since 4.2
*
*/
public final class GatewayEventImpl implements GatewayEvent, GatewayQueueEvent, DataSerializableFixedID, Conflatable, Sizeable, NewValueImporter {
private static final long serialVersionUID = -5690172020872255422L;
private static final Object TOKEN_UN_INITIALIZED = new Object();
private static final short VERSION = 0x10;
protected EnumListenerEvent _operation;
protected EntryEventImpl _entryEvent;
/**
* The action to be taken (e.g. AFTER_CREATE)
*/
protected int _action;
/**
* The number of parts for the Message
*
* @see com.gemstone.gemfire.internal.cache.tier.sockets.Message
*/
protected int _numberOfParts;
/**
* The identifier of this event
*/
protected EventID _id;
//protected String _id;
/**
* The Region
that was updated
*/
private LocalRegion _region;
/**
* The name of the region being affected by this event
*/
protected String _regionName;
/**
* The key being affected by this event
*/
private Object _key = TOKEN_UN_INITIALIZED;
/**
* The serialized new value for this event's key
*/
protected byte[] _value;
/**
* Whether the value is a serialized object or just a byte[]
*/
protected byte _valueIsObject;
/**
* The callback argument for this event
*/
protected GatewayEventCallbackArgument _callbackArgument;
// /*
// * The version timestamp
// */
// private long versionTimeStamp;
/**
* Whether this event is a possible duplicate
*/
protected boolean _possibleDuplicate;
/**
* The creation timestamp in ms
*/
protected long _creationTime;
/**
* For ParalledGatewaySender we need bucketId of the PartitionRegion on which
* the update operation was applied.
*/
private int bucketId;
private Long shadowKey = new Long(1);
public static final int CREATE_ACTION = 0;
public static final int UPDATE_ACTION = 1;
public static final int DESTROY_ACTION = 2;
// Used by gemfirexd to send bulk DML across wan
public static final int BULK_DML_OP_ACTION = 3;
/**
* Constructor. No-arg constructor for data serialization.
*
* @see DataSerializer
*/
public GatewayEventImpl() {
}
/**
* Constructor. Creates an initialized GatewayEventImpl
*
* @param operation
* The operation for this event (e.g. AFTER_CREATE)
* @param event
* The CacheEvent
on which this
* GatewayEventImpl
is based
*
* @throws IOException
*/
public GatewayEventImpl(EnumListenerEvent operation, CacheEvent event)
throws IOException {
this(operation, event, true);
}
public GatewayEventImpl(EnumListenerEvent operation, CacheEvent event,
boolean initialize, int bucketId) throws IOException {
this(operation, event, true);
this.bucketId = bucketId;
}
/**
* Constructor.
*
* @param operation
* The operation for this event (e.g. AFTER_CREATE)
* @param event
* The CacheEvent
on which this
* GatewayEventImpl
is based
* @param initialize
* Whether to initialize this instance
*
* @throws IOException
*/
public GatewayEventImpl(EnumListenerEvent operation, CacheEvent event,
boolean initialize) throws IOException {
// Set the operation and event
this._operation = operation;
EntryEventImpl entryEvent = (EntryEventImpl)event;
this._entryEvent = entryEvent; // this reference is discarded in initialize()
// Initialize the region name. This is being done here because the event
// can get serialized/deserialized (for some reason) between the time
// it is set above and used (in initialize). If this happens, the
// region is null because it is a transient field of the event.
this._region = this._entryEvent.getRegion();
this._regionName = this._region.getFullPath();
// Initialize the unique id
initializeId();
// Initialize possible duplicate
this._possibleDuplicate = false;
// Initialize the creation timestamp
this._creationTime = _region.cacheTimeMillis();
// if (GatewayBatchOp.VERSION_WITH_OLD_WAN) {
// if (this._entryEvent.getVersionTag() != null) {
// this.versionTimeStamp = this._entryEvent.getVersionTag().getVersionTimeStamp();
// }
// }
// Initialize the remainder of the event if necessary
if (initialize) {
initialize();
} else {
this._entryEvent.copyOffHeapToHeap();
}
}
/**
* Returns this event's action
*
* @return this event's action
*/
public int getAction()
{
return this._action;
}
/**
* Returns this event's operation
*
* @return this event's operation
*/
public Operation getOperation() {
Operation operation = null;
switch (this._action) {
case CREATE_ACTION:
operation = Operation.CREATE;
break;
case UPDATE_ACTION:
operation = Operation.UPDATE;
break;
case DESTROY_ACTION:
operation = Operation.DESTROY;
break;
case BULK_DML_OP_ACTION:
operation = Operation.BULK_DML_OP;
break;
}
return operation;
}
/**
* Return this event's region name
*
* @return this event's region name
*/
public String getRegionName()
{
return this._regionName;
}
/**
* Returns this event's key
*
* @return this event's key
*/
public Object getKey()
{
//TODO:Asif : Ideally would like to have throw exception if the key
//is TOKEN_UN_INITIALIZED, but for the time being trying to retain the GFE behaviour
// of returning null if getKey is invoked on un-initialized gateway event
return this._key==TOKEN_UN_INITIALIZED?null:this._key;
}
/**
* Returns this event's serialized value
*
* @return this event's serialized value
*/
public byte[] getValue()
{
return this._value;
}
/**
* Returns whether this event's value is a serialized object. A return value
* of 0 indicates that value is a raw byte[], that of 1 indicates an object
* and 2 indicates an internal delta object.
*
* @return whether this event's value is a serialized object
*/
public byte getValueIsObject() {
return this._valueIsObject;
}
/**
* Return this event's callback argument
*
* @return this event's callback argument
*/
public Object getCallbackArgument()
{
Object result = getGatewayCallbackArgument();
while (result instanceof WrappedCallbackArgument) {
WrappedCallbackArgument wca = (WrappedCallbackArgument)result;
result = wca.getOriginalCallbackArg();
}
return result;
}
public GatewayEventCallbackArgument getGatewayCallbackArgument() {
return this._callbackArgument;
}
/**
* Return this event's number of parts
*
* @return this event's number of parts
*/
public int getNumberOfParts()
{
return this._numberOfParts;
}
protected void initializeKey() {
if (this._key instanceof KeyWithRegionContext) {
final LocalRegion r = getRegion();
if (r == null) {
// log a warning
final LogWriterI18n logger = InternalDistributedSystem.getLoggerI18n();
if (logger != null && logger.warningEnabled()) {
logger.warning(LocalizedStrings.DEBUG,
"GatewayEventImpl.initializeKey: region "
+ this._regionName + " not found while initializing key "
+ this._key);
}
}
else if (r.keyRequiresRegionContext()) {
((KeyWithRegionContext)this._key).setRegionContext(r);
}
}
}
/**
* Return this event's deserialized value
*
* SUPPRESSES ANY DESERIALIZATION FAILURES.
*
* @return this event's deserialized value
*/
public Object getDeserializedValueForLogging()
{
Object deserializedObject = this._value;
// This is a debugging method so ignore all exceptions like
// ClassNotFoundException
try {
if(this._valueIsObject == 0x00){
deserializedObject = this._value;
} else {
deserializedObject = EntryEventImpl.deserialize(this._value);
}
}
catch (Exception e) {
}
return deserializedObject;
}
/**
* Return this event's deserialized value
*
* @return this event's deserialized value
*/
public Object getDeserializedValue()
{
if(this._valueIsObject == 0x00){
return this._value;
} else {
return EntryEventImpl.deserialize(this._value);
}
}
public byte[] getSerializedValue() {
return this._value;
}
public void setPossibleDuplicate(boolean possibleDuplicate) {
this._possibleDuplicate = possibleDuplicate;
}
public boolean getPossibleDuplicate() {
return this._possibleDuplicate;
}
public long getCreationTime() {
return this._creationTime;
}
public int getDSFID() {
return GATEWAY_EVENT_IMPL;
}
public void toData(DataOutput out) throws IOException
{
// Make sure we are initialized before we serialize.
if (this._key == TOKEN_UN_INITIALIZED) {
initialize();
}
out.writeShort(VERSION);
out.writeInt(this._action);
out.writeInt(this._numberOfParts);
//out.writeUTF(this._id);
DataSerializer.writeObject(this._id, out);
DataSerializer.writeString(this._regionName, out);
out.writeByte(this._valueIsObject);
DataSerializer.writeObject(this._key, out);
DataSerializer.writeByteArray(this._value, out);
DataSerializer.writeObject(this._callbackArgument, out);
out.writeBoolean(this._possibleDuplicate);
out.writeLong(this._creationTime);
out.writeInt(this.bucketId);
out.writeLong(this.shadowKey);
// if (GatewayBatchOp.VERSION_WITH_OLD_WAN) {
// out.writeLong(this.versionTimeStamp);
// }
}
public void fromData66(DataInput in) throws IOException, ClassNotFoundException
{
this._action = in.readInt();
this._numberOfParts = in.readInt();
//this._id = in.readUTF();
this._id = (EventID)DataSerializer.readObject(in);
//TODO:Asif ; Check if this violates Barry's logic of not assiging VM
// specific Token.FROM_GATEWAY
//and retain the serialized Token.FROM_GATEWAY
//this._id.setFromGateway(false);
this._regionName = DataSerializer.readString(in);
this._valueIsObject = in.readByte();
this._key = DataSerializer.readObject(in);
this._value = DataSerializer.readByteArray(in);
this._callbackArgument = (GatewayEventCallbackArgument)DataSerializer.readObject(in);
this._possibleDuplicate = in.readBoolean();
this._creationTime = in.readLong();
// skumar's comments:
// bucketId and shadowKey is not required for SerialGatewaySender. So,
// the any default values will work with it. We should provide
// documnetation for not using old disk files for ParallelGatewaySender.
// This cannot be supported.
this.bucketId = 1;
this.shadowKey = new Long(1);
// if (GatewayBatchOp.VERSION_WITH_OLD_WAN) {
// this.versionTimeStamp = in.readLong();
// }
}
public void fromData(DataInput in) throws IOException, ClassNotFoundException
{
short version = in.readShort();
if (version != VERSION) {
// warning?
}
this._action = in.readInt();
this._numberOfParts = in.readInt();
//this._id = in.readUTF();
this._id = (EventID)DataSerializer.readObject(in);
this._regionName = DataSerializer.readString(in);
this._valueIsObject = in.readByte();
this._key = DataSerializer.readObject(in);
this._value = DataSerializer.readByteArray(in);
this._callbackArgument = (GatewayEventCallbackArgument)DataSerializer.readObject(in);
this._possibleDuplicate = in.readBoolean();
this._creationTime = in.readLong();
this.bucketId = in.readInt();
this.shadowKey = in.readLong();
// if (GatewayBatchOp.VERSION_WITH_OLD_WAN) {
// this.versionTimeStamp = in.readLong();
// }
}
@Override
public String toString()
{
StringBuilder buffer = new StringBuilder();
buffer.append("GatewayEventImpl[")
.append("id=")
.append(this._id)
.append(";action=")
.append(this._action)
.append(";operation=")
.append(getOperation())
.append(";region=")
.append(this._regionName)
.append(";key=")
.append(this._key)
.append(";value=")
.append(getDeserializedValueForLogging())
.append(";valueIsObject=")
.append(this._valueIsObject)
.append(";numberOfParts=")
.append(this._numberOfParts)
.append(";callbackArgument=")
.append(this._callbackArgument)
.append(";possibleDuplicate=")
.append(this._possibleDuplicate)
.append(";creationTime=")
.append(this._creationTime)
.append(":bucketId=")
.append(this.bucketId)
.append(":shadowKey=")
.append(this.shadowKey);
// if (GatewayBatchOp.VERSION_WITH_OLD_WAN) {
// buffer.append(";versionStamp=")
// .append(this.versionTimeStamp);
// }
buffer.append("]");
return buffer.toString();
}
protected Object deserializeForLogging(byte[] serializedBytes)
{
Object deserializedObject = serializedBytes;
// This is a debugging method so ignore all exceptions like
// ClassNotFoundException
try {
if(this._valueIsObject == 0x00){
deserializedObject = serializedBytes;
} else {
deserializedObject = EntryEventImpl.deserialize(serializedBytes);
}
}
catch (Exception e) {
throw new InternalGemFireError(e);
}
return deserializedObject;
}
/// Conflatable interface methods ///
/**
* Determines whether or not to conflate this message. This method will answer
* true IFF the message's operation is AFTER_UPDATE and its region has enabled
* are conflation. Otherwise, this method will answer false. Messages whose
* operation is AFTER_CREATE, AFTER_DESTROY, AFTER_INVALIDATE or
* AFTER_REGION_DESTROY are not conflated.
*
* @return Whether to conflate this message
*/
public boolean shouldBeConflated()
{
// The event may be conflated if any of the following is true
// - the event is an update
// - the region is empty and the event's operation is a create
// Otherwise, the event is not conflatable.
return isUpdate()
|| (getRegion() != null
&& !getRegion().getAttributes().getDataPolicy().withStorage() && isCreate());
}
/**
* {@inheritDoc}
*/
public boolean shouldBeMerged() {
return false;
}
/**
* {@inheritDoc}
*/
public boolean merge(Conflatable existing) {
throw new AssertionError("not expected to be invoked");
}
public String getRegionToConflate()
{
return this._regionName;
}
public Object getKeyToConflate()
{
return this._key;
}
public Object getValueToConflate()
{
return this._value;
}
public void setLatestValue(Object value)
{
this._value = (byte[])value;
}
/// End Conflatable interface methods ///
/**
* Returns whether this GatewayEvent
represents an update.
* @return whether this GatewayEvent
represents an update
*/
protected boolean isUpdate()
{
// This event can be in one of three states:
// - in memory primary (initialized)
// - in memory secondary (not initialized)
// - evicted to disk, read back in (initialized)
// In the first case, both the operation and action are set.
// In the second case, only the operation is set.
// In the third case, only the action is set.
return this._operation == null
? this._action == UPDATE_ACTION
: this._operation == EnumListenerEvent.AFTER_UPDATE;
}
/**
* Returns whether this GatewayEvent
represents a create.
* @return whether this GatewayEvent
represents a create
*/
protected boolean isCreate()
{
//See the comment in isUpdate() for additional details
return this._operation == null
? this._action == CREATE_ACTION
: this._operation == EnumListenerEvent.AFTER_CREATE;
}
/**
* Returns whether this GatewayEvent
represents a destroy.
* @return whether this GatewayEvent
represents a destroy
*/
protected boolean isDestroy()
{
// See the comment in isUpdate() for additional details
return this._operation == null
? this._action == DESTROY_ACTION
: this._operation == EnumListenerEvent.AFTER_DESTROY;
}
/**
* Initialize the unique identifier for this event. This id is used by the
* receiving Gateway
to keep track of which events have been
* processed. Duplicates can be dropped.
*/
private void initializeId()
{
//CS43_HA
this._id = this._entryEvent.getEventId();
//TODO:ASIF :Once stabilized remove the check below
if (this._id == null) {
throw new IllegalStateException(LocalizedStrings.GatewayEventImpl_NO_EVENT_ID_IS_AVAILABLE_FOR_THIS_GATEWAY_EVENT.toLocalizedString());
}
}
/**
* Initialize this instance. Get the useful parts of the input operation and
* event.
*
* @throws IOException
*/
protected void initialize() throws IOException
{
// Set key
//System.out.println("this._entryEvent: " + this._entryEvent);
//System.out.println("this._entryEvent.getKey(): " +
// this._entryEvent.getKey());
if (this._key != TOKEN_UN_INITIALIZED) {
// We have already initialized, or initialized elsewhere. Lets return.
return;
}
this._key = this._entryEvent.getKey();
// Set the value to be a byte[] representation of the value. If the value
// is already serialized, use it.
this._valueIsObject = 0x01;
if (this._entryEvent.hasDelta()) {
this._valueIsObject = 0x02;
}
this._entryEvent.exportNewValue(this);
// Set the callback arg
this._callbackArgument = (GatewayEventCallbackArgument)this._entryEvent.getRawCallbackArgument();
// Initialize the action and number of parts (called after _callbackArgument
// is set above)
initializeAction(this._operation);
// The entry event is no longer necessary. Null it so it can be GCed.
this._entryEvent = null;
// any initialization required for key
initializeKey();
}
/**
* Initialize this event's action and number of parts
*
* @param operation
* The operation from which to initialize this event's action and
* number of parts
*/
protected void initializeAction(EnumListenerEvent operation)
{
if (operation == EnumListenerEvent.AFTER_CREATE) {
// Initialize after create action
this._action = CREATE_ACTION;
// Initialize number of parts
// part 1 = action
// part 2 = posDup flag
// part 3 = regionName
// part 4 = eventId
// part 5 = key
// part 6 = value (create and update only)
// part 7 = whether callbackArgument is non-null
// part 8 = callbackArgument (if non-null)
this._numberOfParts = (this._callbackArgument == null) ? 7 : 8;
}
else if (operation == EnumListenerEvent.AFTER_UPDATE) {
// Initialize after update action
this._action = UPDATE_ACTION;
// Initialize number of parts
this._numberOfParts = (this._callbackArgument == null) ? 7 : 8;
}
else if (operation == EnumListenerEvent.AFTER_DESTROY) {
// Initialize after destroy action
this._action = DESTROY_ACTION;
// Initialize number of parts
// Since there is no value, there is one less part
this._numberOfParts = (this._callbackArgument == null) ? 6 : 7;
}else if (operation == EnumListenerEvent.AFTER_BULK_DML_OP) {
this._numberOfParts = 3;
this._action = BULK_DML_OP_ACTION;
}
}
public EventID getEventId()
{
return this._id;
}
public int getSizeInBytes() {
// Calculate the size of this event. This is used for overflow to disk.
// The sizes of the following variables are calculated:
//
// - the value (byte[])
// - the original callback argument (Object)
// - primitive and object instance variable references
//
// The sizes of the following variables are not calculated:
// - the key because it is a reference
// - the region and regionName because they are references
// - the operation because it is a reference
// - the entry event because it is nulled prior to calling this method
// The size of instances of the following internal datatypes were estimated
// using a NullDataOutputStream and hardcoded into this method:
// - the id (an instance of EventId)
// - the callbackArgument (an instance of GatewayEventCallbackArgument)
int size = 0;
// Add this event overhead
size += Sizeable.PER_OBJECT_OVERHEAD;
// Add object references
//_id reference = 4 bytes
//_region reference = 4 bytes
//_regionName reference = 4 bytes
//_key reference = 4 bytes
//_callbackArgument reference = 4 bytes
//_operation reference = 4 bytes
//_entryEvent reference = 4 bytes
size += 28;
// Add primitive references
// int _action = 4 bytes
// int _numberOfParts = 4 bytes
// byte _valueIsObject = 1 byte
// boolean _possibleDuplicate = 1 byte
size += 10;
// Add the id (an instance of EventId)
// The hardcoded value below was estimated using a NullDataOutputStream
size += Sizeable.PER_OBJECT_OVERHEAD + 56;
// The value (a byte[])
if (this._value != null) {
size += CachedDeserializableFactory.calcMemSize(this._value);
}
// The callback argument (a GatewayEventCallbackArgument wrapping an Object
// which is the original callback argument)
// The hardcoded value below represents the GatewayEventCallbackArgument
// and was estimated using a NullDataOutputStream
size += Sizeable.PER_OBJECT_OVERHEAD + 194;
// The sizeOf call gets the size of the input callback argument.
size += Sizeable.PER_OBJECT_OVERHEAD + sizeOf(getCallbackArgument());
// the version timestamp
size += 8;
return size;
}
private int sizeOf(Object obj) {
int size = 0;
if (obj == null) {
return size;
}
if (obj instanceof String) {
size = ObjectSizer.DEFAULT.sizeof(obj);
} else if (obj instanceof Integer) {
size = 4; //estimate
} else if (obj instanceof Long) {
size = 8; //estimate
} else {
size = CachedDeserializableFactory.calcMemSize(obj) - Sizeable.PER_OBJECT_OVERHEAD;
}
return size;
}
public EntryEvent