blob: 3ac381179446faf4b69af40e46f90a4a0a419762 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.cache.tier.sockets;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.logging.log4j.Logger;
import org.apache.geode.DataSerializer;
import org.apache.geode.GemFireIOException;
import org.apache.geode.InternalGemFireError;
import org.apache.geode.annotations.VisibleForTesting;
import org.apache.geode.cache.query.internal.cq.InternalCqQuery;
import org.apache.geode.cache.util.ObjectSizer;
import org.apache.geode.internal.InternalDataSerializer;
import org.apache.geode.internal.Sendable;
import org.apache.geode.internal.cache.CachedDeserializableFactory;
import org.apache.geode.internal.cache.EntryEventImpl.NewValueImporter;
import org.apache.geode.internal.cache.EnumListenerEvent;
import org.apache.geode.internal.cache.EventID;
import org.apache.geode.internal.cache.LocalRegion;
import org.apache.geode.internal.cache.WrappedCallbackArgument;
import org.apache.geode.internal.cache.ha.HAContainerRegion;
import org.apache.geode.internal.cache.tier.MessageType;
import org.apache.geode.internal.cache.versions.VersionTag;
import org.apache.geode.internal.concurrent.ConcurrentHashSet;
import org.apache.geode.internal.serialization.ByteArrayDataInput;
import org.apache.geode.internal.serialization.DSCODE;
import org.apache.geode.internal.serialization.DeserializationContext;
import org.apache.geode.internal.serialization.SerializationContext;
import org.apache.geode.internal.serialization.Version;
import org.apache.geode.internal.size.Sizeable;
import org.apache.geode.logging.internal.log4j.api.LogService;
/**
* Class <code>ClientUpdateMessageImpl</code> is a message representing a cache operation that is
* sent from a server to an interested client.
*
*
* @since GemFire 4.2
*/
public class ClientUpdateMessageImpl implements ClientUpdateMessage, Sizeable, NewValueImporter {
private static final long serialVersionUID = 7037106666445312400L;
private static final Logger logger = LogService.getLogger();
/**
* The operation performed (e.g. AFTER_CREATE, AFTER_UPDATE, AFTER_DESTROY, AFTER_INVALIDATE,
* AFTER_REGION_DESTROY)
*/
protected EnumListenerEvent _operation;
/**
* The name of the <code>Region</code> that was updated
*/
private String _regionName;
/**
* The key that was updated
*/
private Object _keyOfInterest;
/**
* The new value
*/
private Object _value;
/**
* Whether the value is a serialized object or just a byte[]
*/
protected byte _valueIsObject;
/**
* The callback argument
*/
protected Object _callbackArgument;
/**
* The membership id of the originator of the event
*/
protected ClientProxyMembershipID _membershipId;
/**
* The event id of the event
*/
protected EventID _eventIdentifier;
private boolean _shouldConflate = false;
/**
* To determine if this client message is part of InterestList.
*/
private volatile boolean _isInterestListPassed;
/**
* To determine if this client message is part of CQs.
*/
private volatile boolean _hasCqs = false;
/**
* Map containing clientId and the cqs satisfied for the client.
*/
private ClientCqConcurrentMap _clientCqs = null;
/**
* Client list satisfying the interestList who want values
*/
private volatile Set<ClientProxyMembershipID> _clientInterestList;
/**
* Client list satisfying the interestList who want invalidations
*/
private volatile Set<ClientProxyMembershipID> _clientInterestListInv;
/**
* To determine if the message is result of netLoad. If its net load the message is not delivered
* to the client that has requested the load.
*/
private transient boolean _isNetLoad = false;
/**
* Represents the changed bytes of this event's _value.
*
* @since GemFire 6.1
*/
private byte[] deltaBytes = null;
private VersionTag versionTag;
/* added up all constants and form single value */
private static final int CONSTANT_MEMORY_OVERHEAD;
/**
* Constructor.
*
* @param operation The operation performed (e.g. AFTER_CREATE, AFTER_UPDATE, AFTER_DESTROY,
* AFTER_INVALIDATE, AFTER_REGION_DESTROY)
* @param region The <code>Region</code> that was updated
* @param keyOfInterest The key that was updated
* @param value The new value
* @param valueIsObject false if value is an actual byte[] that isn't serialized info
* @param callbackArgument The callback argument
* @param memberId membership id of the originator of the event
*/
public ClientUpdateMessageImpl(EnumListenerEvent operation, LocalRegion region,
Object keyOfInterest, Object value, byte valueIsObject, Object callbackArgument,
ClientProxyMembershipID memberId, EventID eventIdentifier) {
this(operation, region, keyOfInterest, value, null, valueIsObject, callbackArgument, memberId,
eventIdentifier, null);
}
public ClientUpdateMessageImpl(EnumListenerEvent operation, LocalRegion region,
Object keyOfInterest, Object value, byte[] delta, byte valueIsObject, Object callbackArgument,
ClientProxyMembershipID memberId, EventID eventIdentifier, VersionTag versionTag) {
this._operation = operation;
this._regionName = region.getFullPath();
this._keyOfInterest = keyOfInterest;
this._value = value;
this._valueIsObject = valueIsObject;
this._callbackArgument = callbackArgument;
this._membershipId = memberId;
this._eventIdentifier = eventIdentifier;
this._shouldConflate = (isUpdate() && region.getEnableConflation());
this.deltaBytes = delta;
this.versionTag = versionTag;
}
/**
* Constructor used by ClientInstantiatorMessage
*
* @param operation The operation performed (e.g. AFTER_CREATE, AFTER_UPDATE, AFTER_DESTROY,
* AFTER_INVALIDATE, AFTER_REGION_DESTROY)
* @param memberId membership id of the originator of the event
* @param eventIdentifier EventID of this message
*/
protected ClientUpdateMessageImpl(EnumListenerEvent operation, ClientProxyMembershipID memberId,
EventID eventIdentifier) {
this._operation = operation;
this._membershipId = memberId;
this._eventIdentifier = eventIdentifier;
}
/**
* default constructor
*
*/
public ClientUpdateMessageImpl() {
}
@Override
public String getRegionName() {
return this._regionName;
}
@Override
public Object getKeyOfInterest() {
return this._keyOfInterest;
}
@Override
public EnumListenerEvent getOperation() {
return this._operation;
}
@Override
public Object getValue() {
return this._value;
}
@Override
public boolean valueIsObject() {
return (this._valueIsObject == 0x01);
}
/**
* @return the callback argument
*/
public Object getCallbackArgument() {
return this._callbackArgument;
}
/// 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
*/
@Override
public boolean shouldBeConflated() {
// If the message is an update, it may be conflatable. If it is a
// create, destroy, invalidate or destroy-region, it is not conflatable.
// Only updates are conflated. If it is an update, then verify that
// the region has conflation enabled.
return this._shouldConflate;
}
@Override
public String getRegionToConflate() {
return this._regionName;
}
@Override
public Object getKeyToConflate() {
return this._keyOfInterest;
}
@Override
public Object getValueToConflate() {
return this._value;
}
@Override
public void setLatestValue(Object value) {
// does this also need to set _valueIsObject
this._value = value;
}
/// End Conflatable interface methods ///
@Override
public ClientProxyMembershipID getMembershipId() {
return this._membershipId;
}
/**
* Returns the unqiue event eventifier for event corresponding to this message.
*
* @return the unqiue event eventifier for event corresponding to this message.
*/
@Override
public EventID getEventId() {
return this._eventIdentifier;
}
@Override
public VersionTag getVersionTag() {
return this.versionTag;
}
@Override
public boolean isCreate() {
return this._operation == EnumListenerEvent.AFTER_CREATE;
}
@Override
public boolean isUpdate() {
return this._operation == EnumListenerEvent.AFTER_UPDATE;
}
@Override
public boolean isDestroy() {
return this._operation == EnumListenerEvent.AFTER_DESTROY;
}
@Override
public boolean isInvalidate() {
return this._operation == EnumListenerEvent.AFTER_INVALIDATE;
}
@Override
public boolean isDestroyRegion() {
return this._operation == EnumListenerEvent.AFTER_REGION_DESTROY;
}
@Override
public boolean isClearRegion() {
return this._operation == EnumListenerEvent.AFTER_REGION_CLEAR;
}
public boolean isInvalidateRegion() {
return this._operation == EnumListenerEvent.AFTER_REGION_INVALIDATE;
}
public boolean isClientCompatible() {
return false;
}
@Override
public Message getMessage(CacheClientProxy proxy, boolean notify) throws IOException {
// the MessageDispatcher uses getMessage(CacheClientProxy, byte[]) for this class
throw new Error("ClientUpdateMessage.getMessage(proxy) should not be invoked");
}
/**
* Returns a <code>Message</code> generated from the fields of this
* <code>ClientUpdateMessage</code>.
*
* @param latestValue Object containing the latest value to use. This could be the original value
* if conflation is not enabled, or it could be a conflated value if conflation is enabled.
* @return a <code>Message</code> generated from the fields of this
* <code>ClientUpdateMessage</code>
* @see org.apache.geode.internal.cache.tier.sockets.Message
*/
protected Message getMessage(CacheClientProxy proxy, byte[] latestValue) throws IOException {
Version clientVersion = proxy.getVersion();
byte[] serializedValue = null;
Message message = null;
boolean conflation = false;
conflation = (proxy.clientConflation == Handshake.CONFLATION_ON)
|| (proxy.clientConflation == Handshake.CONFLATION_DEFAULT && this.shouldBeConflated());
if (latestValue != null) {
serializedValue = latestValue;
} else {
/**
* This means latestValue is instance of Delta, and its delta has already been extracted and
* put into deltaBytes. We serialize the value.
*/
if (this.deltaBytes == null || isCreate()) {
// Delta could not be extracted. We would need to send full value.
// OR A CREATE operation has a value which has delta. But we send full value for CREATE.
// So serialize it.
this._value = serializedValue = CacheServerHelper.serialize(latestValue);
}
}
if (clientVersion.compareTo(Version.GFE_70) >= 0) {
message = getGFE70Message(proxy, serializedValue, conflation, clientVersion);
} else if (clientVersion.compareTo(Version.GFE_65) >= 0) {
message = getGFE65Message(proxy, serializedValue, conflation, clientVersion);
} else if (clientVersion.compareTo(Version.GFE_61) >= 0) {
message = getGFE61Message(proxy, serializedValue, conflation, clientVersion);
} else if (clientVersion.compareTo(Version.GFE_57) >= 0) {
message = getGFEMessage(proxy.getProxyID(), latestValue, clientVersion);
} else {
throw new IOException(
"Unsupported client version for server-to-client message creation: " + clientVersion);
}
return message;
}
protected Message getGFEMessage(ClientProxyMembershipID proxyId, byte[] latestValue,
Version clientVersion) throws IOException {
Message message = null;
// Add CQ info.
int cqMsgParts = 0;
boolean clientHasCq = this._hasCqs && (this.getCqs(proxyId) != null);
if (clientHasCq) {
cqMsgParts = (this.getCqs(proxyId).length * 2) + 1;
}
if (isCreate() || isUpdate()) {
// Create or update event
if (this._clientInterestListInv != null && this._clientInterestListInv.contains(proxyId)) {
// Notify all - do not send the value
message = new Message(6, clientVersion);
message.setMessageType(MessageType.LOCAL_INVALIDATE);
message.addStringPart(this._regionName, true);
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.FALSE);
} else {
// Notify by subscription - send the value
message = new Message(7 + cqMsgParts, clientVersion);
// Set message type
if (isCreate()) {
message.setMessageType(MessageType.LOCAL_CREATE);
} else {
message.setMessageType(MessageType.LOCAL_UPDATE);
}
message.addStringPart(this._regionName, true);
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
message.addRawPart(latestValue, (this._valueIsObject == 0x01));
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
}
} else if (isDestroy() || isInvalidate()) {
message = new Message(6 + cqMsgParts, clientVersion);
if (isDestroy()) {
message.setMessageType(MessageType.LOCAL_DESTROY);
} else {
message.setMessageType(MessageType.LOCAL_INVALIDATE);
}
message.addStringPart(this._regionName, true);
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isDestroyRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_DESTROY_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isClearRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.CLEAR_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isInvalidateRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.INVALIDATE_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else {
throw new InternalGemFireError("Don't know what kind of message");
}
message.setTransactionId(0);
// Add the EventId since 5.1 (used to prevent duplicate events
// received on the client side after a failover)
message.addObjPart(this._eventIdentifier);
return message;
}
protected Message getGFE61Message(CacheClientProxy proxy, byte[] latestValue, boolean conflation,
Version clientVersion) throws IOException {
Message message = null;
ClientProxyMembershipID proxyId = proxy.getProxyID();
// Add CQ info.
int cqMsgParts = 0;
boolean clientHasCq = this._hasCqs && (this.getCqs(proxyId) != null);
if (clientHasCq) {
cqMsgParts = (this.getCqs(proxyId).length * 2) + 1;
}
if (isCreate() || isUpdate()) {
// Create or update event
if (this._clientInterestListInv != null && this._clientInterestListInv.contains(proxyId)) {
// Notify all - do not send the value
message = new Message(6, clientVersion);
message.setMessageType(MessageType.LOCAL_INVALIDATE);
// Add the region name
message.addStringPart(this._regionName, true);
// Add the key
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
// Add the callback argument
message.addObjPart(this._callbackArgument);
// Add interestlist status.
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
// Add CQ status.
message.addObjPart(Boolean.FALSE);
} else {
boolean isClientInterested = isClientInterested(proxyId);
// Notify by subscription - send the value
message = new Message(8 + cqMsgParts, clientVersion);
// Set message type
if (isCreate()) {
message.setMessageType(MessageType.LOCAL_CREATE);
// Add the region name
message.addStringPart(this._regionName, true);
// Add the key
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(Boolean.FALSE); // NO delta
// Add the value (which has already been serialized)
message.addRawPart(latestValue, (this._valueIsObject == 0x01));
} else {
message.setMessageType(MessageType.LOCAL_UPDATE);
// Add the region name
message.addStringPart(this._regionName, true);
// Add the key
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
if (this.deltaBytes != null && !conflation && !proxy.isMarkerEnqueued()
&& !proxy.getRegionsWithEmptyDataPolicy().containsKey(_regionName)) {
message.addObjPart(Boolean.TRUE);
message.addBytesPart(this.deltaBytes);
proxy.getStatistics().incDeltaMessagesSent();
} else {
message.addObjPart(Boolean.FALSE);
byte[] l = latestValue;
if (l == null) {
if (!(this._value instanceof byte[])) {
this._value = CacheServerHelper.serialize(this._value);
}
l = (byte[]) this._value;
}
// Add the value (which has already been serialized)
message.addRawPart(l, (this._valueIsObject == 0x01));
}
}
// Add the callback argument
message.addObjPart(this._callbackArgument);
// Add interest list status.
message.addObjPart(Boolean.valueOf(isClientInterested));
// Add CQ status.
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
}
} else if (isDestroy() || isInvalidate()) {
// Destroy or invalidate event
message = new Message(6 + cqMsgParts, clientVersion);
if (isDestroy()) {
message.setMessageType(MessageType.LOCAL_DESTROY);
} else {
message.setMessageType(MessageType.LOCAL_INVALIDATE);
}
message.addStringPart(this._regionName, true);
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated later
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isDestroyRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_DESTROY_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isClearRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.CLEAR_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isInvalidateRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.INVALIDATE_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else {
throw new InternalGemFireError("Don't know what kind of message");
}
message.setTransactionId(0);
// Add the EventId since 5.1 (used to prevent duplicate events
// received on the client side after a failover)
message.addObjPart(this._eventIdentifier);
return message;
}
protected Message getGFE65Message(CacheClientProxy proxy, byte[] p_latestValue,
boolean conflation, Version clientVersion) throws IOException {
byte[] latestValue = p_latestValue;
Message message = null;
ClientProxyMembershipID proxyId = proxy.getProxyID();
// Add CQ info.
int cqMsgParts = 0;
boolean clientHasCq = this._hasCqs && (this.getCqs(proxyId) != null);
if (clientHasCq) {
cqMsgParts = (this.getCqs(proxyId).length * 2) + 1;
}
if (isCreate() || isUpdate()) {
// Create or update event
if (this._clientInterestListInv != null && this._clientInterestListInv.contains(proxyId)) {
// Client is registered for invalidates.
if (cqMsgParts > 0) {
cqMsgParts++; // To store base operation type for CQ.
}
message = new Message(6 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_INVALIDATE);
// Add the region name
message.addStringPart(this._regionName, true);
// Add the key
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
} else {
// Notify by subscription - send the value
message = new Message(8 + cqMsgParts, clientVersion);
// Set message type
if (isCreate()) {
message.setMessageType(MessageType.LOCAL_CREATE);
// Add the region name
message.addStringPart(this._regionName, true);
// Add the key
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(Boolean.FALSE); // NO delta
// Add the value (which has already been serialized)
message.addRawPart(latestValue, (this._valueIsObject == 0x01));
} else {
message.setMessageType(MessageType.LOCAL_UPDATE);
// Add the region name
message.addStringPart(this._regionName, true);
// Add the key
// Currently serializing the key here instead of when the message
// is put in the queue so that it can be conflated it later
message.addStringOrObjPart(this._keyOfInterest);
if (this.deltaBytes != null && !conflation && !proxy.isMarkerEnqueued()
&& !proxy.getRegionsWithEmptyDataPolicy().containsKey(_regionName)) {
message.addObjPart(Boolean.TRUE);
message.addBytesPart(this.deltaBytes);
proxy.getStatistics().incDeltaMessagesSent();
} else {
message.addObjPart(Boolean.FALSE);
if (latestValue == null) {
if (!(this._value instanceof byte[])) {
this._value = CacheServerHelper.serialize(this._value);
}
latestValue = (byte[]) this._value;
}
// Add the value (which has already been serialized)
message.addRawPart(latestValue, (this._valueIsObject == 0x01));
}
}
}
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
if (message.getMessageType() == MessageType.LOCAL_INVALIDATE) {
// in case of invalidate, set the region operation type.
message.addIntPart(isCreate() ? MessageType.LOCAL_CREATE : MessageType.LOCAL_UPDATE);
}
this.addCqsToMessage(proxyId, message);
}
} else if (isDestroy() || isInvalidate()) {
if (isDestroy()) {
message = new Message(6 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_DESTROY);
} else {
if (clientHasCq) {
cqMsgParts++;/* To store the region operation for CQ */
}
message = new Message(6 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_INVALIDATE);
}
message.addStringPart(this._regionName, true);
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
if (isInvalidate()) {
// This is to take care when invalidate message is getting sent
// to the Client. See the code for create/update operation.
message.addIntPart(MessageType.LOCAL_INVALIDATE);
}
this.addCqsToMessage(proxyId, message);
}
} else if (isDestroyRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_DESTROY_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isClearRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.CLEAR_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isInvalidateRegion()) {
message = new Message(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.INVALIDATE_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
// Add CQ status.
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else {
throw new InternalGemFireError("Don't know what kind of message");
}
message.setTransactionId(0);
// Add the EventId since 5.1 (used to prevent duplicate events
// received on the client side after a failover)
message.addObjPart(this._eventIdentifier);
return message;
}
protected Message getGFE70Message(CacheClientProxy proxy, byte[] p_latestValue,
boolean conflation, Version clientVersion) throws IOException {
byte[] latestValue = p_latestValue;
Message message = null;
ClientProxyMembershipID proxyId = proxy.getProxyID();
// Add CQ info.
int cqMsgParts = 0;
boolean clientHasCq = this._hasCqs && (this.getCqs(proxyId) != null);
if (clientHasCq) {
cqMsgParts = (this.getCqs(proxyId).length * 2) + 1;
}
if (isCreate() || isUpdate()) {
// Create or update event
if (this._clientInterestListInv != null && this._clientInterestListInv.contains(proxyId)) {
// Client is registered for invalidates.
if (cqMsgParts > 0) {
cqMsgParts++; // To store base operation type for CQ.
}
message = getMessage(7 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_INVALIDATE);
message.addStringPart(this._regionName, true);
message.addStringOrObjPart(this._keyOfInterest);
} else {
// Notify by subscription - send the value
message = getMessage(9 + cqMsgParts, clientVersion);
if (isCreate()) {
message.setMessageType(MessageType.LOCAL_CREATE);
message.addStringPart(this._regionName, true);
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(Boolean.FALSE); // NO delta
// Add the value (which has already been serialized)
message.addRawPart(latestValue, (this._valueIsObject == 0x01));
} else {
message.setMessageType(MessageType.LOCAL_UPDATE);
message.addStringPart(this._regionName, true);
message.addStringOrObjPart(this._keyOfInterest);
if (this.deltaBytes != null && !conflation && !proxy.isMarkerEnqueued()
&& !proxy.getRegionsWithEmptyDataPolicy().containsKey(_regionName)) {
message.addObjPart(Boolean.TRUE);
message.addBytesPart(this.deltaBytes);
proxy.getStatistics().incDeltaMessagesSent();
} else {
message.addObjPart(Boolean.FALSE);
if (latestValue == null) {
if (!(this._value instanceof byte[])) {
this._value = CacheServerHelper.serialize(this._value);
}
latestValue = (byte[]) this._value;
}
// Add the value (which has already been serialized)
message.addRawPart(latestValue, (this._valueIsObject == 0x01));
}
}
}
message.addObjPart(this._callbackArgument);
if (this.versionTag != null) {
this.versionTag.setCanonicalIDs(proxy.getCache().getDistributionManager());
}
message.addObjPart(this.versionTag);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
if (message.getMessageType() == MessageType.LOCAL_INVALIDATE) {
// in case of invalidate, set the region operation type.
message.addIntPart(isCreate() ? MessageType.LOCAL_CREATE : MessageType.LOCAL_UPDATE);
}
this.addCqsToMessage(proxyId, message);
}
} else if (isDestroy() || isInvalidate()) {
if (isDestroy()) {
message = getMessage(7 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_DESTROY);
} else {
if (clientHasCq) {
cqMsgParts++;/* To store the region operation for CQ */
}
message = getMessage(7 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_INVALIDATE);
}
message.addStringPart(this._regionName, true);
message.addStringOrObjPart(this._keyOfInterest);
message.addObjPart(this._callbackArgument);
message.addObjPart(this.versionTag);
message.addObjPart(Boolean.valueOf(isClientInterested(proxyId)));
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
if (isInvalidate()) {
// This is to take care when invalidate message is getting sent
// to the Client. See the code for create/update operation.
message.addIntPart(MessageType.LOCAL_INVALIDATE);
}
this.addCqsToMessage(proxyId, message);
}
} else if (isDestroyRegion()) {
message = getMessage(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.LOCAL_DESTROY_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isClearRegion()) {
message = getMessage(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.CLEAR_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else if (isInvalidateRegion()) {
message = getMessage(4 + cqMsgParts, clientVersion);
message.setMessageType(MessageType.INVALIDATE_REGION);
message.addStringPart(this._regionName, true);
message.addObjPart(this._callbackArgument);
// Add CQ status.
message.addObjPart(Boolean.valueOf(clientHasCq));
if (clientHasCq) {
this.addCqsToMessage(proxyId, message);
}
} else {
throw new InternalGemFireError("Don't know what kind of message");
}
message.setTransactionId(0);
// Add the EventId since 5.1 (used to prevent duplicate events
// received on the client side after a failover)
message.addObjPart(this._eventIdentifier);
return message;
}
private static final ThreadLocal<Map<Integer, Message>> CACHED_MESSAGES =
new ThreadLocal<Map<Integer, Message>>() {
@Override
protected Map<Integer, Message> initialValue() {
return new HashMap<Integer, Message>();
};
};
private Message getMessage(int numParts, Version clientVersion) {
Message m = CACHED_MESSAGES.get().get(numParts);
if (m == null) {
m = new Message(numParts, Version.CURRENT);
CACHED_MESSAGES.get().put(numParts, m);
}
m.clearParts();
m.setVersion(clientVersion);
return m;
}
/**
* @return boolean true if the event is due to net load.
*/
@Override
public boolean isNetLoad() {
return this._isNetLoad;
}
/**
* @param isNetLoad boolean true if the event is due to net load.
*/
@Override
public void setIsNetLoad(boolean isNetLoad) {
this._isNetLoad = isNetLoad;
}
/**
* @return boolean true if cq info is present for the given proxy.
*/
@Override
public boolean hasCqs(ClientProxyMembershipID clientId) {
if (this._clientCqs != null) {
CqNameToOp cqs = this._clientCqs.get(clientId);
if (cqs != null && !cqs.isEmpty()) {
return true;
}
}
return false;
}
/**
* @return boolean true if cq info is present.
*/
@Override
public boolean hasCqs() {
return this._hasCqs;
}
/**
* Returns the cqs for the given client.
*
*/
public String[] getCqs(ClientProxyMembershipID clientId) {
String[] cqNames = null;
if (this._clientCqs != null) {
CqNameToOp cqs = this._clientCqs.get(clientId);
if (cqs != null && !cqs.isEmpty()) {
cqNames = cqs.getNames();
}
}
return cqNames;
}
public ClientCqConcurrentMap getClientCqs() {
return this._clientCqs;
}
/**
* Add cqs for the given client.
*
*/
public void addClientCqs(ClientProxyMembershipID clientId, CqNameToOp filteredCqs) {
if (this._clientCqs == null) {
this._clientCqs = new ClientCqConcurrentMap();
this._hasCqs = true;
}
this._clientCqs.put(clientId, filteredCqs);
}
public void addClientCq(ClientProxyMembershipID clientId, String cqName, Integer cqEvent) {
if (this._clientCqs == null) {
this._clientCqs = new ClientCqConcurrentMap();
this._hasCqs = true;
}
CqNameToOp cqInfo = this._clientCqs.get(clientId);
if (cqInfo == null) {
cqInfo = new CqNameToOpSingleEntry(cqName, cqEvent);
this._clientCqs.put(clientId, cqInfo);
} else if (!cqInfo.isFull()) {
cqInfo.add(cqName, cqEvent);
} else {
cqInfo = new CqNameToOpHashMap((CqNameToOpSingleEntry) cqInfo);
cqInfo.add(cqName, cqEvent);
this._clientCqs.put(clientId, cqInfo);
}
}
private void addCqsToMessage(ClientProxyMembershipID proxyId, Message message) {
if (this._clientCqs != null) {
CqNameToOp cqs = this._clientCqs.get(proxyId);
if (cqs != null) {
message.addIntPart(cqs.size() * 2);
cqs.addToMessage(message);
}
}
}
public void removeClientCq(ClientProxyMembershipID clientId, InternalCqQuery cqToClose) {
CqNameToOp cqs = getClientCq(clientId);
if (cqs != null) {
cqs.delete(cqToClose.getName());
// remove clientId key if no more cqs exist for this clientId
if (cqs.isEmpty()) {
this._clientCqs.remove(clientId);
}
}
}
/**
* Set the region name that was updated.
*/
@Override
public void setRegionName(String regionName) {
this._regionName = regionName;
}
/**
* @see HAEventWrapper#fromData(DataInput)
* @see HAContainerRegion#get(Object)
*/
public void setEventIdentifier(EventID eventId) {
if (this._eventIdentifier == null) {
this._eventIdentifier = eventId;
}
}
/**
* @see HAEventWrapper#fromData(DataInput)
* @see HAContainerRegion#get(Object)
*/
public void setClientCqs(ClientCqConcurrentMap clientCqs) {
if (this._clientCqs == null) {
this._clientCqs = clientCqs;
}
}
void addClientInterestList(Set<ClientProxyMembershipID> clientIds, boolean receiveValues) {
if (receiveValues) {
if (this._clientInterestList == null) {
this._clientInterestList = clientIds;
} else {
this._clientInterestList.addAll(clientIds);
}
} else {
if (this._clientInterestListInv == null) {
this._clientInterestListInv = clientIds;
} else {
this._clientInterestListInv.addAll(clientIds);
}
}
}
public void addClientInterestList(ClientProxyMembershipID clientId, boolean receiveValues) {
// This happens under synchronization on HAContainer.
if (receiveValues) {
if (this._clientInterestList == null) {
this._clientInterestList = new ConcurrentHashSet<>();
}
this._clientInterestList.add(clientId);
} else {
if (this._clientInterestListInv == null) {
this._clientInterestListInv = new ConcurrentHashSet<>();
}
this._clientInterestListInv.add(clientId);
}
}
@Override
public boolean isClientInterested(ClientProxyMembershipID clientId) {
return (this._clientInterestList != null && this._clientInterestList.contains(clientId))
|| (this._clientInterestListInv != null && this._clientInterestListInv.contains(clientId));
}
public boolean isClientInterestedInUpdates(ClientProxyMembershipID clientId) {
return (this._clientInterestList != null && this._clientInterestList.contains(clientId));
}
public boolean isClientInterestedInInvalidates(ClientProxyMembershipID clientId) {
return (this._clientInterestListInv != null && this._clientInterestListInv.contains(clientId));
}
@VisibleForTesting
boolean hasClientsInterestedInUpdates() {
return this._clientInterestList != null;
}
@VisibleForTesting
boolean hasClientsInterestedInInvalidates() {
return this._clientInterestListInv != null;
}
protected Object deserialize(byte[] serializedBytes) {
Object deserializedObject = serializedBytes;
// This is a debugging method so ignore all exceptions like
// ClassNotFoundException
try {
ByteArrayDataInput dis = new ByteArrayDataInput(serializedBytes);
deserializedObject = DataSerializer.readObject(dis);
} catch (Exception e) {
}
return deserializedObject;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("ClientUpdateMessageImpl[").append("op=").append(this._operation)
.append(";region=").append(this._regionName).append(";key=").append(this._keyOfInterest);
if (logger.isTraceEnabled()) {
buffer.append(";value=").append(
(this._value instanceof byte[]) ? deserialize((byte[]) this._value) : this._value);
}
buffer.append(";isObject=").append(_valueIsObject).append(";cbArg=")
.append(this._callbackArgument).append(";memberId=").append(this._membershipId)
.append(";eventId=").append(_eventIdentifier).append(";shouldConflate=")
.append(_shouldConflate).append(";versionTag=").append(this.versionTag).append(";hasCqs=")
.append(this._hasCqs)
// skip _logger :-)
.append("]");
return buffer.toString();
}
@Override
public int getDSFID() {
return CLIENT_UPDATE_MESSAGE;
}
@Override
public void toData(DataOutput out,
SerializationContext context) throws IOException {
out.writeByte(_operation.getEventCode());
DataSerializer.writeString(_regionName, out);
DataSerializer.writeObject(_keyOfInterest, out);
if (_value instanceof byte[]) {
DataSerializer.writeByteArray((byte[]) _value, out);
} else {
DataSerializer.writeByteArray(CacheServerHelper.serialize(_value), out);
}
out.writeByte(_valueIsObject);
DataSerializer.writeObject(_membershipId, out);
out.writeBoolean(_shouldConflate);
out.writeBoolean(_isInterestListPassed);
DataSerializer.writeByteArray(this.deltaBytes, out);
out.writeBoolean(_hasCqs);
DataSerializer.writeObject(_callbackArgument, out);
HashSet<ClientProxyMembershipID> clientInterestListSnapshot =
this._clientInterestList != null
? new HashSet<>(this._clientInterestList)
: null;
DataSerializer.writeHashSet(clientInterestListSnapshot, out);
HashSet<ClientProxyMembershipID> clientInterestListInvSnapshot =
this._clientInterestListInv != null
? new HashSet<>(this._clientInterestListInv)
: null;
DataSerializer.writeHashSet(clientInterestListInvSnapshot, out);
DataSerializer.writeObject(this.versionTag, out);
}
@Override
public void fromData(DataInput in,
DeserializationContext context) throws IOException, ClassNotFoundException {
this._operation = EnumListenerEvent.getEnumListenerEvent(in.readByte());
this._regionName = DataSerializer.readString(in);
this._keyOfInterest = DataSerializer.readObject(in);
this._value = DataSerializer.readByteArray(in);
this._valueIsObject = in.readByte();
this._membershipId = ClientProxyMembershipID.readCanonicalized(in);
this._shouldConflate = in.readBoolean();
this._isInterestListPassed = in.readBoolean();
this.deltaBytes = DataSerializer.readByteArray(in);
this._hasCqs = in.readBoolean();
this._callbackArgument = DataSerializer.readObject(in);
CacheClientNotifier ccn = CacheClientNotifier.getInstance();
Set<ClientProxyMembershipID> clientInterestList = DataSerializer.readHashSet(in);
this._clientInterestList = ccn != null && clientInterestList != null
? ccn.getProxyIDs(clientInterestList)
: null;
Set<ClientProxyMembershipID> clientInterestListInv = DataSerializer.readHashSet(in);
this._clientInterestListInv = ccn != null && clientInterestListInv != null
? ccn.getProxyIDs(clientInterestListInv)
: null;
this.versionTag = DataSerializer.readObject(in);
}
private Object getOriginalCallbackArgument() {
Object result = this._callbackArgument;
while (result instanceof WrappedCallbackArgument) {
WrappedCallbackArgument wca = (WrappedCallbackArgument) result;
result = wca.getOriginalCallbackArg();
}
return result;
}
/*
* Statically calculate constant overhead for ClientUpdateMessageImpl instance.
*/
static {
// The sizes of the following variables are calculated:
// - 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 membershipId because it is a reference
// - the logger because it is a reference
// - the keyOfInterest because it is a reference
// - the clientCqs because it is a reference
// - the clientInterestList because it is a reference
// - the eventIdentifier because it is a reference
// 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)
int size = 0;
// Add overhead for this instance.
size += Sizeable.PER_OBJECT_OVERHEAD;
// Add object references
// _operation reference = 4 bytes
// _regionName reference = 4 bytes
// _keyOfInterest reference = 4 bytes
// _value reference = 4 bytes
// _callbackArgument reference = 4 bytes
// _membershipId reference = 4 bytes
// _eventIdentifier reference = 4 bytes
// _logger reference = 4 bytes
// _clientCqs reference = 4 bytes
// _clientInterestList reference = 4 bytes
size += 40;
// Add primitive references
// byte _valueIsObject = 1 byte
// boolean _shouldConflate = 1 byte
// boolean _isInterestListPassed = 1 byte
// boolean _hasCqs = 1 byte
// boolean _isNetLoad = 1 byte
size += 5;
// not sure on the kind on wrapper is around callbackArgument
// 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; // do we need it
// add overhead for callback Argument
size += Sizeable.PER_OBJECT_OVERHEAD;
// total overhead
CONSTANT_MEMORY_OVERHEAD = size;
}
@Override
public int getSizeInBytes() {
int size = CONSTANT_MEMORY_OVERHEAD;
// The value (a byte[])
if (this._value != null) {
size += CachedDeserializableFactory.calcMemSize(this._value);
}
// The sizeOf call gets the size of the input callback argument.
size += sizeOf(getOriginalCallbackArgument());
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;
}
/*
* (non-Javadoc)
*
* @see
* org.apache.geode.internal.cache.tier.sockets.ClientUpdateMessage#needsNoAuthorizationCheck()
*/
@Override
public boolean needsNoAuthorizationCheck() {
return false;
}
@Override
public CqNameToOp getClientCq(ClientProxyMembershipID clientId) {
if (this._clientCqs != null) {
return this._clientCqs.get(clientId);
} else {
return null;
}
}
@Override
public Version[] getSerializationVersions() {
return null;
}
/**
* Even though this class is just a ConcurrentHashMap I wanted it to be its own class so it could
* be easily identified in heap dumps. The concurrency level on these should be 1 to keep their
* memory footprint down.
*/
public static class ClientCqConcurrentMap
extends ConcurrentHashMap<ClientProxyMembershipID, CqNameToOp> {
public ClientCqConcurrentMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
}
public ClientCqConcurrentMap() {
super(16, 1.0f, 1);
}
}
/**
* Replaces what used to be a HashMap<String, Integer>.
*/
public interface CqNameToOp extends Sendable {
boolean isEmpty();
/**
* Returns true if calling add would fail.
*/
boolean isFull();
void addToMessage(Message message);
int size();
String[] getNames();
void add(String name, Integer op);
void delete(String name);
}
/**
* Contains either zero or one String to int tuples. This is a common case and this impl has a
* much smaller memory footprint than a HashMap with one entry.
*/
public static class CqNameToOpSingleEntry implements CqNameToOp {
private String[] name;
private int op;
private static final String[] EMPTY_NAMES_ARRAY = new String[0];
public CqNameToOpSingleEntry(String name, Integer op) {
initializeName(name);
this.op = op.intValue();
}
private void initializeName(String name) {
this.name = new String[] {name};
}
@Override
public void sendTo(DataOutput out) throws IOException {
// When serialized it needs to look just as if writeObject was called on a HASH_MAP
out.writeByte(DSCODE.HASH_MAP.toByte());
int size = size();
InternalDataSerializer.writeArrayLength(size, out);
if (size > 0) {
DataSerializer.writeObject(this.name[0], out);
DataSerializer.writeObject(Integer.valueOf(this.op), out);
}
}
@Override
public boolean isEmpty() {
return this.name == null;
}
@Override
public void addToMessage(Message message) {
if (!isEmpty()) {
message.addStringPart(this.name[0], true);
message.addIntPart(this.op);
}
}
@Override
public int size() {
return isEmpty() ? 0 : 1;
}
@Override
public String[] getNames() {
return (isEmpty()) ? EMPTY_NAMES_ARRAY : this.name;
}
@Override
public void add(String name, Integer op) {
if (isEmpty()) {
this.name = new String[] {name};
this.op = op.intValue();
} else if (this.name[0].equals(name)) {
this.op = op.intValue();
} else {
throw new IllegalStateException("tried to add to a full CqNameToOpSingleEntry");
}
}
@Override
public void delete(String name) {
if (name.equals(this.name[0])) {
this.name = null;
}
}
@Override
public boolean isFull() {
return !isEmpty();
}
}
/**
* Basically just a ConcurrentHashMap<String, Integer> but limits itself to the CqNameToOp
* interface.
*/
public static class CqNameToOpHashMap extends ConcurrentHashMap<String, Integer>
implements CqNameToOp {
public CqNameToOpHashMap(int initialCapacity) {
super(initialCapacity, 1.0f);
}
public CqNameToOpHashMap(CqNameToOpSingleEntry se) {
super(2, 1.0f);
add(se.name[0], se.op);
}
@Override
public void sendTo(DataOutput out) throws IOException {
// When serialized it needs to look just as if writeObject was called on a HASH_MAP
out.writeByte(DSCODE.HASH_MAP.toByte());
DataSerializer.writeConcurrentHashMap(this, out);
}
@Override
public String[] getNames() {
String[] cqNames = new String[size()];
cqNames = keySet().toArray(cqNames);
return cqNames;
}
@Override
public void addToMessage(Message message) {
Iterator<Entry<String, Integer>> entries = entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Integer> entry = entries.next();
// Add CQ Name.
String cq = entry.getKey();
message.addStringPart(cq, true);
// Add CQ Op.
int op = entry.getValue().intValue();
message.addIntPart(op);
}
}
@Override
public void add(String name, Integer op) {
put(name, op);
}
@Override
public void delete(String name) {
remove(name);
}
@Override
public boolean isFull() {
return false;
}
}
// NewValueImporter methods
@Override
public boolean prefersNewSerialized() {
return true;
}
@Override
public boolean isUnretainedNewReferenceOk() {
return false;
}
@Override
public void importNewObject(Object nv, boolean isSerialized) {
if (!isSerialized) {
throw new IllegalStateException("Expected importNewBytes to be called.");
}
try {
this._value = CacheServerHelper.serialize(nv);
} catch (IOException e) {
throw new GemFireIOException("Exception serializing entry value", e);
}
}
@Override
public void importNewBytes(byte[] nv, boolean isSerialized) {
if (!isSerialized) {
// The value is already a byte[]. Set _valueIsObject flag to 0x00
// (not an object)
this._valueIsObject = 0x00;
}
this._value = nv;
}
}