com.gemstone.gemfire.distributed.internal.locks.DLockQueryProcessor Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.distributed.internal.locks;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.LockServiceDestroyedException;
import com.gemstone.gemfire.distributed.internal.DM;
import com.gemstone.gemfire.distributed.internal.DistributionManager;
import com.gemstone.gemfire.distributed.internal.DistributionMessage;
import com.gemstone.gemfire.distributed.internal.MessageWithReply;
import com.gemstone.gemfire.distributed.internal.PooledDistributionMessage;
import com.gemstone.gemfire.distributed.internal.ReplyException;
import com.gemstone.gemfire.distributed.internal.ReplyMessage;
import com.gemstone.gemfire.distributed.internal.ReplyProcessor21;
import com.gemstone.gemfire.distributed.internal.locks.DLockGrantor.DLockGrantToken;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* Queries the grantor for current leasing information of a lock.
*
* @author Kirk Lund
*/
public class DLockQueryProcessor extends ReplyProcessor21 {
/** DLockLogWriter wrapped logger */
private final LogWriter log;
/** The reply to the query or null */
private volatile DLockQueryReplyMessage reply;
/**
* Query the grantor for current leasing information of a lock.
*
* @param grantor the member that is the grantor
* @param serviceName the name of the lock service
* @param objectName the named lock
* @param lockBatch true if the named lock is a TX lock batch
* @param dm distribution manager to use for logging and messaging
* @return the query reply or null if there was no reply due to membership
* change
*/
static DLockQueryReplyMessage query(final InternalDistributedMember grantor,
final String serviceName,
final Object objectName,
final boolean lockBatch,
final DM dm) {
DLockQueryProcessor processor = new DLockQueryProcessor(
dm, grantor, serviceName);
DLockQueryMessage msg = new DLockQueryMessage();
msg.processorId = processor.getProcessorId();
msg.serviceName = serviceName;
msg.objectName = objectName;
msg.lockBatch = lockBatch;
msg.setRecipient(grantor);
if (grantor.equals(dm.getId())) {
// local... don't actually send message
msg.setSender(grantor);
msg.processLocally(dm);
}
else {
dm.putOutgoing(msg);
}
// keep waiting even if interrupted
try {
processor.waitForRepliesUninterruptibly();
}
catch (ReplyException e) {
e.handleAsUnexpected();
}
if (processor.reply == null) {
return null;
}
else {
return processor.reply;
}
}
/**
* Instantiates a new DLockQueryProcessor.
*
* @param dm the distribution manager to use for logging and messaging
* @param grantor the member to query for lock leasing info
* @param serviceName the name of the lock service
*/
private DLockQueryProcessor(DM dm,
InternalDistributedMember grantor,
String serviceName) {
super(dm, grantor);
this.log = new DLockLogWriter(serviceName, dm.getLoggerI18n());
}
@Override
protected boolean allowReplyFromSender() {
return true;
}
@Override
public void process(DistributionMessage msg) {
try {
DLockQueryReplyMessage myReply = (DLockQueryReplyMessage) msg;
if (this.log.fineEnabled()) {
this.log.fine("Handling: " + myReply);
}
this.reply = myReply;
}
finally {
super.process(msg);
}
}
// -------------------------------------------------------------------------
// DLockQueryMessage
// -------------------------------------------------------------------------
public static final class DLockQueryMessage
extends PooledDistributionMessage
implements MessageWithReply {
/** The name of the DistributedLockService */
protected String serviceName;
/** The object name */
protected Object objectName;
/** True if objectName identifies a batch of locks */
protected boolean lockBatch;
/** Id of the processor that will handle replies */
protected int processorId;
// set used during processing of this msg...
protected transient DLockService svc;
protected transient DLockGrantor grantor;
public DLockQueryMessage() {
}
@Override
public int getProcessorId() {
return this.processorId;
}
/**
* Processes this message - invoked on the node that is the lock grantor.
*/
@Override
protected void process(final DistributionManager dm) {
boolean failed = true;
ReplyException replyException = null;
try {
this.svc = DLockService.getInternalServiceNamed(this.serviceName);
if (this.svc == null) {
failed = false; // basicProcess has it's own finally-block w reply
basicProcess(dm, false); // don't have a grantor anymore
}
else {
executeBasicProcess(dm); // use executor
}
failed = false; // nothing above threw anything
}
catch (RuntimeException e) {
replyException = new ReplyException(e);
throw e;
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
replyException = new ReplyException(e);
throw e;
}
SystemFailure.checkFailure();
replyException = new ReplyException(e);
throw e;
}
finally {
if (failed) {
// above code failed so now ensure reply is sent
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm,
"DLockQueryMessage.process failed for <" + this + ">");
}
DLockQueryReplyMessage replyMsg = new DLockQueryReplyMessage();
replyMsg.setProcessorId(this.processorId);
replyMsg.setRecipient(getSender());
replyMsg.setException(replyException); // might be null
if (dm.getId().equals(getSender())) {
replyMsg.setSender(getSender());
replyMsg.dmProcess(dm);
}
else {
dm.putOutgoing(replyMsg);
}
}
}
}
/** Process locally without using messaging or executor */
protected void processLocally(final DM dm) {
this.svc = DLockService.getInternalServiceNamed(this.serviceName);
basicProcess(dm, true); // don't use executor
}
/**
* Execute basicProcess inside Pooled Executor because grantor may not
* be initializing which will require us to wait.
*
* this.svc and this.grantor must be set before calling this method.
*/
private void executeBasicProcess(final DM dm) {
final DLockQueryMessage msg = this;
dm.getWaitingThreadPool().execute(new Runnable() {
public void run() {
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[executeBasicProcess] " + msg);
}
basicProcess(dm, true);
}
});
}
/**
* Perform basic processing of this message.
*
* this.svc and this.grantor must be set before calling this method.
*/
protected void basicProcess(final DM dm, final boolean waitForGrantor) {
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[basicProcess] " + this);
}
final DLockQueryReplyMessage replyMsg = new DLockQueryReplyMessage();
replyMsg.setProcessorId(this.processorId);
replyMsg.setRecipient(getSender());
replyMsg.replyCode = DLockQueryReplyMessage.NOT_GRANTOR;
replyMsg.lesseeThread = null;
replyMsg.leaseId = DLockService.INVALID_LEASE_ID;
replyMsg.leaseExpireTime = 0;
try {
if (svc == null || svc.isDestroyed()) return;
if (waitForGrantor) {
try {
this.grantor = DLockGrantor.waitForGrantor(this.svc);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
this.grantor = null;
}
}
if (grantor == null || grantor.isDestroyed()) {
return;
}
if (lockBatch) {
throw new UnsupportedOperationException(
"DLockQueryProcessor does not support lock batches");
}
else {
DLockGrantToken grantToken;
try {
grantToken = grantor.handleLockQuery(this);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
grantToken = null;
}
if (grantToken != null) {
synchronized (grantToken) {
if (!grantToken.isDestroyed()) {
replyMsg.lesseeThread = grantToken.getRemoteThread();
replyMsg.leaseId = grantToken.getLockId();
replyMsg.leaseExpireTime = grantToken.getLeaseExpireTime();
}
}
}
}
replyMsg.replyCode = DLockQueryReplyMessage.OK;
}
catch (LockGrantorDestroyedException ignore) {
}
catch (LockServiceDestroyedException ignore) {
}
catch (RuntimeException e) {
replyMsg.setException(new ReplyException(e));
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[basicProcess] caught RuntimeException", e);
}
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
replyMsg.setException(new ReplyException(e));
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[basicProcess] caught Error", e);
}
}
finally {
if (dm.getId().equals(getSender())) {
replyMsg.setSender(getSender());
replyMsg.dmProcess(dm);
}
else {
dm.putOutgoing(replyMsg);
}
}
}
public int getDSFID() {
return DLOCK_QUERY_MESSAGE;
}
@Override
public void toData(DataOutput out) throws IOException {
super.toData(out);
DataSerializer.writeString(this.serviceName, out);
DataSerializer.writeObject(this.objectName, out);
out.writeBoolean(this.lockBatch);
out.writeInt(this.processorId);
}
@Override
public void fromData(DataInput in)
throws IOException, ClassNotFoundException {
super.fromData(in);
this.serviceName = DataSerializer.readString(in);
this.objectName = DataSerializer.readObject(in);
this.lockBatch = in.readBoolean();
this.processorId = in.readInt();
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("DLockQueryMessage@");
sb.append(Integer.toHexString(hashCode()));
sb.append(", serviceName: ").append(this.serviceName);
sb.append(", objectName: ").append(this.objectName);
sb.append(", lockBatch: ").append(this.lockBatch);
sb.append(", processorId: ").append(this.processorId);
return sb.toString();
}
} // DLockQueryMessage
// -------------------------------------------------------------------------
// DLockQueryReplyMessage
// -------------------------------------------------------------------------
public static final class DLockQueryReplyMessage
extends ReplyMessage {
static final int NOT_GRANTOR = 0;
static final int OK = 1;
/** OK or NOT_GRANTOR for the service */
protected int replyCode = NOT_GRANTOR;
/** Member and thread holding lease on the lock */
protected RemoteThread lesseeThread;
/** Lease id used to hold a lease on the lock */
protected int leaseId;
/** Absolute time in millis when lease will expire */
protected long leaseExpireTime;
public DLockQueryReplyMessage() {
}
/**
* Returns true if the queried grantor replied with the current lease info
* for the named lock.
*
* @return true if the queried grantor replied with the current lease info
*/
boolean repliedOK() {
return this.replyCode == DLockQueryReplyMessage.OK;
}
/**
* Returns true if the queried grantor replied NOT_GRANTOR.
*
* @return true if the queried grantor replied NOT_GRANTOR
*/
boolean repliedNotGrantor() {
return this.replyCode == DLockQueryReplyMessage.NOT_GRANTOR;
}
/**
* Returns the member holding the lease or null if there was no lease.
*
* @return the member holding the lease or null
*/
DistributedMember getLessee() {
if (this.lesseeThread != null) {
return this.lesseeThread.getDistributedMember();
}
else {
return null;
}
}
/**
* Returns the query reply's lesseeThread or null if there was no lease.
*
* @return the query reply's lesseeThread or null
*/
RemoteThread getLesseeThread() {
return this.lesseeThread;
}
/**
* Return the query reply's leaseId or -1 if there was no lease.
*
* @return the query reply's leaseId or -1
*/
int getLeaseId() {
return this.leaseId;
}
/**
* Return the query reply's leaseExpireTime or 0 if there was no lease.
*
* @return the query reply's leaseExpireTime or 0
*/
long getLeaseExpireTime() {
return this.leaseExpireTime;
}
@Override
public int getDSFID() {
return DLOCK_QUERY_REPLY;
}
@Override
public void fromData(DataInput in)
throws IOException, ClassNotFoundException {
super.fromData(in);
this.replyCode = in.readInt();
if (this.replyCode == OK) {
InternalDistributedMember lessee =
(InternalDistributedMember) DataSerializer.readObject(in);
if (lessee != null) {
this.lesseeThread = new RemoteThread(lessee, in.readInt());
}
this.leaseId = in.readInt();
this.leaseExpireTime = in.readLong();
}
}
@Override
public void toData(DataOutput out) throws IOException {
super.toData(out);
out.writeInt(this.replyCode);
if (this.replyCode == OK) {
if (this.lesseeThread == null) {
DataSerializer.writeObject(null, out);
}
else {
DataSerializer.writeObject(this.lesseeThread.getDistributedMember(), out);
out.writeInt(this.lesseeThread.getThreadId());
}
out.writeInt(this.leaseId);
out.writeLong(this.leaseExpireTime);
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("DLockQueryReplyMessage@");
sb.append(Integer.toHexString(hashCode()));
sb.append(", replyCode: ");
switch (this.replyCode) {
case NOT_GRANTOR: sb.append("NOT_GRANTOR"); break;
case OK: sb.append("OK"); break;
default: sb.append(String.valueOf(this.replyCode)); break;
}
sb.append(", lesseeThread: ").append(this.lesseeThread);
sb.append(", leaseId: ").append(this.leaseId);
sb.append(", leaseExpireTime: ").append(this.leaseExpireTime);
sb.append(", processorId: ").append(this.processorId);
return sb.toString();
}
} // DLockQueryReplyMessage
}