org.apache.geode.internal.cache.tier.sockets.CacheClientUpdater Maven / Gradle / Ivy
Show all versions of geode-core Show documentation
/*
* 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 org.apache.geode.*;
import org.apache.geode.cache.EntryNotFoundException;
import org.apache.geode.cache.InterestResultPolicy;
import org.apache.geode.cache.Operation;
import org.apache.geode.cache.RegionDestroyedException;
import org.apache.geode.cache.client.ServerRefusedConnectionException;
import org.apache.geode.cache.client.internal.*;
import org.apache.geode.cache.query.internal.cq.CqService;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.*;
import org.apache.geode.distributed.internal.InternalDistributedSystem.DisconnectListener;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.distributed.internal.membership.MemberAttributes;
import org.apache.geode.internal.*;
import org.apache.geode.internal.cache.*;
import org.apache.geode.internal.cache.tier.CachedRegionHelper;
import org.apache.geode.internal.cache.tier.MessageType;
import org.apache.geode.internal.cache.versions.ConcurrentCacheModificationException;
import org.apache.geode.internal.cache.versions.VersionSource;
import org.apache.geode.internal.cache.versions.VersionTag;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.InternalLogWriter;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.LoggingThreadGroup;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.internal.offheap.annotations.Released;
import org.apache.geode.internal.sequencelog.EntryLogger;
import org.apache.geode.internal.statistics.StatisticsTypeFactoryImpl;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.AuthenticationRequiredException;
import org.apache.geode.security.GemFireSecurityException;
import org.apache.logging.log4j.Logger;
import javax.net.ssl.SSLException;
import java.io.*;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* CacheClientUpdater
is a thread that processes update messages from a cache server
* and {@linkplain org.apache.geode.cache.Region#localInvalidate(Object) invalidates} the local
* cache based on the contents of those messages.
*
* @since GemFire 3.5
*/
public class CacheClientUpdater extends Thread implements ClientUpdater, DisconnectListener {
private static final Logger logger = LogService.getLogger();
/**
* true if the constructor successfully created a connection. If false, the run method for this
* thread immediately exits.
*/
private final boolean connected;
/**
* System of which we are a part
*/
private final InternalDistributedSystem system;
/**
* The socket by which we communicate with the server
*/
private final Socket socket;
/**
* The output stream of the socket
*/
private final OutputStream out;
/**
* The input stream of the socket
*/
private final InputStream in;
/**
* Failed updater from the endpoint previously known as the primary
*/
private volatile ClientUpdater failedUpdater;
/**
* The buffer upon which we receive messages
*/
private final ByteBuffer commBuffer;
private boolean commBufferReleased;
private final CCUStats stats;
/**
* Cache for which we provide service
*/
private /* final */ GemFireCacheImpl cache;
private /* final */ CachedRegionHelper cacheHelper;
/**
* Principle flag to signal thread's run loop to terminate
*/
private final AtomicBoolean continueProcessing = new AtomicBoolean(true);
/**
* Is the client durable Used for bug 39010 fix
*/
private final boolean isDurableClient;
/**
* Represents the server we are connected to
*/
private final InternalDistributedMember serverId;
/**
* true if the EndPoint represented by this updater thread is primary
*/
private final boolean isPrimary;
/**
* Added to avoid recording of the event if the concerned operation failed. See #43247
*/
private boolean isOpCompleted;
public final static String CLIENT_UPDATER_THREAD_NAME = "Cache Client Updater Thread ";
/*
* to enable test flag
*/
public static boolean isUsedByTest;
/**
* Indicates if full value was requested from server as a result of failure in applying delta
* bytes.
*/
public static boolean fullValueRequested = false;
// /**
// * True if this thread been initialized. Indicates that the run thread is
// * initialized and ready to process messages
// *
// * TODO is this still needed?
// *
// * Accesses synchronized via this
// *
// * @see #notifyInitializationComplete()
// * @see #waitForInitialization()
// */
// private boolean initialized = false;
private final ServerLocation location;
// TODO - remove these fields
private QueueManager qManager = null;
private EndpointManager eManager = null;
private Endpoint endpoint = null;
static private final long MAX_CACHE_WAIT = Long
.getLong(DistributionConfig.GEMFIRE_PREFIX + "CacheClientUpdater.MAX_WAIT", 120).longValue(); // seconds
/**
* Return true if cache appears
*
* @return true if cache appears
*/
private boolean waitForCache() {
GemFireCacheImpl c;
long tilt = System.currentTimeMillis() + MAX_CACHE_WAIT * 1000;
for (;;) {
if (quitting()) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.CacheClientUpdater_0_ABANDONED_WAIT_DUE_TO_CANCELLATION, this));
return false;
}
if (!this.connected) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.CacheClientUpdater_0_ABANDONED_WAIT_BECAUSE_IT_IS_NO_LONGER_CONNECTED,
this));
return false;
}
if (System.currentTimeMillis() > tilt) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.CacheClientUpdater_0_WAIT_TIMED_OUT_MORE_THAN_1_SECONDS,
new Object[] {this, MAX_CACHE_WAIT}));
return false;
}
c = GemFireCacheImpl.getInstance();
if (c != null && !c.isClosed()) {
break;
}
boolean interrupted = Thread.interrupted();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
interrupted = true;
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
} // for
this.cache = c;
this.cacheHelper = new CachedRegionHelper(c);
return true;
}
/**
* Creates a new CacheClientUpdater
with a given name that waits for a server to
* connect on a given port.
*
* @param name descriptive name, used for our ThreadGroup
* @param location the endpoint we represent
* @param primary true if our endpoint is primary TODO ask the ep for this?
* @param ids the system we are distributing messages through
*
* @throws AuthenticationRequiredException when client is not configured to send credentials using
* security-* system properties but server expects credentials
* @throws AuthenticationFailedException when authentication of the client fails
* @throws ServerRefusedConnectionException when handshake fails for other reasons like using
* durable client ID that is already in use by another client or some server side
* exception while reading handshake/verifying credentials
*/
public CacheClientUpdater(String name, ServerLocation location, boolean primary,
DistributedSystem ids, HandShake handshake, QueueManager qManager, EndpointManager eManager,
Endpoint endpoint, int handshakeTimeout, SocketCreator socketCreator)
throws AuthenticationRequiredException, AuthenticationFailedException,
ServerRefusedConnectionException {
super(LoggingThreadGroup.createThreadGroup("Client update thread"), name);
this.setDaemon(true);
this.system = (InternalDistributedSystem) ids;
this.isDurableClient = handshake.getMembership().isDurable();
this.isPrimary = primary;
this.location = location;
this.qManager = qManager;
// this holds the connection which this threads reads
this.eManager = eManager;
this.endpoint = endpoint;
this.stats = new CCUStats(this.system, this.location);
// Create the connection...
final boolean isDebugEnabled = logger.isDebugEnabled();
if (isDebugEnabled) {
logger.debug("Creating asynchronous update connection");
}
boolean success = false;
Socket mySock = null;
InternalDistributedMember sid = null;
ByteBuffer cb = null;
OutputStream tmpOut = null;
InputStream tmpIn = null;
try {
/** Size of the server-to-client communication socket buffers */
int socketBufferSize =
Integer.getInteger("BridgeServer.SOCKET_BUFFER_SIZE", 32768).intValue();
mySock = socketCreator.connectForClient(location.getHostName(), location.getPort(),
handshakeTimeout, socketBufferSize);
mySock.setTcpNoDelay(true);
mySock.setSendBufferSize(socketBufferSize);
// Verify buffer sizes
verifySocketBufferSize(socketBufferSize, mySock.getReceiveBufferSize(), "receive");
verifySocketBufferSize(socketBufferSize, mySock.getSendBufferSize(), "send");
// set the timeout for the handshake
mySock.setSoTimeout(handshakeTimeout);
tmpOut = mySock.getOutputStream();
tmpIn = mySock.getInputStream();
if (isDebugEnabled) {
logger.debug(
"Initialized server-to-client socket with send buffer size: {} bytes and receive buffer size: {} bytes",
mySock.getSendBufferSize(), mySock.getReceiveBufferSize());
}
if (isDebugEnabled) {
logger.debug(
"Created connection from {}:{} to CacheClientNotifier on port {} for server-to-client communication",
mySock.getInetAddress().getHostAddress(), mySock.getLocalPort(), mySock.getPort());
}
ServerQueueStatus sqs = handshake.handshakeWithSubscriptionFeed(mySock, this.isPrimary);
if (sqs.isPrimary() || sqs.isNonRedundant()) {
PoolImpl pool = (PoolImpl) this.qManager.getPool();
if (!pool.getReadyForEventsCalled()) {
pool.setPendingEventCount(sqs.getServerQueueSize());
}
}
{
int bufSize = 1024;
try {
bufSize = mySock.getSendBufferSize();
if (bufSize < 1024) {
bufSize = 1024;
}
} catch (SocketException ignore) {
}
cb = ServerConnection.allocateCommBuffer(bufSize, mySock);
}
{
// create a "server" memberId we currently don't know much about the
// server.
// Would be nice for it to send us its member id
// @todo - change the serverId to use the endpoint's getMemberId() which
// returns a
// DistributedMember (once gfecq branch is merged to trunk).
MemberAttributes ma =
new MemberAttributes(0, -1, DistributionManager.NORMAL_DM_TYPE, -1, null, null, null);
sid = new InternalDistributedMember(mySock.getInetAddress(), mySock.getPort(), false, true,
ma);
}
success = true;
} catch (ConnectException e) {
if (!quitting()) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.CacheClientUpdater_0_CONNECTION_WAS_REFUSED, this));
}
} catch (SSLException ex) {
if (!quitting()) {
getSecurityLogger().warning(LocalizedStrings.CacheClientUpdater_0_SSL_NEGOTIATION_FAILED_1,
new Object[] {this, ex});
throw new AuthenticationFailedException(
LocalizedStrings.CacheClientUpdater_SSL_NEGOTIATION_FAILED_WITH_ENDPOINT_0
.toLocalizedString(location),
ex);
}
} catch (GemFireSecurityException ex) {
if (!quitting()) {
getSecurityLogger().warning(
LocalizedStrings.CacheClientUpdater_0_SECURITY_EXCEPTION_WHEN_CREATING_SERVERTOCLIENT_COMMUNICATION_SOCKET_1,
new Object[] {this, ex});
throw ex;
}
} catch (IOException e) {
if (!quitting()) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.CacheClientUpdater_0_CAUGHT_FOLLOWING_EXECPTION_WHILE_ATTEMPTING_TO_CREATE_A_SERVER_TO_CLIENT_COMMUNICATION_SOCKET_AND_WILL_EXIT_1,
new Object[] {this, e}), logger.isDebugEnabled() ? e : null);
}
eManager.serverCrashed(this.endpoint);
} catch (ClassNotFoundException e) {
if (!quitting()) {
logger.warn(LocalizedMessage.create(LocalizedStrings.CacheClientUpdater_CLASS_NOT_FOUND,
e.getMessage()));
}
} finally {
connected = success;
if (mySock != null) {
try {
mySock.setSoTimeout(0);
} catch (SocketException e) {
// ignore: nothing we can do about this
}
}
if (connected) {
socket = mySock;
out = tmpOut;
in = tmpIn;
serverId = sid;
commBuffer = cb;
// Don't want the timeout after handshake
if (mySock != null) {
try {
mySock.setSoTimeout(0);
} catch (SocketException ignore) {
}
}
} else {
socket = null;
serverId = null;
commBuffer = null;
out = null;
in = null;
if (mySock != null) {
try {
mySock.close();
} catch (IOException ioe) {
logger.warn(LocalizedMessage
.create(LocalizedStrings.CacheClientUpdater_CLOSING_SOCKET_IN_0_FAILED, this), ioe);
}
}
}
}
}
private void releaseCommBuffer() {
if (!this.commBufferReleased) {
if (this.commBuffer != null) {
synchronized (this.commBuffer) {
if (!this.commBufferReleased) {
this.commBufferReleased = true;
ServerConnection.releaseCommBuffer(this.commBuffer);
}
}
}
}
}
public boolean isConnected() {
return connected;
}
public boolean isPrimary() {
return isPrimary;
}
public InternalLogWriter getSecurityLogger() {
return this.qManager.getSecurityLogger();
}
public void setFailedUpdater(ClientUpdater failedUpdater) {
this.failedUpdater = failedUpdater;
}
/**
* Performs the work of the client update thread. Creates a ServerSocket
and waits
* for the server to connect to it.
*/
@Override
public void run() {
boolean addedListener = false;
EntryLogger.setSource(serverId, "RI");
try {
this.system.addDisconnectListener(this);
addedListener = true;
if (!waitForCache()) {
logger.warn(
LocalizedMessage.create(LocalizedStrings.CacheClientUpdater_0_NO_CACHE_EXITING, this));
return;
}
processMessages();
} catch (CancelException e) {
return; // just bail
} finally {
if (addedListener) {
this.system.removeDisconnectListener(this);
}
this.close();
EntryLogger.clearSource();
}
}
// /**
// * Waits for this thread to be initialized
// *
// * @return true if initialized; false if stopped before init
// */
// public boolean waitForInitialization() {
// boolean result = false;
// // Yogesh : waiting on this thread object is a bad idea
// // as when thread exits it notifies to the waiting threads.
// synchronized (this) {
// for (;;) {
// if (quitting()) {
// break;
// }
// boolean interrupted = Thread.interrupted();
// try {
// this.wait(100); // spurious wakeup ok // timed wait, should fix lost notification problem
// rahul.
// }
// catch (InterruptedException e) {
// interrupted = true;
// }
// finally {
// if (interrupted) {
// Thread.currentThread().interrupt();
// }
// }
// } // while
// // Even if we succeed, there is a risk that we were shut down
// // Can't check for cache; it isn't set yet :-(
// this.system.getCancelCriterion().checkCancelInProgress(null);
// result = this.continueProcessing;
// } // synchronized
// return result;
// }
// /**
// * @see #waitForInitialization()
// */
// private void notifyInitializationComplete() {
// synchronized (this) {
// this.initialized = true;
// this.notifyAll();
// }
// }
/**
* Notifies this thread to stop processing
*/
protected void stopProcessing() {
continueProcessing.set(false);// = false;
}
/**
* Stops the updater. It will wait for a while for the thread to finish to try to prevent
* duplicates. Note: this method is not named stop because this is a Thread which has a deprecated
* stop method.
*/
public void stopUpdater() {
boolean isSelfDestroying = Thread.currentThread() == this;
stopProcessing();
// need to also close the socket for this interrupt to wakeup
// the thread. This fixes bug 35691.
// this.close(); // this should not be done here.
if (this.isAlive()) {
if (logger.isDebugEnabled()) {
logger.debug("{}: Stopping {}", this.location, this);
}
if (!isSelfDestroying) {
interrupt();
try {
if (socket != null) {
socket.close();
}
} catch (VirtualMachineError err) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
} catch (Throwable t) {
// Whenever you catch Error or Throwable, you must also
// catch VirtualMachineError (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
// dont care...
if (logger.isDebugEnabled()) {
logger.debug(t.getMessage(), t);
}
}
} // !isSelfDestroying
} // isAlive
}
/**
* Signals the run thread to stop, closes underlying resources.
*/
public void close() {
this.continueProcessing.set(false);// = false; // signals we are done.
// Close the socket
// This will also cause the underlying streams to fail.
try {
if (socket != null) {
socket.close();
}
} catch (Exception e) {
// ignore
}
try {
this.stats.close();
} catch (Exception e) {
// ignore
}
// close the helper
try {
if (cacheHelper != null) {
cacheHelper.close();
}
} catch (Exception e) {
// ignore
}
releaseCommBuffer();
}
/**
* Creates a cached {@link Message}object whose state is filled in with a message received from
* the server.
*/
private Message initializeMessage() {
Message _message = new Message(2, Version.CURRENT);
try {
_message.setComms(socket, in, out, commBuffer, this.stats);
} catch (IOException e) {
if (!quitting()) {
if (logger.isDebugEnabled()) {
logger.debug(
"{}: Caught following exception while attempting to initialize a server-to-client communication socket and will exit",
this, e);
}
stopProcessing();
}
}
return _message;
}
/* refinement of method inherited from Thread */
@Override
public String toString() {
return this.getName() + " (" + this.location.getHostName() + ":" + this.location.getPort()
+ ")";
}
/**
* Handle a marker message
*
* @param m message containing the data
*/
private void handleMarker(Message m) {
try {
final boolean isDebugEnabled = logger.isDebugEnabled();
if (isDebugEnabled) {
logger.debug("Received marker message of length ({} bytes)", m.getPayloadLength());
}
this.qManager.getState().processMarker();
if (isDebugEnabled) {
logger.debug("Processed marker message");
}
} catch (Exception e) {
String message =
LocalizedStrings.CacheClientUpdater_THE_FOLLOWING_EXCEPTION_OCCURRED_WHILE_ATTEMPTING_TO_HANDLE_A_MARKER
.toLocalizedString();
handleException(message, e);
}
}
/**
* Create or update an entry
*
* @param m message containing the data
*/
private void handleUpdate(Message m) {
String regionName = null;
Object key = null;
Part valuePart = null;
Object newValue = null;
byte[] deltaBytes = null;
Object fullValue = null;
boolean isValueObject = false;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
this.isOpCompleted = false;
// Retrieve the data from the put message parts
if (isDebugEnabled) {
logger.debug("Received put message of length ({} bytes)", m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
Part keyPart = m.getPart(partCnt++);
boolean isDeltaSent = ((Boolean) m.getPart(partCnt++).getObject()).booleanValue();
valuePart = m.getPart(partCnt++);
Part callbackArgumentPart = m.getPart(partCnt++);
VersionTag versionTag = (VersionTag) m.getPart(partCnt++).getObject();
if (versionTag != null) {
versionTag.replaceNullIDs((InternalDistributedMember) this.endpoint.getMemberId());
}
Part isInterestListPassedPart = m.getPart(partCnt++);
Part hasCqsPart = m.getPart(partCnt++);
EventID eventId = (EventID) m.getPart(m.getNumberOfParts() - 1).getObject();
boolean withInterest = ((Boolean) isInterestListPassedPart.getObject()).booleanValue();
boolean withCQs = ((Boolean) hasCqsPart.getObject()).booleanValue();
regionName = regionNamePart.getString();
key = keyPart.getStringOrObject();
Object callbackArgument = callbackArgumentPart.getObject();
// Don't automatically deserialize the value.
// Pass it onto the region as a byte[]. If it is a serialized
// object, it will be stored as a CachedDeserializable and
// deserialized only when requested.
boolean isCreate = (m.getMessageType() == MessageType.LOCAL_CREATE);
if (isDebugEnabled) {
logger
.debug(
"Putting entry for region: {} key: {} create: {}{} callbackArgument: {} withInterest={} withCQs={} eventID={} version={}",
regionName, key, isCreate,
(valuePart.isObject() ? new StringBuilder(" value: ")
.append(deserialize(valuePart.getSerializedForm())) : ""),
callbackArgument, withInterest, withCQs, eventId, versionTag);
}
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (!isDeltaSent) {
// bug #42162 - must check for a serialized null here
byte[] serializedForm = valuePart.getSerializedForm();
if (isCreate && InternalDataSerializer.isSerializedNull(serializedForm)) {
// newValue = null; newValue is already null
} else {
newValue = valuePart.getSerializedForm();
}
if (withCQs) {
fullValue = valuePart.getObject();
}
isValueObject = valuePart.isObject();
} else {
deltaBytes = valuePart.getSerializedForm();
isValueObject = true;
}
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("{}: Region named {} does not exist", this, regionName);
}
} else if (region.hasServerProxy() && ServerResponseMatrix
.checkForValidStateAfterNotification(region, key, m.getMessageType())
&& (withInterest || !withCQs)) {
@Released
EntryEventImpl newEvent = null;
try {
// Create an event and put the entry
newEvent = EntryEventImpl.create(region,
((m.getMessageType() == MessageType.LOCAL_CREATE) ? Operation.CREATE
: Operation.UPDATE),
key, null /* newValue */, callbackArgument /* callbackArg */, true /* originRemote */,
eventId.getDistributedMember());
newEvent.setVersionTag(versionTag);
newEvent.setFromServer(true);
region.basicBridgeClientUpdate(eventId.getDistributedMember(), key, newValue, deltaBytes,
isValueObject, callbackArgument, m.getMessageType() == MessageType.LOCAL_CREATE,
qManager.getState().getProcessedMarker() || !this.isDurableClient, newEvent, eventId);
this.isOpCompleted = true;
// bug 45520 - ConcurrentCacheModificationException is not thrown and we must check this
// flag
// if (newEvent.isConcurrencyConflict()) {
// return; // this is logged elsewhere at fine level
// }
if (withCQs && isDeltaSent) {
fullValue = newEvent.getNewValue();
}
} catch (InvalidDeltaException ide) {
Part fullValuePart = requestFullValue(eventId, "Caught InvalidDeltaException.");
region.getCachePerfStats().incDeltaFullValuesRequested();
fullValue = newValue = fullValuePart.getObject();
isValueObject = Boolean.valueOf(fullValuePart.isObject());
region.basicBridgeClientUpdate(eventId.getDistributedMember(), key, newValue, null,
isValueObject, callbackArgument, m.getMessageType() == MessageType.LOCAL_CREATE,
qManager.getState().getProcessedMarker() || !this.isDurableClient, newEvent, eventId);
this.isOpCompleted = true;
} finally {
if (newEvent != null)
newEvent.release();
}
if (isDebugEnabled) {
logger.debug("Put entry for region: {} key: {} callbackArgument: {}", regionName, key,
callbackArgument);
}
}
// Update CQs. CQs can exist without client region.
if (withCQs) {
Part numCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Received message has CQ Event. Number of cqs interested in the event : {}",
numCqsPart.getInt() / 2);
}
partCnt = processCqs(m, partCnt, numCqsPart.getInt(), m.getMessageType(), key, fullValue,
deltaBytes, eventId);
this.isOpCompleted = true;
}
} catch (Exception e) {
String message =
LocalizedStrings.CacheClientUpdater_THE_FOLLOWING_EXCEPTION_OCCURRED_WHILE_ATTEMPTING_TO_PUT_ENTRY_REGION_0_KEY_1_VALUE_2
.toLocalizedString(
new Object[] {regionName, key, deserialize(valuePart.getSerializedForm())});
handleException(message, e);
}
}
private Part requestFullValue(EventID eventId, String reason) throws Exception {
if (isUsedByTest) {
fullValueRequested = true;
}
final boolean isDebugEnabled = logger.isDebugEnabled();
if (isDebugEnabled) {
logger.debug("{} Requesting full value...", reason);
}
Part result = (Part) GetEventValueOp.executeOnPrimary(qManager.getPool(), eventId, null);
if (result == null) {
// Just log a warning. Do not stop CCU thread.
throw new Exception("Could not retrieve full value for " + eventId);
}
if (isDebugEnabled) {
logger.debug("Full value received.");
}
return result;
}
/**
* Invalidate an entry
*
* @param m message describing the entry
*/
private void handleInvalidate(Message m) {
String regionName = null;
Object key = null;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
this.isOpCompleted = false;
// Retrieve the data from the local-invalidate message parts
if (isDebugEnabled) {
logger.debug("Received invalidate message of length ({} bytes)", m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
Part keyPart = m.getPart(partCnt++);
Part callbackArgumentPart = m.getPart(partCnt++);
VersionTag versionTag = (VersionTag) m.getPart(partCnt++).getObject();
if (versionTag != null) {
versionTag.replaceNullIDs((InternalDistributedMember) this.endpoint.getMemberId());
}
Part isInterestListPassedPart = m.getPart(partCnt++);
Part hasCqsPart = m.getPart(partCnt++);
regionName = regionNamePart.getString();
key = keyPart.getStringOrObject();
Object callbackArgument = callbackArgumentPart.getObject();
boolean withInterest = ((Boolean) isInterestListPassedPart.getObject()).booleanValue();
boolean withCQs = ((Boolean) hasCqsPart.getObject()).booleanValue();
if (isDebugEnabled) {
logger.debug(
"Invalidating entry for region: {} key: {} callbackArgument: {} withInterest={} withCQs={} version={}",
regionName, key, callbackArgument, withInterest, withCQs, versionTag);
}
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("Region named {} does not exist", regionName);
}
} else {
if (region.hasServerProxy() && (withInterest || !withCQs)) {
try {
Part eid = m.getPart(m.getNumberOfParts() - 1);
EventID eventId = (EventID) eid.getObject();
try {
region.basicBridgeClientInvalidate(eventId.getDistributedMember(), key,
callbackArgument,
qManager.getState().getProcessedMarker() || !this.isDurableClient, eventId,
versionTag);
} catch (ConcurrentCacheModificationException e) {
// return; allow CQs to be processed
}
this.isOpCompleted = true;
// fix for 36615
qManager.getState().incrementInvalidatedStats();
if (isDebugEnabled) {
logger.debug("Invalidated entry for region: {} key: {} callbackArgument: {}",
regionName, key, callbackArgument);
}
} catch (EntryNotFoundException e) {
/* ignore */
if (isDebugEnabled && !quitting()) {
logger.debug("Already invalidated entry for region: {} key: {} callbackArgument: {}",
regionName, key, callbackArgument);
}
this.isOpCompleted = true;
}
}
}
if (withCQs) {
// The client may have been registered to receive invalidates for
// create and updates operations. Get the actual region operation.
Part regionOpType = m.getPart(partCnt++);
Part numCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Received message has CQ Event. Number of cqs interested in the event : {}",
numCqsPart.getInt() / 2);
}
partCnt = processCqs(m, partCnt, numCqsPart.getInt(), regionOpType.getInt(), key, null);
this.isOpCompleted = true;
}
} catch (Exception e) {
final String message =
LocalizedStrings.CacheClientUpdater_THE_FOLLOWING_EXCEPTION_OCCURRED_WHILE_ATTEMPTING_TO_INVALIDATE_ENTRY_REGION_0_KEY_1
.toLocalizedString(new Object[] {regionName, key});
handleException(message, e);
}
}
/**
* locally destroy an entry
*
* @param m message describing the entry
*/
private void handleDestroy(Message m) {
String regionName = null;
Object key = null;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
this.isOpCompleted = false;
// Retrieve the data from the local-destroy message parts
if (isDebugEnabled) {
logger.debug("Received destroy message of length ({} bytes)", m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
Part keyPart = m.getPart(partCnt++);
Part callbackArgumentPart = m.getPart(partCnt++);
VersionTag versionTag = (VersionTag) m.getPart(partCnt++).getObject();
if (versionTag != null) {
versionTag.replaceNullIDs((InternalDistributedMember) this.endpoint.getMemberId());
}
regionName = regionNamePart.getString();
key = keyPart.getStringOrObject();
Part isInterestListPassedPart = m.getPart(partCnt++);
Part hasCqsPart = m.getPart(partCnt++);
boolean withInterest = ((Boolean) isInterestListPassedPart.getObject()).booleanValue();
boolean withCQs = ((Boolean) hasCqsPart.getObject()).booleanValue();
Object callbackArgument = callbackArgumentPart.getObject();
if (isDebugEnabled) {
logger.debug(
"Destroying entry for region: {} key: {} callbackArgument: {} withInterest={} withCQs={} version={}",
regionName, key, callbackArgument, withInterest, withCQs, versionTag);
}
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
EventID eventId = null;
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("Region named {} does not exist", regionName);
}
} else if (region.hasServerProxy() && (withInterest || !withCQs)) {
try {
Part eid = m.getPart(m.getNumberOfParts() - 1);
eventId = (EventID) eid.getObject();
try {
region.basicBridgeClientDestroy(eventId.getDistributedMember(), key, callbackArgument,
qManager.getState().getProcessedMarker() || !this.isDurableClient, eventId,
versionTag);
} catch (ConcurrentCacheModificationException e) {
// return; allow CQs to be processed
}
this.isOpCompleted = true;
if (isDebugEnabled) {
logger.debug("Destroyed entry for region: {} key: {} callbackArgument: {}", regionName,
key, callbackArgument);
}
} catch (EntryNotFoundException e) {
/* ignore */
if (isDebugEnabled && !quitting()) {
logger.debug(
"Already destroyed entry for region: {} key: {} callbackArgument: {} eventId={}",
regionName, key, callbackArgument, eventId.expensiveToString());
}
this.isOpCompleted = true;
}
}
if (withCQs) {
Part numCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Received message has CQ Event. Number of cqs interested in the event : {}",
numCqsPart.getInt() / 2);
}
partCnt = processCqs(m, partCnt, numCqsPart.getInt(), m.getMessageType(), key, null);
this.isOpCompleted = true;
}
} catch (Exception e) {
String message =
LocalizedStrings.CacheClientUpdater_THE_FOLLOWING_EXCEPTION_OCCURRED_WHILE_ATTEMPTING_TO_DESTROY_ENTRY_REGION_0_KEY_1
.toLocalizedString(new Object[] {regionName, key});
handleException(message, e);
}
}
/**
* Locally destroy a region
*
* @param m message describing the region
*/
private void handleDestroyRegion(Message m) {
Part regionNamePart = null, callbackArgumentPart = null;
String regionName = null;
Object callbackArgument = null;
LocalRegion region = null;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
// Retrieve the data from the local-destroy-region message parts
if (isDebugEnabled) {
logger.debug("Received destroy region message of length ({} bytes)", m.getPayloadLength());
}
regionNamePart = m.getPart(partCnt++);
callbackArgumentPart = m.getPart(partCnt++);
regionName = regionNamePart.getString();
callbackArgument = callbackArgumentPart.getObject();
Part hasCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Destroying region: {} callbackArgument: {}", regionName, callbackArgument);
}
// Handle CQs if any on this region.
if (((Boolean) hasCqsPart.getObject()).booleanValue()) {
Part numCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Received message has CQ Event. Number of cqs interested in the event : {}",
numCqsPart.getInt() / 2);
}
partCnt = processCqs(m, partCnt, numCqsPart.getInt(), m.getMessageType(), null, null);
}
// Confirm that the region exists
region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("Region named {} does not exist", regionName);
}
return;
}
// Verify that the region in question should respond to this message
if (region.hasServerProxy()) {
// Locally destroy the region
region.localDestroyRegion(callbackArgument);
if (isDebugEnabled) {
logger.debug("Destroyed region: {} callbackArgument: {}", regionName, callbackArgument);
}
}
} catch (RegionDestroyedException e) { // already destroyed
if (isDebugEnabled) {
logger.debug("region already destroyed: {}", regionName);
}
} catch (Exception e) {
String message =
LocalizedStrings.CacheClientUpdater_CAUGHT_AN_EXCEPTION_WHILE_ATTEMPTING_TO_DESTROY_REGION_0
.toLocalizedString(regionName);
handleException(message, e);
}
}
/**
* Locally clear a region
*
* @param m message describing the region to clear
*/
private void handleClearRegion(Message m) {
String regionName = null;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
// Retrieve the data from the clear-region message parts
if (isDebugEnabled) {
logger.debug("{}: Received clear region message of length ({} bytes)", this,
m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
Part callbackArgumentPart = m.getPart(partCnt++);
Part hasCqsPart = m.getPart(partCnt++);
regionName = regionNamePart.getString();
Object callbackArgument = callbackArgumentPart.getObject();
if (isDebugEnabled) {
logger.debug("Clearing region: {} callbackArgument: {}", regionName, callbackArgument);
}
if (((Boolean) hasCqsPart.getObject()).booleanValue()) {
Part numCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Received message has CQ Event. Number of cqs interested in the event : {}",
numCqsPart.getInt() / 2);
}
partCnt = processCqs(m, partCnt, numCqsPart.getInt(), m.getMessageType(), null, null);
}
// Confirm that the region exists
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("Region named {} does not exist", regionName);
}
return;
}
// Verify that the region in question should respond to this
// message
if (region.hasServerProxy()) {
// Locally clear the region
region.basicBridgeClientClear(callbackArgument,
qManager.getState().getProcessedMarker() || !this.isDurableClient);
if (isDebugEnabled) {
logger.debug("Cleared region: {} callbackArgument: {}", regionName, callbackArgument);
}
}
} catch (Exception e) {
String message =
LocalizedStrings.CacheClientUpdater_CAUGHT_THE_FOLLOWING_EXCEPTION_WHILE_ATTEMPTING_TO_CLEAR_REGION_0
.toLocalizedString(regionName);
handleException(message, e);
}
}
/**
* Locally invalidate a region NOTE: Added as part of bug#38048. The code only takes care of CQ
* processing. Support needs to be added for local region invalidate.
*
* @param m message describing the region to clear
*/
private void handleInvalidateRegion(Message m) {
String regionName = null;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
// Retrieve the data from the invalidate-region message parts
if (isDebugEnabled) {
logger.debug("{}: Received invalidate region message of length ({} bytes)", this,
m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
partCnt++; // Part callbackArgumentPart = m.getPart(partCnt++);
Part hasCqsPart = m.getPart(partCnt++);
regionName = regionNamePart.getString();
// Object callbackArgument = callbackArgumentPart.getObject();
if (((Boolean) hasCqsPart.getObject()).booleanValue()) {
Part numCqsPart = m.getPart(partCnt++);
if (isDebugEnabled) {
logger.debug("Received message has CQ Event. Number of cqs interested in the event : {}",
numCqsPart.getInt() / 2);
}
partCnt = processCqs(m, partCnt, numCqsPart.getInt(), m.getMessageType(), null, null);
}
// Confirm that the region exists
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("Region named {} does not exist", regionName);
}
return;
}
// Verify that the region in question should respond to this
// message
if (region.hasServerProxy()) {
return;
// NOTE:
// As explained in the method description, this code is added as part
// of CQ bug fix. Cache server team needs to look into changes relating
// to local region.
//
// Locally invalidate the region
// region.basicBridgeClientInvalidate(callbackArgument,
// proxy.getProcessedMarker());
// if (logger.debugEnabled()) {
// logger.debug(toString() + ": Cleared region: " + regionName
// + " callbackArgument: " + callbackArgument);
// }
}
} catch (Exception e) {
String message =
LocalizedStrings.CacheClientUpdater_CAUGHT_THE_FOLLOWING_EXCEPTION_WHILE_ATTEMPTING_TO_INVALIDATE_REGION_0
.toLocalizedString(regionName);
handleException(message, e);
}
}
/**
* Register instantiators locally
*
* @param msg message describing the new instantiators
* @param eventId eventId of the instantiators
*/
private void handleRegisterInstantiator(Message msg, EventID eventId) {
String instantiatorClassName = null;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
int noOfParts = msg.getNumberOfParts();
if (isDebugEnabled) {
logger.debug("{}: Received register instantiators message of parts {}", getName(),
noOfParts);
}
Assert.assertTrue((noOfParts - 1) % 3 == 0);
for (int i = 0; i < noOfParts - 1; i = i + 3) {
instantiatorClassName =
(String) CacheServerHelper.deserialize(msg.getPart(i).getSerializedForm());
String instantiatedClassName =
(String) CacheServerHelper.deserialize(msg.getPart(i + 1).getSerializedForm());
int id = msg.getPart(i + 2).getInt();
InternalInstantiator.register(instantiatorClassName, instantiatedClassName, id, false,
eventId, null/* context */);
// distribute is false because we don't want to propagate this to
// servers recursively
}
// // CALLBACK TESTING PURPOSE ONLY ////
if (PoolImpl.IS_INSTANTIATOR_CALLBACK) {
ClientServerObserver bo = ClientServerObserverHolder.getInstance();
bo.afterReceivingFromServer(eventId);
}
// /////////////////////////////////////
}
// TODO bug: can the following catch be more specific?
catch (Exception e) {
if (isDebugEnabled) {
logger.debug("{}: Caught following exception while attempting to read Instantiator : {}",
this, instantiatorClassName, e);
}
}
}
private void handleRegisterDataSerializer(Message msg, EventID eventId) {
Class dataSerializerClass = null;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
int noOfParts = msg.getNumberOfParts();
// int numOfClasses = noOfParts - 3; // 1 for ds classname, 1 for ds id and 1 for eventId.
if (isDebugEnabled) {
logger.debug("{}: Received register dataserializer message of parts {}", getName(),
noOfParts);
}
for (int i = 0; i < noOfParts - 1;) {
try {
String dataSerializerClassName =
(String) CacheServerHelper.deserialize(msg.getPart(i).getSerializedForm());
int id = msg.getPart(i + 1).getInt();
InternalDataSerializer.register(dataSerializerClassName, false, eventId,
null/* context */, id);
// distribute is false because we don't want to propagate this to
// servers recursively
int numOfClasses = msg.getPart(i + 2).getInt();
int j = 0;
for (; j < numOfClasses; j++) {
String className =
(String) CacheServerHelper.deserialize(msg.getPart(i + 3 + j).getSerializedForm());
InternalDataSerializer.updateSupportedClassesMap(dataSerializerClassName, className);
}
i = i + 3 + j;
} catch (ClassNotFoundException e) {
if (isDebugEnabled) {
logger.debug(
"{}: Caught following exception while attempting to read DataSerializer : {}", this,
dataSerializerClass, e);
}
}
}
// // CALLBACK TESTING PURPOSE ONLY ////
if (PoolImpl.IS_INSTANTIATOR_CALLBACK) {
ClientServerObserver bo = ClientServerObserverHolder.getInstance();
bo.afterReceivingFromServer(eventId);
}
///////////////////////////////////////
}
// TODO bug: can the following catch be more specific?
catch (Exception e) {
if (isDebugEnabled) {
logger.debug("{}: Caught following exception while attempting to read DataSerializer : {}",
this, dataSerializerClass, e);
}
}
}
/**
* Processes message to invoke CQ listeners.
*
* @param startMessagePart
* @param numCqParts
* @param messageType
* @param key
* @param value
*/
private int processCqs(Message m, int startMessagePart, int numCqParts, int messageType,
Object key, Object value) {
return processCqs(m, startMessagePart, numCqParts, messageType, key, value, null,
null/* eventId */);
}
private int processCqs(Message m, int startMessagePart, int numCqParts, int messageType,
Object key, Object value, byte[] delta, EventID eventId) {
// String[] cqs = new String[numCqs/2];
HashMap cqs = new HashMap();
final boolean isDebugEnabled = logger.isDebugEnabled();
for (int cqCnt = 0; cqCnt < numCqParts;) {
StringBuilder str = null;
if (isDebugEnabled) {
str = new StringBuilder(100);
str.append("found these queries: ");
}
try {
// Get CQ Name.
Part cqNamePart = m.getPart(startMessagePart + (cqCnt++));
// Get CQ Op.
Part cqOpPart = m.getPart(startMessagePart + (cqCnt++));
cqs.put(cqNamePart.getString(), Integer.valueOf(cqOpPart.getInt()));
if (str != null) {
str.append(cqNamePart.getString()).append(" op=").append(cqOpPart.getInt()).append(" ");
}
} catch (Exception ex) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.CacheClientUpdater_ERROR_WHILE_PROCESSING_THE_CQ_MESSAGE_PROBLEM_WITH_READING_MESSAGE_FOR_CQ_0,
cqCnt));
}
if (isDebugEnabled && str != null) {
logger.debug(str);
}
}
{
CqService cqService = this.cache.getCqService();
try {
cqService.dispatchCqListeners(cqs, messageType, key, value, delta, qManager, eventId);
} catch (Exception ex) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.CacheClientUpdater_FAILED_TO_INVOKE_CQ_DISPATCHER_ERROR___0,
ex.getMessage()));
if (isDebugEnabled) {
logger.debug("Failed to invoke CQ Dispatcher.", ex);
}
}
}
return (startMessagePart + numCqParts);
}
private void handleRegisterInterest(Message m) {
String regionName = null;
Object key = null;
int interestType;
byte interestResultPolicy;
boolean isDurable;
boolean receiveUpdatesAsInvalidates;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
// Retrieve the data from the add interest message parts
if (isDebugEnabled) {
logger.debug("{}: Received add interest message of length ({} bytes)", this,
m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
Part keyPart = m.getPart(partCnt++);
Part interestTypePart = m.getPart(partCnt++);
Part interestResultPolicyPart = m.getPart(partCnt++);
Part isDurablePart = m.getPart(partCnt++);
Part receiveUpdatesAsInvalidatesPart = m.getPart(partCnt++);
regionName = regionNamePart.getString();
key = keyPart.getStringOrObject();
interestType = ((Integer) interestTypePart.getObject()).intValue();
interestResultPolicy = ((Byte) interestResultPolicyPart.getObject()).byteValue();
isDurable = ((Boolean) isDurablePart.getObject()).booleanValue();
receiveUpdatesAsInvalidates =
((Boolean) receiveUpdatesAsInvalidatesPart.getObject()).booleanValue();
// Confirm that region exists
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (isDebugEnabled && !quitting()) {
logger.debug("{}: Region named {} does not exist", this, regionName);
}
return;
}
// Verify that the region in question should respond to this message
if (!region.hasServerProxy()) {
return;
}
if (key instanceof List) {
region.getServerProxy().addListInterest((List) key,
InterestResultPolicy.fromOrdinal(interestResultPolicy), isDurable,
receiveUpdatesAsInvalidates);
} else {
region.getServerProxy().addSingleInterest(key, interestType,
InterestResultPolicy.fromOrdinal(interestResultPolicy), isDurable,
receiveUpdatesAsInvalidates);
}
} catch (Exception e) {
String message =
": The following exception occurred while attempting to add interest (region: "
+ regionName + " key: " + key + "): ";
handleException(message, e);
}
}
private void handleUnregisterInterest(Message m) {
String regionName = null;
Object key = null;
int interestType;
boolean isDurable;
boolean receiveUpdatesAsInvalidates;
int partCnt = 0;
final boolean isDebugEnabled = logger.isDebugEnabled();
try {
// Retrieve the data from the remove interest message parts
if (isDebugEnabled) {
logger.debug("{}: Received remove interest message of length ({} bytes)", this,
m.getPayloadLength());
}
Part regionNamePart = m.getPart(partCnt++);
Part keyPart = m.getPart(partCnt++);
Part interestTypePart = m.getPart(partCnt++);
Part isDurablePart = m.getPart(partCnt++);
Part receiveUpdatesAsInvalidatesPart = m.getPart(partCnt++);
// Not reading the eventId part
regionName = regionNamePart.getString();
key = keyPart.getStringOrObject();
interestType = ((Integer) interestTypePart.getObject()).intValue();
isDurable = ((Boolean) isDurablePart.getObject()).booleanValue();
receiveUpdatesAsInvalidates =
((Boolean) receiveUpdatesAsInvalidatesPart.getObject()).booleanValue();
// Confirm that region exists
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (isDebugEnabled) {
logger.debug("{}: Region named {} does not exist", this, regionName);
}
return;
}
// Verify that the region in question should respond to this message
if (!region.hasServerProxy()) {
return;
}
if (key instanceof List) {
region.getServerProxy().removeListInterest((List) key, isDurable,
receiveUpdatesAsInvalidates);
} else {
region.getServerProxy().removeSingleInterest(key, interestType, isDurable,
receiveUpdatesAsInvalidates);
}
} catch (Exception e) {
String message =
": The following exception occurred while attempting to add interest (region: "
+ regionName + " key: " + key + "): ";
handleException(message, e);
}
}
private void handleTombstoneOperation(Message msg) {
String regionName = "unknown";
try { // not sure why this isn't done by the caller
int partIdx = 0;
// see ClientTombstoneMessage.getGFE70Message
regionName = msg.getPart(partIdx++).getString();
int op = msg.getPart(partIdx++).getInt();
LocalRegion region = (LocalRegion) cacheHelper.getRegion(regionName);
if (region == null) {
if (!quitting()) {
if (logger.isDebugEnabled()) {
logger.debug("{}: Region named {} does not exist", this, regionName);
}
}
return;
}
if (logger.isDebugEnabled()) {
logger.debug("{}: Received tombstone operation for region {} with operation={}", this,
region, op);
}
if (!region.getConcurrencyChecksEnabled()) {
return;
}
switch (op) {
case 0:
Map regionGCVersions =
(Map) msg.getPart(partIdx++).getObject();
EventID eventID = (EventID) msg.getPart(partIdx++).getObject();
region.expireTombstones(regionGCVersions, eventID, null);
break;
case 1:
Set