com.gemstone.gemfire.internal.cache.EventID Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* 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.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.InternalGemFireException;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.DistributedSystem;
import com.gemstone.gemfire.distributed.internal.DistributionManager;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.DataSerializableFixedID;
import com.gemstone.gemfire.internal.HeapDataOutputStream;
import com.gemstone.gemfire.internal.cache.ha.HARegionQueue;
import com.gemstone.gemfire.internal.cache.ha.ThreadIdentifier;
import com.gemstone.gemfire.internal.cache.tier.sockets.ClientProxyMembershipID;
import com.gemstone.gemfire.internal.concurrent.AL;
import com.gemstone.gemfire.internal.concurrent.CFactory;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.shared.Version;
import com.gemstone.gemfire.internal.util.Breadcrumbs;
/**
* This class uniquely identifies any Region Operation like create, update
* destroy etc. It is composed of three parts , namely :- 1)
* DistributedMembershipID 2) ThreadID 3) SequenceID This helps in sequencing
* the events belonging to a unique producer.
*
* @author Asif
*
*/
public final class EventID
implements DataSerializableFixedID, Serializable, Externalizable
{
/** turns on very verbose logging ove membership id bytes */
private static boolean LOG_ID_BYTES = Boolean.getBoolean("gemfire.log-event-member-id-bytes");
/**
* Uniquely identifies the distributed member VM in which the Event is
* produced
*/
private byte[] membershipID;
/**
* Unqiuely identifies the thread producing the event
*/
private long threadID;
/**
* Uniquely identifies individual event produced by a given thread
*/
private long sequenceID;
private int bucketID;
private byte breadcrumbCounter = 0x0;
public void incBreadcrumbCounter() {
this.breadcrumbCounter++;
}
/** The versions in which this message was modified */
private static final Version[] dsfidVersions = new Version[] {
Version.GFXD_14 };
private static ThreadLocal threadIDLocal = new ThreadLocal() {
@Override
protected Object initialValue()
{
return new ThreadAndSequenceIDWrapper();
}
};
private transient int hashCode = 0;
/**
* the distributed system associated with the static client_side_event_identity
*/
private volatile static DistributedSystem system = null;
/**
* the membership id of the distributed system in this client (if running in a
* client) that is reflected in client_side_event_identity
*/
private static DistributedMember systemMemberId;
/**
* this form of client ID is used in event identifiers to reduce the size
* of the ID
*/
private volatile static byte[] client_side_event_identity = null;
/**
* An array containing the helper class objects which are used to create
* optimized byte array for an eventID , which can be sent on the network
*/
static AbstractEventIDByteArrayFiller[] fillerArray = new AbstractEventIDByteArrayFiller[] {
new ByteEventIDByteArrayFiller(), new ShortEventIDByteArrayFiller(),
new IntegerEventIDByteArrayFiller(), new LongEventIDByteArrayFiller() };
/**
* Constructor used for creating EventID object at the actual source of
* creation of Event. The thread identification & sequence ID of the event is
* done using ThreadLocal object. The membershipId must be created with
* EventID.getMembershipId() and not the methods or byte array stored in
* a ClientProxyMembershipID as those are heavyweight identifiers and
* they will cause serialization and comparison problems when used in EventIDs
*/
private EventID(final byte[] membershipId) {
// Assert.assertTrue(membershipId.length <= Short.MAX_VALUE);
this.membershipID = membershipId;
// TODO:Asif : If the DS is closed & restarted can we continue with the
// existing Thread ID & Sequenec ID. Should not be an issue.
// But we should not cache membershipID as for the same thread it can
// differ.Hence it should be passed as parameter in the constructor
ThreadAndSequenceIDWrapper wrapper = (ThreadAndSequenceIDWrapper)threadIDLocal
.get();
this.threadID = wrapper.threadID;
this.sequenceID = wrapper.getAndIncrementSequenceID();
this.bucketID = -1;
}
/**
* constructor for creating an event ID originating in the local cache
* @param sys the local distributed system
*/
public EventID(DistributedSystem sys) {
this(initializeAndGetDSEventIdentity(sys));
// LogWriterI18n logger = InternalDistributedSystem.getLoggerI18n();
// if (logger != null) {
// logger.fine("creating new event ID " + threadID + ", " + sequenceID, new Exception("stack trace"));
// }
}
public static byte[] getMembershipId(DistributedSystem sys) {
return EventID.initializeAndGetDSEventIdentity(sys);
}
public static void unsetDS() {
system = null;
}
/**
* Convert a ClientProxyMembershipID distribted member ID array into one
* usable by EventIDs
* @param client the client's ID
* @return a byte array that may be used in EventID formation
*/
public static byte[] getMembershipId(ClientProxyMembershipID client) {
try {
HeapDataOutputStream hdos = new HeapDataOutputStream(256, Version.CURRENT);
((InternalDistributedMember)client.getDistributedMember()).writeEssentialData(hdos);
return hdos.toByteArray();
}
catch (IOException ioe) {
throw new InternalGemFireException(LocalizedStrings.ClientProxyMembershipID_UNABLE_TO_SERIALIZE_IDENTITY.toLocalizedString(), ioe);
}
}
static ThreadAndSequenceIDWrapper getWrapper() {
return (ThreadAndSequenceIDWrapper)threadIDLocal.get();
}
/**
* Returns the thread id used by the calling thread for its event ids
* @since 5.7
*/
public static long getThreadId() {
ThreadAndSequenceIDWrapper wrapper = (ThreadAndSequenceIDWrapper)threadIDLocal.get();
return wrapper.threadID;
}
/**
* Returns the next reservable sequence id used by the calling thread for its event ids.
* Note that the returned id is not yet reserved by the calling thread.
* @since 5.7
*/
public static long getSequenceId() {
ThreadAndSequenceIDWrapper wrapper = (ThreadAndSequenceIDWrapper)threadIDLocal.get();
return wrapper.sequenceID;
}
/**
* Reserves and returns a sequence id for the calling thread to be used
* for an event id.
* @since 5.7
*/
public static long reserveSequenceId() {
ThreadAndSequenceIDWrapper wrapper = (ThreadAndSequenceIDWrapper)threadIDLocal.get();
return wrapper.getAndIncrementSequenceID();
}
public void reserveSequenceId(int count) {
ThreadAndSequenceIDWrapper wrapper = (ThreadAndSequenceIDWrapper)threadIDLocal.get();
wrapper.reserveSequenceID(count);
}
/**
* Constructor used for creating an EventID with specified sequenceID for putAll
*/
public EventID(EventID eventId, int offset) {
assert (eventId != null);
this.membershipID = eventId.getMembershipID();
this.threadID = eventId.getThreadID();
this.sequenceID = eventId.getSequenceID() + offset;
this.bucketID = -1;
}
/**
* Constructor which explicitly sets all the fields and by-passes
* auto-generation of thread and seq. ids. This is used by the QRM thread
* and by ServerConnection threads that have cached their client ID
* byte arrays. It is also used by many unit test methods to create
* fake event identifiers.
*
* @param memId -
* membership id for this entry - must be created by EventID.getMembershipID()
* @param threadId -
* thread id for this entry
* @param seqId -
* sequence id for this entry
*/
public EventID(byte[] memId, long threadId, long seqId) {
this.membershipID = memId;
this.threadID = threadId;
this.sequenceID = seqId;
this.bucketID = -1;
}
public EventID(byte[] memId, long threadId, long seqId, int bucketId) {
this.membershipID = memId;
this.threadID = threadId;
this.sequenceID = seqId;
this.bucketID = bucketId;
}
/** support for migrating across threads for Hydra */
public static Object getThreadLocalDataForHydra() {
Object result = threadIDLocal.get();
threadIDLocal.set(null);
return result;
}
/** support for migrating across threads for Hydra */
public static void setThreadLocalDataForHydra(Object wrapper) {
if ( ! (wrapper instanceof ThreadAndSequenceIDWrapper) ) {
throw new IllegalArgumentException("Expected a ThreadAndSequenceIdWrapper but received " + wrapper);
}
threadIDLocal.set(wrapper);
}
/**
* Default constructor used for deserialization of EventID object
*
*/
public EventID() {
}
public long getThreadID()
{
return this.threadID;
}
public void setThreadID(long threadID) {
this.threadID = threadID;
}
public byte[] getMembershipID()
{
return this.membershipID;
}
public int getBucketID() {
return this.bucketID;
}
/**
* starting in v6.5 this method returns a somewhat crippled Identifier.
* It is missing any durable attributes and roles information but contains
* all other info about the member. This fixes bug #39361.
* @return the member that initiated this event
*/
public InternalDistributedMember getDistributedMember() {
ByteArrayInputStream bais = new ByteArrayInputStream(this.membershipID);
DataInputStream dis = new DataInputStream(bais);
InternalDistributedMember result = null;
try {
result = InternalDistributedMember.readEssentialData(dis);
} catch (IOException e) {
// nothing can be done about this
} catch (ClassNotFoundException e) {
// ditto
}
return result;
}
public long getSequenceID()
{
return this.sequenceID;
}
/**
* Returns a byte[] whose contents are calculated by
* calling {@link #getOptimizedByteArrayForEventID}
* @since 5.7
*/
public byte[] calcBytes() {
return getOptimizedByteArrayForEventID(getThreadID(), getSequenceID());
}
public int getDSFID() {
return EVENT_ID;
}
public void toData(DataOutput dop) throws IOException
{
DataSerializer.writeByteArray(this.membershipID, dop);
DataSerializer.writeByteArray(getOptimizedByteArrayForEventID(this.threadID, this.sequenceID),dop);
dop.writeInt(this.bucketID);
dop.writeByte(this.breadcrumbCounter);
}
public void toDataPre_GFXD_1_4_0_0(DataOutput dop) throws IOException{
DataSerializer.writeByteArray(this.membershipID, dop);
DataSerializer.writeByteArray(getOptimizedByteArrayForEventID(this.threadID, this.sequenceID),dop);
dop.writeInt(this.bucketID);
}
public void fromData(DataInput di) throws IOException, ClassNotFoundException
{
this.membershipID = DataSerializer.readByteArray(di);
ByteBuffer eventIdParts = ByteBuffer.wrap(DataSerializer.readByteArray(di));
this.threadID = readEventIdPartsFromOptmizedByteArray(eventIdParts);
this.sequenceID = readEventIdPartsFromOptmizedByteArray(eventIdParts);
this.bucketID = di.readInt();
this.breadcrumbCounter = di.readByte();
}
public void fromDataPre_GFXD_1_4_0_0(DataInput di) throws IOException, ClassNotFoundException
{
this.membershipID = DataSerializer.readByteArray(di);
ByteBuffer eventIdParts = ByteBuffer.wrap(DataSerializer.readByteArray(di));
this.threadID = readEventIdPartsFromOptmizedByteArray(eventIdParts);
this.sequenceID = readEventIdPartsFromOptmizedByteArray(eventIdParts);
this.bucketID = di.readInt();
}
public void writeExternal(ObjectOutput out) throws IOException
{
DataSerializer.writeByteArray(this.membershipID, out);
DataSerializer.writeByteArray(getOptimizedByteArrayForEventID(this.threadID, this.sequenceID),out);
out.writeInt(this.bucketID);
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException
{
this.membershipID = DataSerializer.readByteArray(in);
ByteBuffer eventIdParts = ByteBuffer.wrap(DataSerializer.readByteArray(in));
this.threadID = readEventIdPartsFromOptmizedByteArray(eventIdParts);
this.sequenceID = readEventIdPartsFromOptmizedByteArray(eventIdParts);
this.bucketID = in.readInt();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EventID other = (EventID) obj;
if (sequenceID != other.sequenceID)
return false;
if (threadID != other.threadID)
return false;
if (!Arrays.equals(membershipID, other.membershipID))
return false;
return true;
}
@Override
public int hashCode() {
if(hashCode == 0) {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(membershipID);
result = prime * result + (int) (sequenceID ^ (sequenceID >>> 32));
result = prime * result + (int) (threadID ^ (threadID >>> 32));
hashCode = result;
}
return hashCode;
}
String getShortClassName() {
String cname = getClass().getName();
return cname.substring(getClass().getPackage().getName().length()+1);
}
public String expensiveToString() {
Object mbr;
try {
mbr = InternalDistributedMember.readEssentialData(
new DataInputStream(new ByteArrayInputStream(membershipID)));
}
catch (Exception e) {
mbr = membershipID; // punt and use the bytes
}
return "EventID[" + mbr + ";threadID=" + ThreadIdentifier.toDisplayString(threadID) + ";sequenceID=" + sequenceID
+ (Breadcrumbs.ENABLED? ";bcrumb=" + breadcrumbCounter : "")
+ (bucketID>=0? (";bucketID=" + bucketID) : "")
+ "]";
}
@Override
public String toString() {
if (DistributionManager.VERBOSE || BridgeServerImpl.VERBOSE) {
return expensiveToString();
} else {
return cheapToString();
}
}
public String cheapToString() {
final StringBuffer buf = new StringBuffer();
buf.append(getShortClassName());
if (LOG_ID_BYTES) {
buf.append("[membershipID=");
for (int i=0; i= 0) {
buf.append(";bucketId=");
buf.append(this.bucketID);
}
buf.append("]");
return buf.toString();
}
private static byte[] initializeAndGetDSEventIdentity(DistributedSystem sys)
{
if (sys == null) {
// DistributedSystem is required now before handshaking -Kirk
throw new IllegalStateException(LocalizedStrings.ClientProxyMembershipID_ATTEMPTING_TO_HANDSHAKE_WITH_CACHESERVER_BEFORE_CREATING_DISTRIBUTEDSYSTEM_AND_CACHE.toLocalizedString());
}
if (EventID.system != sys) {
// DS already exists... make sure it's for current DS connection
EventID.systemMemberId = sys.getDistributedMember();
try {
HeapDataOutputStream hdos = new HeapDataOutputStream(256, Version.CURRENT);
((InternalDistributedMember)EventID.systemMemberId).writeEssentialData(hdos);
client_side_event_identity = hdos.toByteArray();
}
catch (IOException ioe) {
throw new InternalGemFireException(LocalizedStrings.ClientProxyMembershipID_UNABLE_TO_SERIALIZE_IDENTITY.toLocalizedString(), ioe);
}
EventID.system = sys;
}
return EventID.client_side_event_identity;
}
/**
* Returns the number of bytes needed to store the given value. This
* calculation is needed to create the optimized byte-array for an eventId,
* which will be sent across the network
*
* @param id -
* the long value ( threadId or sequenceId in this context)
* @return - the number of byte taken by this value
*/
public static int getByteSizeForValue(long id)
{
int length = 0;
// compare threadId to find its range
if (id <= Byte.MAX_VALUE) {
length = 1;
}
else if (id <= Short.MAX_VALUE) {
length = 2;
}
else if (id <= Integer.MAX_VALUE) {
length = 4;
}
else {
length = 8;
}
return length;
}
/**
* Gets the optmized byte-array representation of an eventId which can be sent
* across the network. For client to server messages, the membership id part
* of event-id is not need to be sent with each event. Also, the threadId and
* sequenceId need not be sent as long if their value is small. This function
* returns the optimal byte-array for the eventId.
*
* This is public for unit testing purposes only
*
* @param threadId -
* the long value of threadId
* @param sequenceId -
* the long value of sequenceId
* @return - the optimized byte-array representing the eventId
*/
public static byte[] getOptimizedByteArrayForEventID(long threadId,
long sequenceId)
{
int threadIdLength = getByteSizeForValue(threadId);
int threadIdIndex = (threadIdLength == 1) ? 0 : ((threadIdLength / 4) + 1);
int sequenceIdLength = getByteSizeForValue(sequenceId);
int sequenceIdIndex = (sequenceIdLength == 1) ? 0
: ((sequenceIdLength / 4) + 1);
int byteBufferLength = 2 + threadIdLength + sequenceIdLength;
ByteBuffer buffer = ByteBuffer.allocate(byteBufferLength);
fillerArray[threadIdIndex].fill(buffer, threadId);
fillerArray[sequenceIdIndex].fill(buffer, sequenceId);
return buffer.array();
}
/**
* Reads the optimized byte-array representation of an eventId and returns the
* long value of threadId or sequenceId ( the first invocation of this method
* on bytebuffer returns the threadId and the second returns the sequenceId.
*
* @param buffer -
* the byte-buffer wrapping the optimized byte-array for the eventId
* @return - long value of threadId or sequenceId
*/
public static long readEventIdPartsFromOptmizedByteArray(ByteBuffer buffer)
{
byte byteType = buffer.get();
long id = fillerArray[byteType].read(buffer);
return id;
}
/**
* Abstract helper class used to create optimized byte-array for the eventid,
* which will be sent across the network.
*
*/
protected static abstract class AbstractEventIDByteArrayFiller
{
/**
* This method adds to the byte-buffer, the token indicating the type of the
* passed 'id' (threadId or sequenceId) and the optimal byte array
* representing the id depending on the value of the 'id'.
*
* @param buffer -
* the byte buffer wrapping the byte array for the event id
* @param id -
* the long value of threadId or sequenceId
*/
public abstract void fill(ByteBuffer buffer, long id);
/**
* Reads the given buffer and returns the value as long.
*
* @param buffer -
* the byte-buffer containing the eventId parts which needs to be
* read
* @return - the long value of id (threadId or sequenceId).
*/
public abstract long read(ByteBuffer buffer);
}
protected static class ByteEventIDByteArrayFiller extends
AbstractEventIDByteArrayFiller
{
/**
* The token to indicate that given id ( threadId or sequenceId) is of type
* Byte
*/
static private final byte EVENTID_BYTE = 0;
/**
* Writes the given 'id' to the given buffer as 'byte' preceeded by a token
* indicating that it is written as 'byte' type.
*
* @param buffer -
* the buffer in which id is to be written
* @param id -
* the threadId or sequenceId to be written
*/
@Override
public void fill(ByteBuffer buffer, long id)
{
buffer.put(EVENTID_BYTE);
buffer.put((byte)id);
}
/**
* Reads the byte value of id from the buffer and returns it as long.
*
* @param buffer -
* the buffer from which 'id'(threadId or sequenceId) is to be read
* @return - the value of the id as long
*
*/
@Override
public long read(ByteBuffer buffer)
{
long value = buffer.get();
return value;
}
}
protected static class ShortEventIDByteArrayFiller extends
AbstractEventIDByteArrayFiller
{
/**
* The token to indicate that given id ( threadId or sequenceId) is of type
* Short
*/
static private final byte EVENTID_SHORT = 1;
/**
* Writes the given 'id' to the given buffer as 'short' preceeded by a token
* indicating that it is written as 'short' type.
*
* @param buffer -
* the buffer in which id is to be written
* @param id -
* the threadId or sequenceId to be written
*/
@Override
public void fill(ByteBuffer buffer, long id)
{
buffer.put(EVENTID_SHORT);
buffer.putShort((short)id);
}
/**
* Reads the short value of id from the buffer and returns it as long.
*
* @param buffer -
* the buffer from which 'id'(threadId or sequenceId) is to be read
* @return - the value of the id as long
*
*/
@Override
public long read(ByteBuffer buffer)
{
long value = buffer.getShort();
return value;
}
}
protected static class IntegerEventIDByteArrayFiller extends
AbstractEventIDByteArrayFiller
{
/**
* The token to indicate that given id ( threadId or sequenceId) is of type
* Integer
*/
static private final byte EVENTID_INT = 2;
/**
* Writes the given 'id' to the given buffer as 'int' preceeded by a token
* indicating that it is written as 'int' type.
*
* @param buffer -
* the buffer in which id is to be written
* @param id -
* the threadId or sequenceId to be written
*/
@Override
public void fill(ByteBuffer buffer, long id)
{
buffer.put(EVENTID_INT);
buffer.putInt((int)id);
}
/**
* Reads the int value of id from the buffer and returns it as long.
*
* @param buffer -
* the buffer from which 'id'(threadId or sequenceId) is to be read
* @return - the value of the id as long
*
*/
@Override
public long read(ByteBuffer buffer)
{
long value = buffer.getInt();
return value;
}
}
protected static class LongEventIDByteArrayFiller extends
AbstractEventIDByteArrayFiller
{
/**
* The token to indicate that given id ( threadId or sequenceId) is of type
* Long
*/
static private final byte EVENTID_LONG = 3;
/**
* Writes the given 'id' to the given buffer as 'long' preceeded by a token
* indicating that it is written as 'long' type.
*
* @param buffer -
* the buffer in which id is to be written
* @param id -
* the threadId or sequenceId to be written
*/
@Override
public void fill(ByteBuffer buffer, long id)
{
buffer.put(EVENTID_LONG);
buffer.putLong(id);
}
/**
* Reads the long value of id from the buffer and returns it.
*
* @param buffer -
* the buffer from which 'id'(threadId or sequenceId) is to be read
* @return - the value of the id as long
*
*/
@Override
public long read(ByteBuffer buffer)
{
long value = buffer.getLong();
return value;
}
}
static class ThreadAndSequenceIDWrapper {
final long threadID;
long sequenceID = (HARegionQueue.INIT_OF_SEQUENCEID + 1);
private static AL atmLong = CFactory.createAL(0);
ThreadAndSequenceIDWrapper() {
threadID = atmLong.incrementAndGet();
}
long getAndIncrementSequenceID()
{
return this.sequenceID++;
}
void reserveSequenceID(int size)
{
this.sequenceID += size;
}
}
@Override
public Version[] getSerializationVersions() {
return dsfidVersions;
}
}