| /*========================================================================= |
| * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * more patents listed at http://www.pivotal.io/patents. |
| *======================================================================== |
| */ |
| |
| package com.gemstone.gemfire.internal.cache; |
| |
| import static com.gemstone.gemfire.internal.offheap.annotations.OffHeapIdentifier.TX_ENTRY_STATE; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import com.gemstone.gemfire.DataSerializer; |
| import com.gemstone.gemfire.cache.CacheRuntimeException; |
| import com.gemstone.gemfire.cache.CacheWriter; |
| import com.gemstone.gemfire.cache.CacheWriterException; |
| import com.gemstone.gemfire.cache.CommitConflictException; |
| import com.gemstone.gemfire.cache.DataPolicy; |
| import com.gemstone.gemfire.cache.EntryDestroyedException; |
| import com.gemstone.gemfire.cache.EntryEvent; |
| import com.gemstone.gemfire.cache.EntryNotFoundException; |
| import com.gemstone.gemfire.cache.Operation; |
| import com.gemstone.gemfire.cache.Region; |
| import com.gemstone.gemfire.cache.RegionDestroyedException; |
| import com.gemstone.gemfire.cache.TimeoutException; |
| import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember; |
| import com.gemstone.gemfire.internal.Assert; |
| import com.gemstone.gemfire.internal.DataSerializableFixedID; |
| import com.gemstone.gemfire.internal.Version; |
| import com.gemstone.gemfire.internal.cache.delta.Delta; |
| import com.gemstone.gemfire.internal.cache.versions.RegionVersionVector; |
| import com.gemstone.gemfire.internal.cache.versions.VersionTag; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| import com.gemstone.gemfire.internal.lang.StringUtils; |
| import com.gemstone.gemfire.internal.logging.LogService; |
| import com.gemstone.gemfire.internal.offheap.OffHeapHelper; |
| import com.gemstone.gemfire.internal.offheap.Releasable; |
| import com.gemstone.gemfire.internal.offheap.StoredObject; |
| import com.gemstone.gemfire.internal.offheap.annotations.Released; |
| import com.gemstone.gemfire.internal.offheap.annotations.Retained; |
| import com.gemstone.gemfire.internal.offheap.annotations.Unretained; |
| import com.gemstone.gemfire.pdx.PdxSerializationException; |
| |
| /** |
| * TXEntryState is the entity that tracks transactional changes, except for |
| * those tracked by {@link TXEntryUserAttrState}, to an entry. |
| * |
| * @author Darrel Schneider |
| * |
| * @since 4.0 |
| * |
| */ |
| public class TXEntryState implements Releasable |
| { |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** |
| * This field is final except for when it is nulled out during cleanup |
| */ |
| @Retained(TX_ENTRY_STATE) |
| private Object originalVersionId; |
| private final Object originalValue; |
| |
| /** |
| * Serial number that is set each time this entry is modified. Used to order |
| * the events in a TransactionEvent. |
| */ |
| protected int modSerialNum; |
| /** |
| * Used to remember the event id to use on the farSide for this entry. See bug 39434. |
| * @since 5.7 |
| */ |
| private int farSideEventOffset = -1; |
| /** |
| * Used to remember the event id to use on the nearSide for this entry. See bug 39434. |
| * @since 5.7 |
| */ |
| private int nearSideEventOffset = -1; |
| |
| //Asif: In case of Sqlfabric, the pending value may be a SerializableDelta object |
| //which may be containing base value ( in case of Tx create) along with bunch |
| //of incremental deltas, so for correct behaviour this field should be accessed only |
| //by its getter. Do not use it directly |
| private Object pendingValue; |
| |
| /** |
| * Remember the callback argument for listener invocation |
| */ |
| private Object callBackArgument; |
| |
| private byte op; |
| |
| /** |
| * destroy field remembers the strongest destroy op perfomed on this entry |
| */ |
| private byte destroy; // DESTROY_NONE, DESTROY_LOCAL, DESTROY_DISTRIBUTED |
| |
| // ORDER of the following is important to the implementation! |
| private static final byte DESTROY_NONE = 0; |
| |
| private static final byte DESTROY_LOCAL = 1; |
| |
| private static final byte DESTROY_DISTRIBUTED = 2; |
| |
| // ORDER of the following is important to the implementation! |
| private static final byte OP_NULL = 0; |
| |
| private static final byte OP_L_DESTROY = 1; |
| |
| private static final byte OP_CREATE_LD = 2; |
| |
| private static final byte OP_LLOAD_CREATE_LD = 3; |
| |
| private static final byte OP_NLOAD_CREATE_LD = 4; |
| |
| private static final byte OP_PUT_LD = 5; |
| |
| private static final byte OP_LLOAD_PUT_LD = 6; |
| |
| private static final byte OP_NLOAD_PUT_LD = 7; |
| |
| private static final byte OP_D_INVALIDATE_LD = 8; |
| |
| private static final byte OP_D_DESTROY = 9; |
| |
| private static final byte OP_L_INVALIDATE = 10; |
| |
| private static final byte OP_PUT_LI = 11; |
| |
| private static final byte OP_LLOAD_PUT_LI = 12; |
| |
| private static final byte OP_NLOAD_PUT_LI = 13; |
| |
| private static final byte OP_D_INVALIDATE = 14; |
| |
| private static final byte OP_CREATE_LI = 15; |
| |
| private static final byte OP_LLOAD_CREATE_LI = 16; |
| |
| private static final byte OP_NLOAD_CREATE_LI = 17; |
| |
| private static final byte OP_CREATE = 18; |
| |
| private static final byte OP_SEARCH_CREATE = 19; |
| |
| private static final byte OP_LLOAD_CREATE = 20; |
| |
| private static final byte OP_NLOAD_CREATE = 21; |
| |
| private static final byte OP_LOCAL_CREATE = 22; |
| |
| private static final byte OP_PUT = 23; |
| |
| private static final byte OP_SEARCH_PUT = 24; |
| |
| private static final byte OP_LLOAD_PUT = 25; |
| |
| private static final byte OP_NLOAD_PUT = 26; |
| |
| static { |
| Assert.assertTrue(OP_SEARCH_PUT - OP_PUT == OP_SEARCH_CREATE - OP_CREATE, |
| "search offset inconsistent"); |
| Assert.assertTrue(OP_LLOAD_PUT - OP_PUT == OP_LLOAD_CREATE - OP_CREATE, |
| "lload offset inconsistent"); |
| Assert.assertTrue(OP_NLOAD_PUT - OP_PUT == OP_NLOAD_CREATE - OP_CREATE, |
| "nload offset inconsistent"); |
| } |
| |
| /** |
| * System property to be set when read conflicts should be detected. |
| * Benefits of read conflict detection are at: |
| * https://wiki.gemstone.com/display/PR/Read+conflict+detection |
| */ |
| private static final boolean DETECT_READ_CONFLICTS = Boolean.getBoolean("gemfire.detectReadConflicts"); |
| |
| // @todo darrel: optimize footprint by having this field on a subclass |
| // that is only created by TXRegionState when it knows its region needs refCounts. |
| /** |
| * A reference to the RegionEntry, in committed state, that this tx entry has |
| * referenced. Note: this field is only needed if the committed region |
| * has eviction or expiration enabled. In both those cases the tx needs to do |
| * reference counting. |
| */ |
| private final RegionEntry refCountEntry; |
| |
| /** |
| * Set to true once this entry has been written by a tx operation. |
| */ |
| private boolean dirty; |
| |
| /** |
| * Set to true if this operation is result of a bulk op. We use this |
| * boolean to determine op type rather than extending the operation algebra. |
| */ |
| private boolean bulkOp; |
| |
| private FilterRoutingInfo filterRoutingInfo =null; |
| private Set<InternalDistributedMember> adjunctRecipients = null; |
| |
| /** |
| * versionTag to be sent to other members. This is propogated up from AbstractRegionMap |
| * and then used while building TXCommitMessage |
| */ |
| private VersionTag versionTag = null; |
| |
| /** |
| * tailKey (used by wan) to be sent to other members. This is propagated up from |
| * AbstractRegionMap and then used while building TXCommitMessage |
| */ |
| private long tailKey = -1; |
| |
| /** |
| * versionTag that is fetched from remote members, if this member's data policy |
| * is not REPLICATE |
| */ |
| private VersionTag remoteVersionTag = null; |
| |
| /** |
| * Next region version generated on the primary |
| */ |
| private long nextRegionVersion = -1; |
| |
| /* |
| * For Distributed Transaction. |
| * THis value is set when applying commit |
| */ |
| private transient DistTxThinEntryState distTxThinEntryState; |
| |
| /** |
| * This constructor is used to create a singleton used by LocalRegion to |
| * signal that noop invalidate op has been performed. The instance returned by |
| * this constructor is just a marker; it is not good for anything else. |
| */ |
| protected TXEntryState() { |
| this.op = OP_NULL; |
| this.originalVersionId = Token.REMOVED_PHASE1; |
| this.originalValue = Token.REMOVED_PHASE1; |
| this.refCountEntry = null; |
| } |
| |
| |
| private TXRegionState txRegionState = null; |
| |
| /** |
| * This constructor is used when creating an entry |
| */ |
| protected TXEntryState(RegionEntry re, Object pvId, Object pv, TXRegionState txRegionState, boolean isDistributed) { |
| Object vId = pvId; |
| if (vId == null) { |
| vId = Token.REMOVED_PHASE1; |
| } |
| if (pv == null) { |
| pv = Token.REMOVED_PHASE1; |
| } |
| this.op = OP_NULL; |
| this.pendingValue = pv; |
| this.originalVersionId = vId; |
| this.originalValue = pv; |
| if (txRegionState.needsRefCounts()) { |
| this.refCountEntry = re; |
| if (re != null) { |
| re.incRefCount(); |
| } |
| } else { |
| this.refCountEntry = null; |
| } |
| this.txRegionState = txRegionState; |
| if (isDistributed) { |
| this.distTxThinEntryState = new DistTxThinEntryState(); |
| } |
| } |
| |
| public TXRegionState getTXRegionState() { |
| return txRegionState; |
| } |
| |
| public Object getOriginalVersionId() |
| { |
| return this.originalVersionId; |
| } |
| |
| public Object getOriginalValue() { |
| Object value = this.originalValue; |
| |
| if(value instanceof Delta) { |
| value = ((Delta) value).getResultantValue(); |
| } |
| |
| return value; |
| } |
| |
| public Object getPendingValue() |
| { |
| Object value = this.pendingValue; |
| |
| if(value instanceof Delta) { |
| value = ((Delta) value).getResultantValue(); |
| } |
| |
| return value; |
| } |
| |
| public Object getCallbackArgument() |
| { |
| return this.callBackArgument; |
| } |
| |
| /** |
| * Gets the pending value for near side operations. Special cases local |
| * destroy and local invalidate to fix bug 34387. |
| */ |
| @Unretained |
| public Object getNearSidePendingValue() |
| { |
| if (isOpLocalDestroy()) { |
| return null; |
| } |
| else if (isOpLocalInvalidate()) { |
| return Token.LOCAL_INVALID; |
| } |
| else { |
| return getPendingValue(); |
| } |
| } |
| |
| void setPendingValue(Object pv) |
| { |
| if(pv instanceof Delta) { |
| Object toMerge = this.pendingValue; |
| this.pendingValue = ((Delta)pv).merge(toMerge, this.op == OP_CREATE); |
| }else { |
| this.pendingValue = pv; |
| } |
| } |
| |
| void setCallbackArgument(Object callbackArgument) |
| { |
| this.callBackArgument = callbackArgument; |
| } |
| |
| /** |
| * Fetches the current modification serial number from the txState and puts it in |
| * this entry. |
| * @param modNum the next modified serial number gotten from TXState |
| * |
| * @since 5.0 |
| */ |
| public void updateForWrite(final int modNum) |
| { |
| this.dirty = true; |
| this.modSerialNum = modNum; |
| } |
| |
| /** |
| * Returns true if this entry has been written; false if only read |
| * @since 5.1 |
| */ |
| public boolean isDirty() { |
| return DETECT_READ_CONFLICTS || this.dirty; |
| } |
| /** |
| * Return true if entry has an operation. |
| * @since 5.5 |
| */ |
| public boolean hasOp() { |
| return this.op != OP_NULL; |
| } |
| /** |
| * Returns true if the transaction state has this entry existing "locally". |
| * Returns false if the transaction is going to remove this entry. |
| */ |
| public boolean existsLocally() |
| { |
| if (this.op == OP_NULL) { |
| return !Token.isRemoved(getOriginalValue()); |
| } |
| else { |
| return this.op > OP_D_DESTROY; |
| } |
| } |
| |
| private boolean isOpLocalDestroy() |
| { |
| return this.op >= OP_L_DESTROY && this.op <= OP_D_INVALIDATE_LD; |
| } |
| |
| private boolean isOpLocalInvalidate() |
| { |
| return this.op >= OP_L_INVALIDATE && this.op <= OP_NLOAD_CREATE_LI |
| && this.op != OP_D_INVALIDATE; |
| } |
| |
| /** |
| * Return true if this transaction has completely invalidated or destroyed the |
| * value of this entry in the entire distributed system. Return false if a |
| * netsearch should be done. |
| */ |
| public boolean noValueInSystem() |
| { |
| if (this.op == OP_D_DESTROY || this.op == OP_D_INVALIDATE_LD |
| || this.op == OP_D_INVALIDATE) { |
| return true; |
| } |
| else if (getNearSidePendingValue() == Token.INVALID) { |
| // Note that we are not interested in LOCAL_INVALID |
| return (this.op >= OP_CREATE_LD && this.op != OP_L_INVALIDATE |
| && this.op != OP_SEARCH_CREATE && this.op != OP_LOCAL_CREATE && this.op != OP_SEARCH_PUT); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| /** |
| * Returns true if the transaction state has this entry existing locally and |
| * has a valid local value. Returns false if the transaction is going to |
| * remove this entry or its local value is invalid. |
| */ |
| public boolean isLocallyValid(boolean isProxy) |
| { |
| if (this.op == OP_NULL) { |
| if (isProxy) { |
| // If it is a proxy that consider it locally valid |
| // since we don't have any local committed state |
| return true; |
| } |
| else { |
| return !Token.isInvalidOrRemoved(getOriginalValue()); |
| } |
| } |
| else { |
| return this.op >= OP_CREATE |
| && !Token.isInvalid(getNearSidePendingValue()); |
| } |
| } |
| |
| @Retained |
| public Object getValueInVM(Object key) throws EntryNotFoundException |
| { |
| if (!existsLocally()) { |
| throw new EntryNotFoundException(String.valueOf(key)); |
| } |
| return getNearSidePendingValue(); |
| } |
| |
| /** |
| * @param key |
| * @return the value, or null if the value does not exist in the cache, |
| * Token.INVALID or Token.LOCAL_INVALID if the value is invalid |
| */ |
| public Object getValue(Object key, Region r, boolean preferCD) |
| { |
| if (!existsLocally()) { |
| return null; |
| } |
| Object v = getNearSidePendingValue(); |
| if (v instanceof CachedDeserializable && !preferCD) { |
| // The only reason we would is if we do a read |
| // in a transaction and the initial op that created the TXEntryState |
| // did not call getDeserializedValue. |
| // If a write op is done then the pendingValue does not belong to |
| // the cache yet so we do not need the RegionEntry. |
| // If we do need the RegionEntry then TXEntryState |
| // will need to be changed to remember the RegionEntry it is created with. |
| v = ((CachedDeserializable)v).getDeserializedValue(r, this.refCountEntry); |
| } |
| |
| return v; |
| } |
| |
| private final boolean isOpCreate() |
| { |
| return this.op >= OP_CREATE_LI && this.op <= OP_LOCAL_CREATE; |
| } |
| |
| final boolean isOpCreateEvent() |
| { |
| return isOpCreate(); |
| } |
| |
| private final boolean isOpPut() |
| { |
| return this.op >= OP_PUT; |
| } |
| |
| protected final boolean isOpPutEvent() |
| { |
| return isOpPut(); |
| } |
| |
| private final boolean isOpInvalidate() |
| { |
| // Note that OP_CREATE_LI, OP_LLOAD_CREATE_LI, and OP_NLOAD_CREATE_LI |
| // do not return true here because they are actually creates |
| // with a value of LOCAL_INVALID locally and some other value remotely. |
| return this.op <= OP_D_INVALIDATE && this.op >= OP_L_INVALIDATE; |
| } |
| |
| final boolean isOpInvalidateEvent() |
| { |
| return isOpInvalidate(); |
| } |
| |
| private final boolean isOpDestroy() |
| { |
| return this.op <= OP_D_DESTROY && this.op >= OP_L_DESTROY; |
| } |
| |
| final boolean isOpDestroyEvent(LocalRegion r) |
| { |
| // Note that if the region is a proxy then we go ahead and distributed |
| // the destroy because we can't eliminate it based on committed state |
| return isOpDestroy() |
| && (r.isProxy() || (getOriginalValue() != null && !Token.isRemoved(getOriginalValue()))); |
| } |
| |
| /** |
| * Returns true if this operation has an event for the tx listener |
| * |
| * @since 5.0 |
| */ |
| final boolean isOpAnyEvent(LocalRegion r) |
| { |
| return isOpPutEvent() || isOpCreateEvent() || isOpInvalidateEvent() |
| || isOpDestroyEvent(r); |
| } |
| |
| final boolean isOpSearchOrLoad() |
| { |
| return this.op >= OP_SEARCH_CREATE && this.op != OP_PUT |
| && this.op != OP_LOCAL_CREATE; |
| } |
| |
| final boolean isOpSearch() |
| { |
| return this.op == OP_SEARCH_CREATE || this.op == OP_SEARCH_PUT; |
| } |
| |
| final boolean isOpLocalLoad() |
| { |
| return this.op == OP_LLOAD_CREATE || this.op == OP_LLOAD_PUT; |
| } |
| |
| final boolean isOpNetLoad() |
| { |
| return this.op == OP_NLOAD_CREATE || this.op == OP_NLOAD_PUT; |
| } |
| |
| final boolean isOpLoad() |
| { |
| return isOpLocalLoad() || isOpNetLoad(); |
| } |
| |
| // private final boolean isLocalEventDistributed() |
| // { |
| // return this.op == OP_D_DESTROY |
| // || (this.op >= OP_D_INVALIDATE && this.op != OP_SEARCH_CREATE |
| // && this.op != OP_LOCAL_CREATE && this.op != OP_SEARCH_PUT); |
| // } |
| |
| String opToString() |
| { |
| return opToString(this.op); |
| } |
| |
| private String opToString(byte opCode) |
| { |
| switch (opCode) { |
| case OP_NULL: |
| return "OP_NULL"; |
| case OP_L_DESTROY: |
| return "OP_L_DESTROY"; |
| case OP_CREATE_LD: |
| return "OP_CREATE_LD"; |
| case OP_LLOAD_CREATE_LD: |
| return "OP_LLOAD_CREATE_LD"; |
| case OP_NLOAD_CREATE_LD: |
| return "OP_NLOAD_CREATE_LD"; |
| case OP_PUT_LD: |
| return "OP_PUT_LD"; |
| case OP_LLOAD_PUT_LD: |
| return "OP_LLOAD_PUT_LD"; |
| case OP_NLOAD_PUT_LD: |
| return "OP_NLOAD_PUT_LD"; |
| case OP_D_INVALIDATE_LD: |
| return "OP_D_INVALIDATE_LD"; |
| case OP_D_DESTROY: |
| return "OP_D_DESTROY"; |
| case OP_L_INVALIDATE: |
| return "OP_L_INVALIDATE"; |
| case OP_PUT_LI: |
| return "OP_PUT_LI"; |
| case OP_LLOAD_PUT_LI: |
| return "OP_LLOAD_PUT_LI"; |
| case OP_NLOAD_PUT_LI: |
| return "OP_NLOAD_PUT_LI"; |
| case OP_D_INVALIDATE: |
| return "OP_D_INVALIDATE"; |
| case OP_CREATE_LI: |
| return "OP_CREATE_LI"; |
| case OP_LLOAD_CREATE_LI: |
| return "OP_LLOAD_CREATE_LI"; |
| case OP_NLOAD_CREATE_LI: |
| return "OP_NLOAD_CREATE_LI"; |
| case OP_CREATE: |
| return "OP_CREATE"; |
| case OP_SEARCH_CREATE: |
| return "OP_SEARCH_CREATE"; |
| case OP_LLOAD_CREATE: |
| return "OP_LLOAD_CREATE"; |
| case OP_NLOAD_CREATE: |
| return "OP_NLOAD_CREATE"; |
| case OP_LOCAL_CREATE: |
| return "OP_LOCAL_CREATE"; |
| case OP_PUT: |
| return "OP_PUT"; |
| case OP_SEARCH_PUT: |
| return "OP_SEARCH_PUT"; |
| case OP_LLOAD_PUT: |
| return "OP_LLOAD_PUT"; |
| case OP_NLOAD_PUT: |
| return "OP_NLOAD_PUT"; |
| default: |
| return "<unhandled op " + opCode + " >"; |
| } |
| } |
| |
| /** |
| * Returns an Operation instance that matches what the transactional operation |
| * done on this entry in the cache the the transaction was performed in. |
| */ |
| protected Operation getNearSideOperation() |
| { |
| switch (this.op) { |
| case OP_NULL: |
| return null; |
| case OP_L_DESTROY: |
| return Operation.LOCAL_DESTROY; |
| case OP_CREATE_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_LLOAD_CREATE_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_NLOAD_CREATE_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_PUT_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_LLOAD_PUT_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_NLOAD_PUT_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_D_INVALIDATE_LD: |
| return Operation.LOCAL_DESTROY; |
| case OP_D_DESTROY: |
| return getDestroyOperation(); |
| case OP_L_INVALIDATE: |
| return Operation.LOCAL_INVALIDATE; |
| case OP_PUT_LI: |
| return Operation.LOCAL_INVALIDATE; |
| case OP_LLOAD_PUT_LI: |
| return Operation.LOCAL_INVALIDATE; |
| case OP_NLOAD_PUT_LI: |
| return Operation.LOCAL_INVALIDATE; |
| case OP_D_INVALIDATE: |
| return Operation.INVALIDATE; |
| case OP_CREATE_LI: |
| return getCreateOperation(); |
| case OP_LLOAD_CREATE_LI: |
| return getCreateOperation(); |
| case OP_NLOAD_CREATE_LI: |
| return getCreateOperation(); |
| case OP_CREATE: |
| return getCreateOperation(); |
| case OP_SEARCH_CREATE: |
| return Operation.SEARCH_CREATE; |
| case OP_LLOAD_CREATE: |
| return Operation.LOCAL_LOAD_CREATE; |
| case OP_NLOAD_CREATE: |
| return Operation.NET_LOAD_CREATE; |
| case OP_LOCAL_CREATE: |
| return getCreateOperation(); |
| case OP_PUT: |
| return getUpdateOperation(); |
| case OP_SEARCH_PUT: |
| return Operation.SEARCH_UPDATE; |
| case OP_LLOAD_PUT: |
| return Operation.LOCAL_LOAD_UPDATE; |
| case OP_NLOAD_PUT: |
| return Operation.NET_LOAD_CREATE; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNHANDLED_OP_0.toLocalizedString(Byte.valueOf(this.op))); |
| } |
| } |
| |
| /** |
| * @return true when the operation is the result of a bulk op |
| */ |
| private boolean isBulkOp() { |
| return this.bulkOp; |
| } |
| /** |
| * Calculate and return the event offset based on the sequence id on TXState. |
| * @since 5.7 |
| */ |
| private static int generateEventOffset(TXState txState) { |
| long seqId = EventID.reserveSequenceId(); |
| int offset = (int)(seqId - txState.getBaseSequenceId()); |
| return offset; |
| } |
| |
| /** |
| * Generate offsets for different eventIds; one for nearside and one for farside |
| * for the ops for this entry. |
| * @since 5.7 |
| */ |
| private void generateBothEventOffsets(TXState txState) { |
| assert this.farSideEventOffset == -1; |
| this.farSideEventOffset = generateEventOffset(txState); |
| generateNearSideEventOffset(txState); |
| } |
| /** |
| * Generate the offset for an eventId that will be used for both a farside and nearside |
| * op for this entry. |
| * @since 5.7 |
| */ |
| private void generateSharedEventOffset(TXState txState) { |
| assert this.farSideEventOffset == -1; |
| generateNearSideEventOffset(txState); |
| this.farSideEventOffset = this.nearSideEventOffset; |
| } |
| /** |
| * Generate the offset for an eventId that will be used for the nearside |
| * op for this entry. No farside op will be done. |
| * @since 5.7 |
| */ |
| private void generateNearSideOnlyEventOffset(TXState txState) { |
| generateNearSideEventOffset(txState); |
| } |
| |
| private void generateNearSideEventOffset(TXState txState) { |
| assert this.nearSideEventOffset == -1; |
| this.nearSideEventOffset = generateEventOffset(txState); |
| } |
| |
| private int getFarSideEventOffset() { |
| assert this.nearSideEventOffset != -1; |
| return this.nearSideEventOffset; |
| } |
| |
| private static EventID createEventID(TXState txState, int offset) { |
| return new EventID(txState.getBaseMembershipId(), |
| txState.getBaseThreadId(), |
| txState.getBaseSequenceId() + offset); |
| } |
| /** |
| * Calculate (if farside has not already done so) and return then eventID |
| * to use for near side op applications. |
| * @since 5.7 |
| */ |
| private EventID getNearSideEventId(TXState txState) { |
| assert this.nearSideEventOffset != -1; |
| return createEventID(txState, this.nearSideEventOffset); |
| } |
| |
| /** |
| * Calculate and return the event offset for this entry's farSide operation. |
| * @since 5.7 |
| */ |
| void generateEventOffsets(TXState txState) { |
| switch (this.op) { |
| case OP_NULL: |
| // no eventIds needed |
| break; |
| case OP_L_DESTROY: |
| generateNearSideOnlyEventOffset(txState); |
| break; |
| case OP_CREATE_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_LLOAD_CREATE_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_NLOAD_CREATE_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_PUT_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_LLOAD_PUT_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_NLOAD_PUT_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_D_INVALIDATE_LD: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_D_DESTROY: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_L_INVALIDATE: |
| generateNearSideOnlyEventOffset(txState); |
| break; |
| case OP_PUT_LI: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_LLOAD_PUT_LI: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_NLOAD_PUT_LI: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_D_INVALIDATE: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_CREATE_LI: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_LLOAD_CREATE_LI: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_NLOAD_CREATE_LI: |
| generateBothEventOffsets(txState); |
| break; |
| case OP_CREATE: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_SEARCH_CREATE: |
| generateNearSideOnlyEventOffset(txState); |
| break; |
| case OP_LLOAD_CREATE: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_NLOAD_CREATE: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_LOCAL_CREATE: |
| generateNearSideOnlyEventOffset(txState); |
| break; |
| case OP_PUT: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_SEARCH_PUT: |
| generateNearSideOnlyEventOffset(txState); |
| break; |
| case OP_LLOAD_PUT: |
| generateSharedEventOffset(txState); |
| break; |
| case OP_NLOAD_PUT: |
| generateSharedEventOffset(txState); |
| break; |
| default: |
| throw new IllegalStateException("<unhandled op " + this.op + " >"); |
| } |
| } |
| /** |
| * Gets the operation code for the operation done on this entry in caches |
| * remote from the originator of the tx (i.e. the "far side"). |
| * |
| * @return null if no far side operation |
| */ |
| private Operation getFarSideOperation() |
| { |
| switch (this.op) { |
| case OP_NULL: |
| return null; |
| case OP_L_DESTROY: |
| return null; |
| case OP_CREATE_LD: |
| return getCreateOperation(); |
| case OP_LLOAD_CREATE_LD: |
| return Operation.LOCAL_LOAD_CREATE; |
| case OP_NLOAD_CREATE_LD: |
| return Operation.NET_LOAD_CREATE; |
| case OP_PUT_LD: |
| return getUpdateOperation(); |
| case OP_LLOAD_PUT_LD: |
| return Operation.LOCAL_LOAD_UPDATE; |
| case OP_NLOAD_PUT_LD: |
| return Operation.NET_LOAD_UPDATE; |
| case OP_D_INVALIDATE_LD: |
| return Operation.INVALIDATE; |
| case OP_D_DESTROY: |
| return getDestroyOperation(); |
| case OP_L_INVALIDATE: |
| return null; |
| case OP_PUT_LI: |
| return getUpdateOperation(); |
| case OP_LLOAD_PUT_LI: |
| return Operation.LOCAL_LOAD_UPDATE; |
| case OP_NLOAD_PUT_LI: |
| return Operation.NET_LOAD_UPDATE; |
| case OP_D_INVALIDATE: |
| return Operation.INVALIDATE; |
| case OP_CREATE_LI: |
| return getCreateOperation(); |
| case OP_LLOAD_CREATE_LI: |
| return Operation.LOCAL_LOAD_CREATE; |
| case OP_NLOAD_CREATE_LI: |
| return Operation.NET_LOAD_CREATE; |
| case OP_CREATE: |
| return getCreateOperation(); |
| case OP_SEARCH_CREATE: |
| return null; |
| case OP_LLOAD_CREATE: |
| return Operation.LOCAL_LOAD_CREATE; |
| case OP_NLOAD_CREATE: |
| return Operation.NET_LOAD_CREATE; |
| case OP_LOCAL_CREATE: |
| return getCreateOperation(); |
| case OP_PUT: |
| return getUpdateOperation(); |
| case OP_SEARCH_PUT: |
| return null; |
| case OP_LLOAD_PUT: |
| return Operation.LOCAL_LOAD_UPDATE; |
| case OP_NLOAD_PUT: |
| return Operation.NET_LOAD_UPDATE; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNHANDLED_OP_0.toLocalizedString(Byte.valueOf(this.op))); |
| } |
| } |
| |
| // private void dumpOp() { |
| // System.out.println("DEBUG: op=" + opToString() |
| // + " destroy=" + this.destroy |
| // + " isDis=" + isLocalEventDistributed()); |
| // System.out.flush(); |
| // } |
| EntryEvent getEvent(LocalRegion r, Object key, TXState txs) |
| { |
| // dumpOp(); |
| //TODO:ASIF : Shopuld we generate EventID ? At this point not generating |
| LocalRegion eventRegion = r; |
| if (r.isUsedForPartitionedRegionBucket()) { |
| eventRegion = r.getPartitionedRegion(); |
| } |
| EntryEventImpl result = new TxEntryEventImpl(eventRegion, key); |
| // OFFHEAP: freeOffHeapResources on this event is called from TXEvent.freeOffHeapResources. |
| boolean returnedResult = false; |
| try { |
| if (this.destroy == DESTROY_NONE || isOpDestroy()) { |
| result.setOldValue(getOriginalValue()); |
| } |
| if(txs.isOriginRemoteForEvents()) { |
| result.setOriginRemote(true); |
| } else { |
| result.setOriginRemote(false); |
| } |
| result.setTransactionId(txs.getTransactionId()); |
| returnedResult = true; |
| return result; |
| } finally { |
| if (!returnedResult) result.release(); |
| } |
| } |
| |
| /** |
| * @return true if invalidate was done |
| */ |
| public boolean invalidate(EntryEventImpl event) throws EntryNotFoundException |
| { |
| // LocalRegion lr = event.getRegion(); |
| // boolean isProxy = lr.isProxy(); |
| // if (!isLocallyValid(isProxy)) { |
| // return false; |
| // } |
| if (event.isLocalInvalid()) { |
| performOp(adviseOp(OP_L_INVALIDATE, event), event); |
| } |
| else { |
| performOp(adviseOp(OP_D_INVALIDATE, event), event); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @return true if destroy was done |
| */ |
| public boolean destroy(EntryEventImpl event, boolean cacheWrite, boolean originRemote) |
| throws CacheWriterException, EntryNotFoundException, TimeoutException |
| { |
| LocalRegion lr = event.getRegion(); |
| CacheWriter cWriter = lr.basicGetWriter(); |
| byte advisedOp = adviseOp((cacheWrite ? OP_D_DESTROY : OP_L_DESTROY), event); |
| if (cWriter != null && cacheWrite && !event.inhibitAllNotifications()) { |
| boolean oldOriginRemote = event.isOriginRemote(); |
| if (event.hasClientOrigin() || originRemote) { |
| event.setOriginRemote(true); |
| } |
| cWriter.beforeDestroy(event); |
| event.setOriginRemote(oldOriginRemote); |
| } |
| |
| if (advisedOp != OP_NULL) { |
| this.bulkOp = event.getOperation().isRemoveAll(); |
| if (cacheWrite) { |
| performOp(advisedOp, event); |
| this.destroy = DESTROY_DISTRIBUTED; |
| } |
| else { |
| performOp(advisedOp, event); |
| if (this.destroy != DESTROY_DISTRIBUTED) { |
| this.destroy = DESTROY_LOCAL; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @param event |
| * the event object for this operation, with the exception that the |
| * oldValue parameter is not yet filled in. The oldValue will be |
| * filled in by this operation. |
| * |
| * @param ifNew |
| * true if this operation must not overwrite an existing key |
| * @param originRemote value for cacheWriter's isOriginRemote flag |
| * @return true if put was done; otherwise returns false |
| */ |
| public boolean basicPut(EntryEventImpl event, boolean ifNew, boolean originRemote) |
| throws CacheWriterException, TimeoutException |
| { |
| byte putOp = OP_CREATE; |
| boolean doingCreate = true; |
| if (!ifNew) { |
| if (existsLocally()) { |
| putOp = OP_PUT; |
| doingCreate = false; |
| } |
| } |
| if (doingCreate) { |
| event.makeCreate(); |
| } |
| else { |
| event.makeUpdate(); |
| } |
| if (event.isNetSearch()) { |
| putOp += (byte)(OP_SEARCH_PUT - OP_PUT); |
| } |
| else if (event.isLocalLoad()) { |
| putOp += (byte)(OP_LLOAD_PUT - OP_PUT); |
| } |
| else if (event.isNetLoad()) { |
| putOp += (byte)(OP_NLOAD_PUT - OP_PUT); |
| } |
| this.bulkOp = event.getOperation().isPutAll(); |
| byte advisedOp = adviseOp(putOp, event); |
| |
| LocalRegion lr = event.getRegion(); |
| if (!event.isNetSearch()) { |
| CacheWriter cWriter = lr.basicGetWriter(); |
| boolean oldOriginRemote = event.isOriginRemote(); |
| if (event.hasClientOrigin() || originRemote) { |
| event.setOriginRemote(true); |
| } |
| if (cWriter != null && event.isGenerateCallbacks() && !event.inhibitAllNotifications()) { |
| if (doingCreate) { |
| cWriter.beforeCreate(event); |
| } |
| else { |
| cWriter.beforeUpdate(event); |
| } |
| } |
| event.setOriginRemote(oldOriginRemote); |
| } |
| |
| performOp(advisedOp, event); |
| |
| if (putOp == OP_CREATE) { |
| fetchRemoteVersionTag(event); |
| } |
| return true; |
| } |
| |
| /** |
| * We will try to establish TXState on members with dataPolicy REPLICATE, this |
| * is done for the first region to be involved in a transaction. For |
| * subsequent region if the dataPolicy is not REPLICATE, we fetch the |
| * VersionTag from replicate members. |
| * |
| * @param event |
| */ |
| private void fetchRemoteVersionTag(EntryEventImpl event) { |
| if (event.getRegion() instanceof DistributedRegion) { |
| DistributedRegion dr = (DistributedRegion) event.getRegion(); |
| if (dr.dataPolicy == DataPolicy.NORMAL |
| || dr.dataPolicy == DataPolicy.PRELOADED) { |
| VersionTag tag = null; |
| try { |
| tag = dr.fetchRemoteVersionTag(event.getKey()); |
| } catch (EntryNotFoundException e) { |
| // ignore |
| } |
| if (tag != null) { |
| setRemoteVersionTag(tag); |
| } |
| } |
| } |
| } |
| |
| |
| /* TODO OFFHEAP MERGE: is this code needed? |
| @Retained |
| protected final Object getRetainedValueInTXOrRegion() { |
| @Unretained Object val = this.getValueInTXOrRegion(); |
| if (val instanceof Chunk) { |
| if (!((Chunk) val).retain()) { |
| throw new IllegalStateException("Could not retain OffHeap value=" + val); |
| } |
| } |
| return val; |
| } |
| */ |
| |
| /** |
| * Perform operation algebra |
| * |
| * @return false if operation was not done |
| */ |
| private byte adviseOp(byte requestedOpCode, EntryEventImpl event) |
| { |
| { // Set event old value |
| Object oldVal; |
| if (this.op == OP_NULL) { |
| oldVal = getOriginalValue(); |
| } |
| else { |
| oldVal = getNearSidePendingValue(); |
| } |
| |
| Region region = event.getRegion(); |
| boolean needOldValue = region instanceof HARegion // fix for bug 37909 |
| || region instanceof BucketRegion; |
| event.setTXEntryOldValue(oldVal, needOldValue); |
| } |
| |
| byte advisedOpCode = OP_NULL; |
| // Determine new operation based on |
| // the requested operation 'requestedOpCode' and |
| // the previous operation 'this.op' |
| switch (requestedOpCode) { |
| case OP_L_DESTROY: |
| switch (this.op) { |
| case OP_NULL: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_L_DESTROY: |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_INVALIDATE_LD: |
| case OP_D_DESTROY: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNEXPECTED_CURRENT_OP_0_FOR_REQUESTED_OP_1.toLocalizedString(new Object[] {opToString(), opToString(requestedOpCode)})); |
| case OP_L_INVALIDATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_PUT_LI: |
| advisedOpCode = OP_PUT_LD; |
| break; |
| case OP_LLOAD_PUT_LI: |
| advisedOpCode = OP_LLOAD_PUT_LD; |
| break; |
| case OP_NLOAD_PUT_LI: |
| advisedOpCode = OP_NLOAD_PUT_LD; |
| break; |
| case OP_D_INVALIDATE: |
| advisedOpCode = OP_D_INVALIDATE_LD; |
| break; |
| case OP_CREATE_LI: |
| advisedOpCode = OP_CREATE_LD; |
| break; |
| case OP_LLOAD_CREATE_LI: |
| advisedOpCode = OP_LLOAD_CREATE_LD; |
| break; |
| case OP_NLOAD_CREATE_LI: |
| advisedOpCode = OP_NLOAD_CREATE_LD; |
| break; |
| case OP_CREATE: |
| advisedOpCode = OP_CREATE_LD; |
| break; |
| case OP_SEARCH_CREATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_LLOAD_CREATE: |
| advisedOpCode = OP_LLOAD_CREATE_LD; |
| break; |
| case OP_NLOAD_CREATE: |
| advisedOpCode = OP_NLOAD_CREATE_LD; |
| break; |
| case OP_LOCAL_CREATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_PUT: |
| advisedOpCode = OP_PUT_LD; |
| break; |
| case OP_SEARCH_PUT: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_LLOAD_PUT: |
| advisedOpCode = OP_LLOAD_PUT_LD; |
| break; |
| case OP_NLOAD_PUT: |
| advisedOpCode = OP_NLOAD_PUT_LD; |
| break; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNHANDLED_0.toLocalizedString(opToString())); |
| } |
| break; |
| case OP_D_DESTROY: |
| Assert.assertTrue(!isOpDestroy(), |
| "Transactional destroy assertion op=" + this.op); |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_L_INVALIDATE: |
| switch (this.op) { |
| case OP_NULL: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_L_DESTROY: |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_DESTROY: |
| case OP_D_INVALIDATE_LD: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNEXPECTED_CURRENT_OP_0_FOR_REQUESTED_OP_1.toLocalizedString(new Object[] {opToString(), opToString(requestedOpCode)})); |
| case OP_L_INVALIDATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_LLOAD_PUT_LI: |
| case OP_NLOAD_PUT_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| advisedOpCode = this.op; |
| break; |
| case OP_PUT_LI: |
| advisedOpCode = OP_PUT_LI; |
| break; |
| case OP_CREATE_LI: |
| advisedOpCode = OP_CREATE_LI; |
| break; |
| case OP_D_INVALIDATE: |
| advisedOpCode = OP_D_INVALIDATE; |
| break; |
| case OP_CREATE: |
| advisedOpCode = OP_CREATE_LI; |
| break; |
| case OP_SEARCH_CREATE: |
| advisedOpCode = OP_LOCAL_CREATE; |
| // pendingValue will be set to LOCAL_INVALID |
| break; |
| case OP_LLOAD_CREATE: |
| advisedOpCode = OP_LLOAD_CREATE_LI; |
| break; |
| case OP_NLOAD_CREATE: |
| advisedOpCode = OP_NLOAD_CREATE_LI; |
| break; |
| case OP_LOCAL_CREATE: |
| advisedOpCode = OP_LOCAL_CREATE; |
| break; |
| case OP_PUT: |
| advisedOpCode = OP_PUT_LI; |
| break; |
| case OP_SEARCH_PUT: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_LLOAD_PUT: |
| advisedOpCode = OP_LLOAD_PUT_LI; |
| break; |
| case OP_NLOAD_PUT: |
| advisedOpCode = OP_NLOAD_PUT_LI; |
| break; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNHANDLED_0.toLocalizedString(opToString())); |
| } |
| break; |
| case OP_D_INVALIDATE: |
| switch (this.op) { |
| case OP_NULL: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_L_DESTROY: |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_INVALIDATE_LD: |
| case OP_D_DESTROY: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNEXPECTED_CURRENT_OP_0_FOR_REQUESTED_OP_1.toLocalizedString(new Object[] {opToString(), opToString(requestedOpCode)})); |
| case OP_D_INVALIDATE: |
| case OP_L_INVALIDATE: |
| advisedOpCode = OP_D_INVALIDATE; |
| break; |
| |
| case OP_PUT_LI: |
| case OP_LLOAD_PUT_LI: |
| case OP_NLOAD_PUT_LI: |
| case OP_CREATE_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| /* |
| * No change, keep it how it was. |
| */ |
| advisedOpCode = this.op; |
| break; |
| case OP_CREATE: |
| advisedOpCode = OP_CREATE; |
| // pendingValue will be set to INVALID turning it into create invalid |
| break; |
| case OP_SEARCH_CREATE: |
| advisedOpCode = OP_LOCAL_CREATE; |
| // pendingValue will be set to INVALID to indicate dinvalidate |
| break; |
| case OP_LLOAD_CREATE: |
| advisedOpCode = OP_CREATE; |
| // pendingValue will be set to INVALID turning it into create invalid |
| break; |
| case OP_NLOAD_CREATE: |
| advisedOpCode = OP_CREATE; |
| // pendingValue will be set to INVALID turning it into create invalid |
| break; |
| case OP_LOCAL_CREATE: |
| advisedOpCode = OP_LOCAL_CREATE; |
| // pendingValue will be set to INVALID to indicate dinvalidate |
| break; |
| case OP_PUT: |
| case OP_SEARCH_PUT: |
| case OP_LLOAD_PUT: |
| case OP_NLOAD_PUT: |
| advisedOpCode = requestedOpCode; |
| break; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNHANDLED_0.toLocalizedString(opToString())); |
| } |
| break; |
| case OP_CREATE: |
| case OP_SEARCH_CREATE: |
| case OP_LLOAD_CREATE: |
| case OP_NLOAD_CREATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_PUT: |
| switch (this.op) { |
| case OP_CREATE: |
| case OP_SEARCH_CREATE: |
| case OP_LLOAD_CREATE: |
| case OP_NLOAD_CREATE: |
| case OP_LOCAL_CREATE: |
| case OP_CREATE_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_INVALIDATE_LD: |
| case OP_L_DESTROY: |
| case OP_D_DESTROY: |
| advisedOpCode = OP_CREATE; |
| break; |
| default: |
| advisedOpCode = requestedOpCode; |
| break; |
| } |
| break; |
| case OP_SEARCH_PUT: |
| switch (this.op) { |
| case OP_NULL: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_L_INVALIDATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| // The incoming search put value should match |
| // the pendingValue from the previous tx operation. |
| // So it is ok to simply drop the _LI from the op |
| case OP_PUT_LI: |
| advisedOpCode = OP_PUT; |
| break; |
| case OP_LLOAD_PUT_LI: |
| advisedOpCode = OP_LLOAD_PUT; |
| break; |
| case OP_NLOAD_PUT_LI: |
| advisedOpCode = OP_NLOAD_PUT; |
| break; |
| case OP_CREATE_LI: |
| advisedOpCode = OP_CREATE; |
| break; |
| case OP_LLOAD_CREATE_LI: |
| advisedOpCode = OP_LLOAD_CREATE; |
| break; |
| case OP_NLOAD_CREATE_LI: |
| advisedOpCode = OP_NLOAD_CREATE; |
| break; |
| default: |
| // Note that OP_LOCAL_CREATE and OP_CREATE with invalid values |
| // are not possible because they would cause the netsearch to |
| // fail and we would do a load or a total miss. |
| // Note that OP_D_INVALIDATE followed by OP_SEARCH_PUT is not |
| // possible since the netsearch will alwsys "miss" in this case. |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_PREVIOUS_OP_0_UNEXPECTED_FOR_REQUESTED_OP_1.toLocalizedString(new Object[] {opToString(), opToString(requestedOpCode)})); |
| } |
| break; |
| case OP_LLOAD_PUT: |
| case OP_NLOAD_PUT: |
| switch (this.op) { |
| case OP_NULL: |
| case OP_L_INVALIDATE: |
| case OP_PUT_LI: |
| case OP_LLOAD_PUT_LI: |
| case OP_NLOAD_PUT_LI: |
| case OP_D_INVALIDATE: |
| advisedOpCode = requestedOpCode; |
| break; |
| case OP_CREATE: |
| case OP_LOCAL_CREATE: |
| case OP_CREATE_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| if (requestedOpCode == OP_LLOAD_PUT) { |
| advisedOpCode = OP_LLOAD_CREATE; |
| } |
| else { |
| advisedOpCode = OP_NLOAD_CREATE; |
| } |
| break; |
| default: |
| // note that other invalid states are covered by this default |
| // case because they should have caused a OP_SEARCH_PUT |
| // to be requested. |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_PREVIOUS_OP_0_UNEXPECTED_FOR_REQUESTED_OP_1.toLocalizedString(new Object[] {opToString(), opToString(requestedOpCode)})); |
| } |
| break; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_OPCODE_0_SHOULD_NEVER_BE_REQUESTED.toLocalizedString(opToString(requestedOpCode))); |
| } |
| return advisedOpCode; |
| } |
| |
| private void performOp(byte advisedOpCode, EntryEventImpl event) |
| { |
| this.op = advisedOpCode; |
| event.putValueTXEntry(this); |
| } |
| |
| private boolean areIdentical(Object o1, Object o2) { |
| if (o1 == o2) return true; |
| if (o1 instanceof StoredObject) { |
| if (o1.equals(o2)) return true; |
| } |
| return false; |
| } |
| |
| void checkForConflict(LocalRegion r, Object key) |
| throws CommitConflictException |
| { |
| if (!isDirty()) { |
| // All we did was read the entry and we don't do read/write conflicts; yet. |
| return; |
| } |
| if (isOpSearch()) { |
| // netsearch never cause conflicts |
| // Note that we don't care, in this case, if region still exists. |
| // Search should never cause a conflict |
| return; |
| } |
| try { |
| r.checkReadiness(); |
| RegionEntry re = r.basicGetEntry(key); |
| Object curCmtVersionId = null; |
| try { |
| if ((re == null) || re.isValueNull()) { |
| curCmtVersionId = Token.REMOVED_PHASE1; |
| } |
| else { |
| if (re.getValueWasResultOfSearch()) { |
| return; |
| } |
| |
| /* |
| * The originalVersionId may be compressed so grab the value as stored in the map |
| * which will match if compression is turned on. |
| */ |
| curCmtVersionId = re.getTransformedValue(); |
| } |
| if (!areIdentical(getOriginalVersionId(), curCmtVersionId)) { |
| // I'm not sure it is a good idea to call getDeserializedValue. |
| // Might be better to add a toString impl on CachedDeserializable. |
| // if (curCmtVersionId instanceof CachedDeserializable) { |
| // curCmtVersionId = |
| // ((CachedDeserializable)curCmtVersionId).getDeserializedValue(); |
| // } |
| String fromString = calcConflictString(getOriginalVersionId()); |
| String toString = calcConflictString(curCmtVersionId); |
| if (fromString.equals(toString)) { |
| throw new CommitConflictException(LocalizedStrings.TXEntryState_ENTRY_FOR_KEY_0_ON_REGION_1_HAD_A_STATE_CHANGE.toLocalizedString(new Object[] {key, r.getDisplayName()})); |
| } |
| else { |
| throw new CommitConflictException(LocalizedStrings.TXEntryState_ENTRY_FOR_KEY_0_ON_REGION_1_HAD_ALREADY_BEEN_CHANGED_FROM_2_TO_3.toLocalizedString(new Object[] {key, r.getDisplayName(), fromString, toString})); |
| } |
| } |
| } finally { |
| OffHeapHelper.release(curCmtVersionId); |
| } |
| } |
| catch (CacheRuntimeException ex) { |
| r.getCancelCriterion().checkCancelInProgress(null); |
| throw new CommitConflictException(LocalizedStrings.TXEntryState_CONFLICT_CAUSED_BY_CACHE_EXCEPTION.toLocalizedString(), ex); |
| } |
| } |
| |
| private String calcConflictString(Object obj) |
| { |
| Object o = obj; |
| if (o instanceof CachedDeserializable) { |
| if (o instanceof StoredObject && ((StoredObject) o).isCompressed()) { |
| // fix for bug 52113 |
| return "<compressed value of size " + ((StoredObject) o).getValueSizeInBytes() + ">"; |
| } |
| try { |
| o = ((CachedDeserializable)o).getDeserializedForReading(); |
| } catch (PdxSerializationException e) { |
| o = "<value unavailable>"; |
| } |
| } |
| if (o == null) { |
| return "<null>"; |
| } |
| else if (Token.isRemoved(o)) { |
| return "<null>"; |
| } |
| else if (o == Token.INVALID) { |
| return "<invalidated>"; |
| } |
| else if (o == Token.LOCAL_INVALID) { |
| return "<local invalidated>"; |
| } |
| return "<" + StringUtils.forceToString(o) + ">"; |
| } |
| |
| private final boolean didDestroy() |
| { |
| return this.destroy != DESTROY_NONE; |
| } |
| |
| private final boolean didDistributedDestroy() |
| { |
| return this.destroy == DESTROY_DISTRIBUTED; |
| } |
| |
| /** |
| * Returns the total number of modifications made by this transaction to this |
| * entry. The result will be +1 for a create and -1 for a destroy. |
| */ |
| int entryCountMod() |
| { |
| switch (this.op) { |
| case OP_L_DESTROY: |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_INVALIDATE_LD: |
| case OP_D_DESTROY: |
| if (getOriginalValue() == null |
| || Token.isRemoved(getOriginalValue())) { |
| return 0; |
| } |
| else { |
| return -1; |
| } |
| case OP_CREATE_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| case OP_CREATE: |
| case OP_SEARCH_CREATE: |
| case OP_LLOAD_CREATE: |
| case OP_NLOAD_CREATE: |
| case OP_LOCAL_CREATE: |
| if (getOriginalValue() == null |
| || Token.isRemoved(getOriginalValue())) { |
| return 1; |
| } |
| else { |
| return 0; |
| } |
| case OP_NULL: |
| case OP_L_INVALIDATE: |
| case OP_PUT_LI: |
| case OP_LLOAD_PUT_LI: |
| case OP_NLOAD_PUT_LI: |
| case OP_D_INVALIDATE: |
| case OP_PUT: |
| case OP_SEARCH_PUT: |
| case OP_LLOAD_PUT: |
| case OP_NLOAD_PUT: |
| default: |
| return 0; |
| } |
| } |
| |
| /** |
| * Returns true if this entry may have been created by this transaction. |
| */ |
| boolean wasCreatedByTX() |
| { |
| return isOpCreate(); |
| } |
| |
| private final void txApplyDestroyLocally(LocalRegion r, Object key, TXState txState) |
| { |
| boolean invokeCallbacks = isOpDestroyEvent(r); |
| List<EntryEventImpl> pendingCallbacks = invokeCallbacks ? txState.getPendingCallbacks() : new ArrayList<EntryEventImpl>(); |
| try { |
| r.txApplyDestroy(key, |
| txState.getTransactionId(), null, false/*inTokenMode*/, |
| getDestroyOperation(), |
| getNearSideEventId(txState), callBackArgument, |
| pendingCallbacks ,getFilterRoutingInfo(), |
| txState.bridgeContext, false, this, null, -1); |
| } |
| catch (RegionDestroyedException ignore) { |
| } |
| catch (EntryDestroyedException ignore) { |
| } |
| // if !isOpDestroyEvent then |
| // this destroy, to local committed state, becomes a noop |
| // since nothing needed to be done locally. |
| // We don't want to actually do the destroy since we told the |
| // transaction listener that no destroy was done. |
| } |
| |
| private final void txApplyInvalidateLocally(LocalRegion r, Object key, |
| Object newValue, boolean didDestroy, TXState txState) |
| { |
| try { |
| r.txApplyInvalidate(key, newValue, didDestroy, |
| txState.getTransactionId(), null, |
| isOpLocalInvalidate() ? true : false, |
| getNearSideEventId(txState), callBackArgument,txState.getPendingCallbacks(), |
| getFilterRoutingInfo(), |
| txState.bridgeContext, this, null, -1); |
| } |
| catch (RegionDestroyedException ignore) { |
| } |
| catch (EntryDestroyedException ignore) { |
| } |
| } |
| |
| private final void txApplyPutLocally(LocalRegion r, Operation putOp, |
| Object key, Object newValue, boolean didDestroy, TXState txState) |
| { |
| try { |
| r.txApplyPut(putOp, key, newValue, didDestroy, |
| txState.getTransactionId(), null, |
| getNearSideEventId(txState), callBackArgument,txState.getPendingCallbacks(), |
| getFilterRoutingInfo(), |
| txState.bridgeContext, this, null, -1); |
| } |
| catch (RegionDestroyedException ignore) { |
| } |
| catch (EntryDestroyedException ignore) { |
| } |
| } |
| |
| /** |
| * If the entry is not dirty (read only) then clean it up. |
| * @return true if this entry is not dirty. |
| */ |
| boolean cleanupNonDirty(LocalRegion r) { |
| if (isDirty()) { |
| return false; |
| } |
| else { |
| cleanup(r); |
| return true; |
| } |
| } |
| |
| void buildMessage(LocalRegion r, Object key, TXCommitMessage msg,Set otherRecipients) |
| { |
| if (!isDirty()) { |
| // all we do was read so just return |
| return; |
| } |
| switch (this.op) { |
| case OP_NULL: |
| case OP_L_DESTROY: |
| case OP_L_INVALIDATE: |
| case OP_SEARCH_CREATE: |
| case OP_SEARCH_PUT: |
| // local only |
| break; |
| |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_INVALIDATE_LD: |
| case OP_D_DESTROY: |
| case OP_PUT_LI: |
| case OP_LLOAD_PUT_LI: |
| case OP_NLOAD_PUT_LI: |
| case OP_D_INVALIDATE: |
| case OP_CREATE_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| case OP_CREATE: |
| case OP_LLOAD_CREATE: |
| case OP_NLOAD_CREATE: |
| case OP_LOCAL_CREATE: |
| case OP_PUT: |
| case OP_LLOAD_PUT: |
| case OP_NLOAD_PUT: |
| msg.addOp(r,key, this,otherRecipients); |
| break; |
| |
| default: |
| throw new IllegalStateException("Unhandled op=" + opToString()); |
| } |
| } |
| |
| |
| void buildCompleteMessage(LocalRegion r, Object key, TXCommitMessage msg,Set otherRecipients) |
| { |
| if (!isDirty()) { |
| // all we do was read so just return |
| return; |
| } |
| switch (this.op) { |
| case OP_NULL: |
| case OP_L_DESTROY: |
| case OP_L_INVALIDATE: |
| case OP_SEARCH_CREATE: |
| case OP_SEARCH_PUT: |
| // local only |
| break; |
| |
| case OP_CREATE_LD: |
| case OP_LLOAD_CREATE_LD: |
| case OP_NLOAD_CREATE_LD: |
| case OP_PUT_LD: |
| case OP_LLOAD_PUT_LD: |
| case OP_NLOAD_PUT_LD: |
| case OP_D_INVALIDATE_LD: |
| case OP_D_DESTROY: |
| case OP_PUT_LI: |
| case OP_LLOAD_PUT_LI: |
| case OP_NLOAD_PUT_LI: |
| case OP_D_INVALIDATE: |
| case OP_CREATE_LI: |
| case OP_LLOAD_CREATE_LI: |
| case OP_NLOAD_CREATE_LI: |
| case OP_CREATE: |
| case OP_LLOAD_CREATE: |
| case OP_NLOAD_CREATE: |
| case OP_LOCAL_CREATE: |
| case OP_PUT: |
| case OP_LLOAD_PUT: |
| case OP_NLOAD_PUT: |
| msg.addOp(r,key, this,otherRecipients); |
| break; |
| |
| default: |
| throw new IllegalStateException("Unhandled op=" + opToString()); |
| } |
| } |
| |
| |
| |
| void applyChanges(LocalRegion r, Object key, TXState txState) |
| { |
| if (LogService.getLogger().isDebugEnabled()) { |
| LogService.getLogger().debug( |
| "applyChanges txState=" + txState + " ,key=" + key + " ,r=" |
| + r.getDisplayName() + " ,op=" + this.op + " ,isDirty=" |
| + isDirty()); |
| } |
| if (!isDirty()) { |
| // all we did was read so just return |
| return; |
| } |
| switch (this.op) { |
| case OP_NULL: |
| // do nothing |
| break; |
| case OP_L_DESTROY: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_CREATE_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_LLOAD_CREATE_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_NLOAD_CREATE_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_PUT_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_LLOAD_PUT_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_NLOAD_PUT_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_D_INVALIDATE_LD: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_D_DESTROY: |
| txApplyDestroyLocally(r, key, txState); |
| break; |
| case OP_L_INVALIDATE: |
| txApplyInvalidateLocally(r, key, Token.LOCAL_INVALID, didDestroy(), txState); |
| break; |
| case OP_PUT_LI: |
| txApplyPutLocally(r, getUpdateOperation(), key, Token.LOCAL_INVALID, |
| didDestroy(), txState); |
| break; |
| case OP_LLOAD_PUT_LI: |
| txApplyPutLocally(r, getUpdateOperation(), key, Token.LOCAL_INVALID, |
| didDestroy(), txState); |
| break; |
| case OP_NLOAD_PUT_LI: |
| txApplyPutLocally(r, getUpdateOperation(), key, Token.LOCAL_INVALID, |
| didDestroy(), txState); |
| break; |
| case OP_D_INVALIDATE: |
| txApplyInvalidateLocally(r, key, Token.INVALID, didDestroy(), txState); |
| break; |
| case OP_CREATE_LI: |
| txApplyPutLocally(r, getCreateOperation(), key, Token.LOCAL_INVALID, |
| didDestroy(), txState); |
| break; |
| case OP_LLOAD_CREATE_LI: |
| txApplyPutLocally(r, getCreateOperation(), key, Token.LOCAL_INVALID, |
| didDestroy(), txState); |
| break; |
| case OP_NLOAD_CREATE_LI: |
| txApplyPutLocally(r, getCreateOperation(), key, Token.LOCAL_INVALID, |
| didDestroy(), txState); |
| break; |
| case OP_CREATE: |
| txApplyPutLocally(r, getCreateOperation(), key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_SEARCH_CREATE: |
| txApplyPutLocally(r, Operation.SEARCH_CREATE, key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_LLOAD_CREATE: |
| txApplyPutLocally(r, Operation.LOCAL_LOAD_CREATE, key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_NLOAD_CREATE: |
| txApplyPutLocally(r, Operation.NET_LOAD_CREATE, key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_LOCAL_CREATE: |
| txApplyPutLocally(r, getCreateOperation(), key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_PUT: |
| txApplyPutLocally(r, getUpdateOperation(), key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_SEARCH_PUT: |
| txApplyPutLocally(r, Operation.SEARCH_UPDATE, key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_LLOAD_PUT: |
| txApplyPutLocally(r, Operation.LOCAL_LOAD_UPDATE, key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| case OP_NLOAD_PUT: |
| txApplyPutLocally(r, Operation.NET_LOAD_UPDATE, key, getPendingValue(), |
| didDestroy(), txState); |
| break; |
| default: |
| throw new IllegalStateException(LocalizedStrings.TXEntryState_UNHANDLED_OP_0.toLocalizedString(opToString())); |
| } |
| } |
| |
| /** |
| * @return returns {@link Operation#PUTALL_CREATE} if the operation is a result of bulk op, |
| * {@link Operation#CREATE} otherwise |
| */ |
| private Operation getCreateOperation() { |
| return isBulkOp() ? Operation.PUTALL_CREATE : Operation.CREATE; |
| } |
| |
| /** |
| * @return returns {@link Operation#PUTALL_UPDATE} if the operation is a result of bulk op, |
| * {@link Operation#UPDATE} otherwise |
| */ |
| private Operation getUpdateOperation() { |
| return isBulkOp() ? Operation.PUTALL_UPDATE : Operation.UPDATE; |
| } |
| |
| @Override |
| @Released(TX_ENTRY_STATE) |
| public void release() { |
| Object tmp = this.originalVersionId; |
| if (OffHeapHelper.release(tmp)) { |
| this.originalVersionId = null; // fix for bug 47900 |
| } |
| } |
| |
| private Operation getDestroyOperation() { |
| if (isOpLocalDestroy()) { |
| return Operation.LOCAL_DESTROY; |
| } else if (isBulkOp()) { |
| return Operation.REMOVEALL_DESTROY; |
| } else { |
| return Operation.DESTROY; |
| } |
| } |
| |
| /** |
| * Serializes this entry state to a data output stream for a far side consumer. |
| * Make sure this method is backwards compatible if changes are made. |
| * @param largeModCount |
| * true if modCount needs to be represented by an int; false if a |
| * byte is enough |
| * @param sendVersionTag |
| * true if versionTag should be sent to clients 7.0 and above |
| * @param sendShadowKey |
| * true if wan shadowKey should be sent to peers 7.0.1 and above |
| * |
| * @since 5.0 |
| */ |
| void toFarSideData(DataOutput out, boolean largeModCount, boolean sendVersionTag, boolean sendShadowKey) throws IOException |
| { |
| Operation operation = getFarSideOperation(); |
| out.writeByte(operation.ordinal); |
| if (largeModCount) { |
| out.writeInt(this.modSerialNum); |
| } |
| else { |
| out.writeByte(this.modSerialNum); |
| } |
| DataSerializer.writeObject(getCallbackArgument(),out); |
| DataSerializer.writeObject(getFilterRoutingInfo(),out); |
| if (sendVersionTag) { |
| DataSerializer.writeObject(getVersionTag(), out); |
| assert getVersionTag() != null |
| || !txRegionState.getRegion().concurrencyChecksEnabled |
| || txRegionState.getRegion().dataPolicy != DataPolicy.REPLICATE : "tag:"+getVersionTag()+" r:"+txRegionState.getRegion()+" op:"+opToString()+" key:"; |
| } |
| if (sendShadowKey) { |
| out.writeLong(this.tailKey); |
| } |
| out.writeInt(getFarSideEventOffset()); |
| if (!operation.isDestroy()) { |
| out.writeBoolean(didDistributedDestroy()); |
| if (!operation.isInvalidate()) { |
| boolean sendObject = Token.isInvalidOrRemoved(getPendingValue()); |
| sendObject = sendObject || getPendingValue() instanceof byte[]; |
| out.writeBoolean(sendObject); |
| if (sendObject) { |
| DataSerializer.writeObject(getPendingValue(), out); |
| } else { |
| DataSerializer.writeObjectAsByteArray(getPendingValue(), out); |
| } |
| } |
| } |
| } |
| |
| public FilterRoutingInfo getFilterRoutingInfo() { |
| return filterRoutingInfo; |
| } |
| |
| /** |
| * Creates a queued op and returns it for this entry on the far side of the |
| * tx. |
| * |
| * @param key |
| * the key for this op |
| * @since 5.0 |
| */ |
| QueuedOperation toFarSideQueuedOp(Object key) |
| { |
| Operation operation = getFarSideOperation(); |
| byte[] valueBytes = null; |
| byte deserializationPolicy = DistributedCacheOperation.DESERIALIZATION_POLICY_NONE; |
| if (!operation.isDestroy() && !operation.isInvalidate()) { |
| Object v = getPendingValue(); |
| if (v == null || v instanceof byte[]) { |
| valueBytes = (byte[])v; |
| } |
| else { |
| // this value shouldn't be a Delta |
| Assert.assertTrue(!(v instanceof Delta)); |
| |
| deserializationPolicy = DistributedCacheOperation.DESERIALIZATION_POLICY_LAZY; |
| valueBytes = EntryEventImpl.serialize(v); |
| } |
| } |
| return new QueuedOperation(operation, key, valueBytes, null, |
| deserializationPolicy, null); |
| } |
| |
| void cleanup(LocalRegion r) |
| { |
| if (this.refCountEntry != null) { |
| r.txDecRefCount(refCountEntry); |
| } |
| close(); |
| } |
| |
| /** |
| * Returns the sort key for this entry. |
| */ |
| int getSortValue() { |
| return this.modSerialNum; |
| } |
| |
| /** |
| * Just like an EntryEventImpl but also has access to TxEntryState to make it |
| * Comparable |
| * |
| * @since 5.0 |
| */ |
| public final class TxEntryEventImpl extends EntryEventImpl implements Comparable |
| { |
| /** |
| * Creates a local tx entry event |
| */ |
| TxEntryEventImpl(LocalRegion r, Object key) { |
| //TODO:ASIF :Check if the eventID should be created. Currently not |
| // creating it |
| super(r, getNearSideOperation(), key, |
| getNearSidePendingValue(),TXEntryState.this.getCallbackArgument(), false, r.getMyId() |
| , true/* generateCallbacks */, true /*initializeId*/); |
| } |
| |
| /** |
| * Returns the value to use to sort us |
| */ |
| private int getSortValue() |
| { |
| return TXEntryState.this.getSortValue(); |
| } |
| |
| public int compareTo(Object o) |
| { |
| TxEntryEventImpl other = (TxEntryEventImpl)o; |
| return getSortValue() - other.getSortValue(); |
| } |
| |
| @Override |
| public boolean equals(Object o) |
| { |
| if (o == null || !(o instanceof TxEntryEventImpl)) return false; |
| return compareTo(o) == 0; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return getSortValue(); |
| } |
| } |
| |
| |
| // Asif:Add for sql fabric as it has to plug in its own TXEntry object |
| private final static TXEntryStateFactory factory = new TXEntryStateFactory() { |
| |
| public TXEntryState createEntry() |
| { |
| return new TXEntryState(); |
| } |
| |
| public TXEntryState createEntry(RegionEntry re, Object vId, Object pendingValue, Object entryKey,TXRegionState txrs,boolean isDistributed) |
| { |
| return new TXEntryState(re, vId, pendingValue, txrs, isDistributed); |
| } |
| |
| }; |
| |
| public static TXEntryStateFactory getFactory() |
| { |
| return factory; |
| } |
| |
| public void setFilterRoutingInfo(FilterRoutingInfo fri) { |
| this.filterRoutingInfo = fri; |
| } |
| |
| public Set<InternalDistributedMember> getAdjunctRecipients() { |
| return adjunctRecipients; |
| } |
| |
| public void setAdjunctRecipients(Set<InternalDistributedMember> members) { |
| this.adjunctRecipients = members; |
| } |
| |
| public VersionTag getVersionTag() { |
| return versionTag; |
| } |
| |
| public void setVersionTag(VersionTag versionTag) { |
| this.versionTag = versionTag; |
| } |
| |
| public VersionTag getRemoteVersionTag() { |
| return remoteVersionTag; |
| } |
| |
| public void setRemoteVersionTag(VersionTag remoteVersionTag) { |
| this.remoteVersionTag = remoteVersionTag; |
| } |
| |
| public long getTailKey() { |
| return tailKey; |
| } |
| |
| public void setTailKey(long tailKey) { |
| this.tailKey = tailKey; |
| } |
| |
| public void close() { |
| release(); |
| } |
| |
| public void setNextRegionVersion(long v) { |
| this.nextRegionVersion = v; |
| } |
| |
| public long getNextRegionVersion() { |
| return this.nextRegionVersion; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder str = new StringBuilder(); |
| str.append("{").append(super.toString()).append(" "); |
| str.append(this.op); |
| str.append("}"); |
| return str.toString(); |
| } |
| |
| public DistTxThinEntryState getDistTxEntryStates() { |
| return this.distTxThinEntryState; |
| } |
| |
| public void setDistTxEntryStates(DistTxThinEntryState thinEntryState) { |
| this.distTxThinEntryState = thinEntryState; |
| } |
| |
| /** |
| * For Distributed Transaction Usage |
| * |
| * This class is used to bring relevant information for DistTxEntryEvent from |
| * primary, after end of precommit. Same information are sent to all |
| * replicates during commit. |
| * |
| * Whereas @see DistTxEntryEvent is used forstoring entry event information on |
| * TxCordinator and carry same to replicates. |
| * |
| * @author vivekb |
| */ |
| public static class DistTxThinEntryState implements DataSerializableFixedID { |
| |
| private long regionVersion =1L; |
| private long tailKey = -1L; |
| private String memberID; |
| |
| // For Serialization |
| public DistTxThinEntryState() { |
| } |
| |
| @Override |
| public Version[] getSerializationVersions() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public int getDSFID() { |
| return DIST_TX_THIN_ENTRY_STATE; |
| } |
| |
| @Override |
| public void toData(DataOutput out) throws IOException { |
| DataSerializer.writeLong(this.regionVersion, out); |
| DataSerializer.writeLong(this.tailKey, out); |
| } |
| |
| @Override |
| public void fromData(DataInput in) throws IOException, ClassNotFoundException { |
| this.regionVersion = DataSerializer.readLong(in); |
| this.tailKey = DataSerializer.readLong(in); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder buf = new StringBuilder(); |
| buf.append("DistTxThinEntryState: "); |
| buf.append(" ,regionVersion=" + this.regionVersion); |
| buf.append(" ,tailKey=" + this.tailKey); |
| buf.append(" ,memberID=" + this.memberID); |
| return buf.toString(); |
| } |
| |
| public long getRegionVersion() { |
| return this.regionVersion; |
| } |
| |
| public void setRegionVersion(long regionVersion) { |
| this.regionVersion = regionVersion; |
| } |
| |
| public long getTailKey() { |
| return this.tailKey; |
| } |
| |
| public void setTailKey(long tailKey) { |
| this.tailKey = tailKey; |
| } |
| |
| public String getMemberID() { |
| return memberID; |
| } |
| |
| public void setMemberID(String memberID) { |
| this.memberID = memberID; |
| } |
| } |
| } |