/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. 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.
*/
package com.hazelcast.impl;
import com.hazelcast.cluster.RemotelyProcessable;
import com.hazelcast.core.MapEntry;
import com.hazelcast.core.Member;
import com.hazelcast.core.Prefix;
import com.hazelcast.impl.base.*;
import com.hazelcast.impl.concurrentmap.MapSystemLogFactory;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.Connection;
import com.hazelcast.nio.Data;
import com.hazelcast.nio.Packet;
import com.hazelcast.util.CounterService;
import com.hazelcast.util.ResponseQueueFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import static com.hazelcast.core.Instance.InstanceType;
import static com.hazelcast.impl.Constants.Objects.OBJECT_NULL;
import static com.hazelcast.impl.Constants.Objects.OBJECT_REDO;
import static com.hazelcast.impl.Constants.ResponseTypes.*;
import static com.hazelcast.impl.base.SystemLogService.Level.CS_INFO;
import static com.hazelcast.nio.IOUtil.toData;
import static com.hazelcast.nio.IOUtil.toObject;
public abstract class BaseManager {
protected final List<MemberImpl> lsMembers;
protected final Map<Address, MemberImpl> mapMembers;
protected final Queue<Packet> qServiceThreadPacketCache;
protected final Map<Long, Call> mapCalls;
protected final AtomicLong localIdGen;
protected final Address thisAddress;
protected final MemberImpl thisMember;
protected final Node node;
protected final ILogger logger;
protected final long redoWaitMillis;
protected final SystemLogService systemLogService;
protected BaseManager(Node node) {
this.node = node;
systemLogService = node.getSystemLogService();
lsMembers = node.baseVariables.lsMembers;
mapMembers = node.baseVariables.mapMembers;
mapCalls = node.baseVariables.mapCalls;
thisAddress = node.baseVariables.thisAddress;
thisMember = node.baseVariables.thisMember;
qServiceThreadPacketCache = node.baseVariables.qServiceThreadPacketCache;
this.localIdGen = node.baseVariables.localIdGen;
this.logger = node.getLogger(this.getClass().getName());
this.redoWaitMillis = node.getGroupProperties().REDO_WAIT_MILLIS.getLong();
}
public List<MemberImpl> getMembers() {
return lsMembers;
}
public Address getThisAddress() {
return thisAddress;
}
public Node getNode() {
return node;
}
public static MapEntry createSimpleMapEntry(final FactoryImpl factory, final String name, final Object key, final Data value) {
return new MapEntry() {
public Object getKey() {
return key;
}
public Object getValue() {
return toObject(value);
}
public Object setValue(Object newValue) {
return ((MProxy) factory.getOrCreateProxyByName(name)).put(key, newValue);
}
public long getCost() {
return 0;
}
public long getCreationTime() {
return 0;
}
public long getExpirationTime() {
return 0;
}
public int getHits() {
return 0;
}
public long getLastAccessTime() {
return 0;
}
public long getLastStoredTime() {
return 0;
}
public long getLastUpdateTime() {
return 0;
}
public long getVersion() {
return 0;
}
public boolean isValid() {
return false;
}
@Override
public String toString() {
return "Map.Entry key=" + getKey() + ", value=" + getValue();
}
};
}
protected void rethrowException(ClusterOperation operation, AddressAwareException exception) {
String msg = operation + " failed at " + thisAddress
+ " because of an exception thrown at " + exception.getAddress();
throw new RuntimeException(msg, exception.getException());
}
abstract class AbstractCall implements Call, CallStateAware {
protected long callId = -1;
protected long firstEnqueueTime = -1;
protected int enqueueCount = 0;
protected CallState callState = null;
public AbstractCall() {
initCall();
}
public CallState getCallState() {
return callState;
}
protected void initCall() {
callId = localIdGen.incrementAndGet();
int threadId = ThreadContext.get().getThreadId();
callState = node.getSystemLogService().getOrCreateCallState(callId, thisAddress, threadId);
}
public long getCallId() {
return callId;
}
public void onDisconnect(final Address dead) {
}
public void onEnqueue() {
if (firstEnqueueTime == -1) {
firstEnqueueTime = System.currentTimeMillis();
}
enqueueCount++;
}
public void redo() {
removeRemoteCall(getCallId());
enqueueCall(this);
}
public void setCallId(long callId) {
this.callId = callId;
}
public void reset() {
initCall();
firstEnqueueTime = -1;
enqueueCount = 0;
}
public int getEnqueueCount() {
return enqueueCount;
}
protected int getDurationSeconds() {
return (int) (System.currentTimeMillis() - firstEnqueueTime) / 1000;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{[" +
"" + getCallId() +
"], duration=" + (+getDurationSeconds()) +
"sn., enqueueCount=" + getEnqueueCount() +
'}';
}
}
abstract class MigrationAwareOperationHandler extends AbstractOperationHandler {
@Override
public void process(Packet packet) {
super.processMigrationAware(packet);
}
}
abstract class TargetAwareOperationHandler extends MigrationAwareOperationHandler {
abstract boolean isRightRemoteTarget(Request request);
@Override
public void process(Packet packet) {
Request remoteReq = Request.copy(packet);
boolean isMigrating = isMigrating(remoteReq);
boolean rightRemoteTarget = isRightRemoteTarget(remoteReq);
SystemLogService css = node.getSystemLogService();
if (css.shouldLog(CS_INFO)) {
css.info(remoteReq, "IsMigrating/RightRemoteTarget", isMigrating, rightRemoteTarget);
}
if (isMigrating || !rightRemoteTarget) {
remoteReq.clearForResponse();
returnRedoResponse(remoteReq);
} else {
if (css.shouldLog(CS_INFO)) {
css.info(remoteReq, "handle");
}
handle(remoteReq);
}
releasePacket(packet);
}
}
public abstract class ResponsiveOperationHandler implements PacketProcessor, RequestHandler {
public void process(Packet packet) {
processSimple(packet);
}
public void processSimple(Packet packet) {
Request request = Request.copy(packet);
handle(request);
releasePacket(packet);
}
public void processMigrationAware(Packet packet) {
Request remoteReq = Request.copy(packet);
if (isMigrating(remoteReq)) {
remoteReq.clearForResponse();
returnRedoResponse(remoteReq);
} else {
handle(remoteReq);
}
releasePacket(packet);
}
}
public class ReturnResponseProcess implements Processable {
private final Request request;
public ReturnResponseProcess(Request request) {
this.request = request;
}
public void process() {
returnResponse(request);
}
}
public boolean returnRedoResponse(Request request) {
request.response = OBJECT_REDO;
return returnResponse(request, null);
}
public boolean returnResponse(Request request) {
return returnResponse(request, null);
}
public boolean returnResponse(Request request, Connection conn) {
SystemLogService css = node.getSystemLogService();
if (css.shouldLog(CS_INFO)) {
css.logObject(request, CS_INFO, "ReturnResponse");
}
if (request.local) {
final TargetAwareOp targetAwareOp = (TargetAwareOp) request.attachment;
targetAwareOp.setResult(request.response);
CounterService.serviceCounter.add(System.nanoTime() - request.lastTime);
} else {
Packet packet = obtainPacket();
request.setPacket(packet);
packet.operation = ClusterOperation.RESPONSE;
packet.responseType = RESPONSE_SUCCESS;
packet.longValue = request.longValue;
if (request.value != null) {
packet.setValue(request.value);
}
if (request.response == OBJECT_REDO) {
packet.lockAddress = null;
packet.responseType = RESPONSE_REDO;
if (systemLogService.shouldInfo()) {
systemLogService.info(request, "Returning REDO response");
}
} else if (request.response != null) {
if (request.response instanceof Boolean) {
if (request.response == Boolean.FALSE) {
packet.responseType = RESPONSE_FAILURE;
}
} else if (request.response instanceof Long) {
packet.longValue = (Long) request.response;
} else {
Data data;
if (request.response instanceof Data) {
data = (Data) request.response;
} else {
data = toData(request.response);
}
if (data != null && data.size() > 0) {
packet.setValue(data);
}
}
}
if (conn != null) {
conn.getWriteHandler().enqueueSocketWritable(packet);
} else {
return sendResponse(packet, request.caller);
}
}
return true;
}
abstract class AbstractOperationHandler extends ResponsiveOperationHandler {
public void process(Packet packet) {
processSimple(packet);
}
abstract void doOperation(Request request);
public void handle(Request request) {
doOperation(request);
returnResponse(request);
}
}
abstract class RequestBasedCall extends AbstractCall {
final protected Request request = new Request();
protected RequestBasedCall() {
request.callState = callState;
}
public boolean booleanCall(final ClusterOperation operation, final String name, final Object key,
final Object value, final long timeout, final long recordId) {
setLocal(operation, name, key, value, timeout, recordId);
request.setBooleanRequest();
doOp();
return getResultAsBoolean();
}
public void reset() {
request.reset();
super.reset();
request.callState = callState;
}
public void clearRequest() {
request.response = null;
request.value = null;
}
public boolean getResultAsBoolean() {
Object resultObj = getResult();
if (resultObj instanceof Data) {
resultObj = toObject((Data) resultObj);
if (resultObj instanceof AddressAwareException) {
rethrowException(request.operation, (AddressAwareException) resultObj);
}
}
boolean result = Boolean.TRUE.equals(resultObj);
afterGettingResult(request);
return result;
}
public Object getResultAsObject() {
return getResultAsObject(true);
}
public Object getResultAsObject(boolean force) {
Object result = getResult();
if (result == OBJECT_NULL || result == null) {
result = null;
} else {
if (result instanceof Data) {
final Data data = (Data) result;
if (ThreadContext.get().isClient() && force) {
result = data;
} else {
if (data.size() == 0) {
result = null;
} else {
result = toObject(data);
}
}
}
}
afterGettingResult(request);
return result;
}
public Object getResultAsIs() {
Object result = getResult();
if (result == OBJECT_NULL || result == null) {
result = null;
} else {
return result;
}
afterGettingResult(request);
return result;
}
protected void afterGettingResult(Request request) {
}
public Object objectCall() {
request.setObjectRequest();
doOp();
return getResultAsObject();
}
public Object objectCall(final ClusterOperation operation, final String name, final Object key,
final Object value, final long timeout, final long ttl) {
setLocal(operation, name, key, value, timeout, ttl);
return objectCall();
}
public void setLocal(final ClusterOperation operation, final String name, final Object key,
final Object value, final long timeout, final long ttl) {
Data keyData = null;
Data valueData = null;
if (key != null) {
keyData = toData(key);
if (keyData.size() == 0) {
throw new RuntimeException(name + " Key with zero-size " + operation);
}
}
if (value != null) {
valueData = toData(value);
}
request.setLocal(operation, name, keyData, valueData, -1, timeout, ttl, thisAddress);
request.attachment = this;
}
abstract void doOp();
abstract Object getResult();
@Override
public String toString() {
return this.getClass().getSimpleName() + "{[" +
"" + getCallId() +
"], duration=" + (+getDurationSeconds()) +
"sn., enqueueCount=" + getEnqueueCount() +
", " + request +
'}';
}
}
protected void handleInterruptedException() {
if (node.factory.restarted) {
throw new RuntimeException();
} else {
throw new RuntimeInterruptedException(Thread.currentThread().toString() + " is interrupted.");
}
}
public abstract class ResponseQueueCall extends RequestBasedCall {
private final BlockingQueue<Object> responses = ResponseQueueFactory.newResponseQueue();
public ResponseQueueCall() {
}
@Override
public void doOp() {
responses.clear();
enqueueCall(ResponseQueueCall.this);
}
public void beforeRedo() {
request.beforeRedo();
node.checkNodeState();
}
public Object getResult(long time, TimeUnit unit) throws InterruptedException {
return responses.poll(time, unit);
}
public boolean getResultAsBoolean(int timeoutSeconds) {
Object resultObj = null;
try {
resultObj = getResult(timeoutSeconds, TimeUnit.SECONDS);
} catch (InterruptedException e) {
handleInterruptedException();
}
boolean result = Boolean.TRUE.equals(resultObj);
afterGettingResult(request);
return result;
}
protected void onStillWaiting() {
}
public Object waitAndGetResult() {
while (true) {
try {
Object obj = responses.poll(10, TimeUnit.SECONDS);
if (obj != null) {
return obj;
}
if (node.isActive()) {
logger.log(Level.FINEST, "Still no response! " + request);
SystemLogService css = node.getSystemLogService();
if (css.shouldTrace()) {
css.trace(this, "Still no response");
}
}
node.checkNodeState();
if (Thread.interrupted()) {
handleInterruptedException();
}
onStillWaiting();
} catch (InterruptedException e) {
handleInterruptedException();
}
}
}
@Override
public Object getResult() {
return getRedoAwareResult();
}
protected final Object getRedoAwareResult() {
for (; ; ) {
Object result = waitAndGetResult();
if (Thread.interrupted()) {
handleInterruptedException();
}
if (result == OBJECT_REDO) {
request.redoCount++;
SystemLogService css = node.getSystemLogService();
if (css.shouldTrace()) {
css.trace(this, MapSystemLogFactory.newRedoLog(node, request));
}
if (request.redoCount > 19 && (request.redoCount % 10 == 0)) {
logger.log(Level.WARNING, MapSystemLogFactory.newRedoLog(node, request).toString());
}
try {
//noinspection BusyWait
Thread.sleep(redoWaitMillis);
} catch (InterruptedException e) {
handleInterruptedException();
}
beforeRedo();
doOp();
continue;
}
return result;
}
}
protected Address getTarget() {
return thisAddress;
}
@Override
public void redo() {
removeRemoteCall(getCallId());
responses.clear();
setResult(OBJECT_REDO);
}
public void reset() {
if (getCallId() != -1) {
removeRemoteCall(getCallId());
}
super.reset();
}
private void handleBooleanNoneRedoResponse(final Packet packet) {
if (packet.responseType == Constants.ResponseTypes.RESPONSE_SUCCESS) {
setResult(Boolean.TRUE);
} else {
setResult(Boolean.FALSE);
}
}
private void handleLongNoneRedoResponse(final Packet packet) {
if (packet.responseType == Constants.ResponseTypes.RESPONSE_SUCCESS) {
setResult(packet.longValue);
} else {
throw new RuntimeException("handleLongNoneRedoResponse.responseType "
+ packet.responseType);
}
}
private void handleObjectNoneRedoResponse(final Packet packet) {
if (packet.responseType == Constants.ResponseTypes.RESPONSE_SUCCESS) {
final Data oldValue = packet.getValueData();
if (oldValue == null || oldValue.size() == 0) {
setResult(OBJECT_NULL);
} else {
setResult(oldValue);
}
} else {
throw new RuntimeException(request.operation + " handleObjectNoneRedoResponse.responseType "
+ packet.responseType);
}
}
protected void handleNoneRedoResponse(final Packet packet) {
removeRemoteCall(getCallId());
if (request.isBooleanRequest()) {
handleBooleanNoneRedoResponse(packet);
} else if (request.isLongRequest()) {
handleLongNoneRedoResponse(packet);
} else if (request.isObjectRequest()) {
handleObjectNoneRedoResponse(packet);
} else {
throw new RuntimeException(request.operation + " Unknown request.responseType. " + request.responseType);
}
}
protected void setResult(final Object obj) {
if (obj == OBJECT_REDO) {
if (systemLogService.shouldInfo()) {
systemLogService.info(request, "setResult(REDO)");
}
}
responses.offer(obj == null ? OBJECT_NULL : obj);
}
}
public abstract class ConnectionAwareOp extends ResponseQueueCall {
final protected Connection targetConnection;
public ConnectionAwareOp(Connection targetConnection) {
this.targetConnection = targetConnection;
}
public void handleResponse(final Packet packet) {
if (packet.responseType == RESPONSE_REDO) {
redo();
} else {
handleNoneRedoResponse(packet);
}
releasePacket(packet);
}
@Override
public void onDisconnect(final Address dead) {
}
public void reset() {
super.reset();
}
public void process() {
invoke();
}
protected void invoke() {
addRemoteCall(ConnectionAwareOp.this);
final Packet packet = obtainPacket();
request.setPacket(packet);
packet.callId = getCallId();
request.callId = getCallId();
final boolean sent = send(packet, targetConnection);
if (!sent) {
logger.log(Level.FINEST, ConnectionAwareOp.this + " Packet cannot be sent to " + targetConnection);
releasePacket(packet);
packetNotSent();
}
}
protected void packetNotSent() {
setResult(new IOException("Connection is lost!"));
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{[" +
"" + getCallId() +
"], firstEnqueue=" + (System.currentTimeMillis() - firstEnqueueTime) / 1000 +
"sn., enqueueCount=" + enqueueCount +
", " + request +
", target=" + getTarget() +
'}';
}
}
public abstract class TargetAwareOp extends ResponseQueueCall {
protected Address target = null;
protected Connection targetConnection = null;
public TargetAwareOp() {
}
public void handleResponse(final Packet packet) {
if (packet.responseType == RESPONSE_REDO) {
redo();
} else {
handleNoneRedoResponse(packet);
}
doReleasePacket(packet);
}
protected void doReleasePacket(Packet packet) {
releasePacket(packet);
}
protected Packet doObtainPacket() {
return obtainPacket();
}
@Override
protected void onStillWaiting() {
enqueueAndReturn(new Processable() {
public void process() {
if (targetConnection != null && !targetConnection.live()) {
redo();
}
}
});
}
@Override
public void onDisconnect(final Address dead) {
if (dead.equals(target)) {
target = null;
redo();
}
}
public void reset() {
super.reset();
target = null;
targetConnection = null;
}
@Override
public void beforeRedo() {
logger.log(Level.FINEST, request.operation + " BeforeRedo target " + target);
super.beforeRedo();
}
public void process() {
request.lastTime = System.nanoTime();
request.caller = thisAddress;
setTarget();
request.target = target;
if (systemLogService.shouldTrace()) {
systemLogService.trace(request, "target: " + target);
}
if (target == null) {
setResult(OBJECT_REDO);
} else {
if (target.equals(thisAddress)) {
request.callId = getCallId();
doLocalOp();
} else {
invoke();
}
}
}
protected boolean memberOnly() {
return true;
}
protected void memberDoesNotExist() {
setResult(OBJECT_REDO);
}
protected void invoke() {
if (memberOnly() && getMember(target) == null) {
if (systemLogService.shouldInfo()) {
systemLogService.info(request, "remote target doesn't exist: " + target);
}
memberDoesNotExist();
} else {
addRemoteCall(TargetAwareOp.this);
final Packet packet = doObtainPacket();
request.setPacket(packet);
packet.callId = getCallId();
request.callId = getCallId();
targetConnection = node.connectionManager.getOrConnect(target);
boolean sent = send(packet, targetConnection);
if (!sent) {
targetConnection = null;
logger.log(Level.FINEST, TargetAwareOp.this + " Packet cannot be sent to " + target);
releasePacket(packet);
packetNotSent();
}
}
}
protected void packetNotSent() {
redo();
}
public void doLocalOp() {
if (isMigrationAware() && isMigrating(request)) {
setResult(OBJECT_REDO);
} else {
request.attachment = TargetAwareOp.this;
request.local = true;
((RequestHandler) getPacketProcessor(request.operation)).handle(request);
}
}
public abstract void setTarget();
@Override
public Address getTarget() {
return target;
}
public boolean isMigrationAware() {
return false;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{[" +
"" + getCallId() +
"], firstEnqueue=" + (System.currentTimeMillis() - firstEnqueueTime) / 1000 +
"sn., enqueueCount=" + enqueueCount +
", " + request +
", target=" + getTarget() +
'}';
}
}
abstract class MultiCall<T> {
int redoCount = 0;
private void logRedo(SubCall subCall) {
redoCount++;
if (redoCount >= 20 && redoCount % 20 == 0) {
logger.log(Level.WARNING, buildRedoLog(subCall));
}
}
private String buildRedoLog(SubCall subCall) {
StringBuilder s = new StringBuilder();
s.append("=========== REDO LOG =========== ");
s.append(MultiCall.this.getClass().getName());
s.append(" Redoing ");
s.append(subCall.request);
s.append("\n");
s.append(node.getClusterImpl());
s.append("\n============================== ");
return s.toString();
}
abstract SubCall createNewTargetAwareOp(Address target);
/**
* As MultiCall receives the responses from the target members
* it will pass each response to the extending call so that it can
* consume and check if the call should continue.
*
* @param response response object from one of the targets
* @return false if call is completed.
*/
abstract boolean onResponse(Object response);
void onComplete() {
}
void onRedo() {
}
void onCall() {
}
abstract Object returnResult();
protected Address getFirstAddressToMakeCall() {
return thisAddress;
}
T call() {
try {
node.checkNodeState();
onCall();
//local call first
Object result = null;
final boolean excludeThisMember = excludeLiteMember() && thisMember.isLiteMember();
if (!excludeThisMember) {
SubCall localCall = createNewTargetAwareOp(getFirstAddressToMakeCall());
localCall.doOp();
result = localCall.getResultAsObject();
if (result == OBJECT_REDO) {
logRedo(localCall);
onRedo();
Thread.sleep(redoWaitMillis);
return call();
}
}
// now other members
final boolean runOnOtherMembers = excludeThisMember || onResponse(result);
if (runOnOtherMembers) {
Set<Member> members = node.getClusterImpl().getMembers();
List<SubCall> lsCalls = new ArrayList<SubCall>();
for (Member member : members) {
MemberImpl cMember = (MemberImpl) member;
final boolean excludeMember = excludeLiteMember() && cMember.isLiteMember();
if (!excludeMember && !cMember.getAddress().equals(getFirstAddressToMakeCall())) {
SubCall subCall = createNewTargetAwareOp(cMember.getAddress());
subCall.doOp();
lsCalls.add(subCall);
}
}
for (SubCall call : lsCalls) {
result = call.getResultAsObject();
if (result == OBJECT_REDO) {
logRedo(call);
onRedo();
//noinspection BusyWait
Thread.sleep(redoWaitMillis);
return call();
} else {
if (!onResponse(result)) {
break;
}
}
}
onComplete();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return (T) returnResult();
}
protected abstract boolean excludeLiteMember();
}
abstract class SubCall extends TargetAwareOp {
public SubCall(final Address target) {
this.target = target;
if (target == null) {
throw new IllegalArgumentException("SubCall target cannot be " + target);
}
}
public void onDisconnect(final Address dead) {
removeRemoteCall(getCallId());
setResult(OBJECT_REDO);
}
@Override
public void setTarget() {
}
@Override
protected void memberDoesNotExist() {
setResult(OBJECT_REDO);
}
@Override
public Object getResult() {
// we don't want to REDO automatically
// MultiCall will control this
return waitAndGetResult();
}
@Override
public boolean isMigrationAware() {
return false;
}
}
protected boolean isMigrating(Request req) {
return false;
}
public static InstanceType getInstanceType(final String name) {
if (name.startsWith(Prefix.ATOMIC_NUMBER)) {
return InstanceType.ATOMIC_NUMBER;
} else if (name.startsWith(Prefix.COUNT_DOWN_LATCH)) {
return InstanceType.COUNT_DOWN_LATCH;
} else if (name.startsWith(Prefix.IDGEN)) {
return InstanceType.ID_GENERATOR;
} else if (name.startsWith(Prefix.AS_LIST)) {
return InstanceType.LIST;
} else if (name.startsWith(Prefix.MAP)) {
return InstanceType.MAP;
} else if (name.startsWith(Prefix.MULTIMAP)) {
return InstanceType.MULTIMAP;
} else if (name.startsWith(Prefix.QUEUE)) {
return InstanceType.QUEUE;
} else if (name.startsWith(Prefix.SEMAPHORE)) {
return InstanceType.SEMAPHORE;
} else if (name.startsWith(Prefix.SET)) {
return InstanceType.SET;
} else if (name.startsWith(Prefix.TOPIC)) {
return InstanceType.TOPIC;
} else {
throw new RuntimeException("Unknown InstanceType " + name);
}
}
public void enqueueCall(Call call) {
call.onEnqueue();
enqueueAndReturn(call);
}
public void enqueueAndReturn(final Processable obj) {
node.clusterService.enqueueAndReturn(obj);
}
public boolean enqueueAndWait(final Processable processable, final int seconds) {
return node.clusterService.enqueueAndWait(processable, seconds);
}
public void enqueueAndWait(final Processable processable) {
node.clusterService.enqueueAndWait(processable);
}
public Packet obtainPacket(String name, Object key, Object value,
ClusterOperation operation, long timeout) {
final Packet packet = obtainPacket();
packet.set(name, operation, key, value);
packet.timeout = timeout;
return packet;
}
public Call getRemoteCall(long id) {
return mapCalls.get(id);
}
public void addRemoteCall(Call call) {
mapCalls.put(call.getCallId(), call);
}
public Call removeRemoteCall(long id) {
return mapCalls.remove(id);
}
public void registerPacketProcessor(ClusterOperation operation, PacketProcessor packetProcessor) {
node.clusterService.registerPacketProcessor(operation, packetProcessor);
}
public PacketProcessor getPacketProcessor(ClusterOperation operation) {
return node.clusterService.getPacketProcessor(operation);
}
public void sendEvents(int eventType, String name, Data key, Data value, Map<Address, Boolean> mapListeners, Address callerAddress) {
if (mapListeners != null) {
checkServiceThread();
final Set<Map.Entry<Address, Boolean>> listeners = mapListeners.entrySet();
for (final Map.Entry<Address, Boolean> listener : listeners) {
final Address toAddress = listener.getKey();
final boolean includeValue = listener.getValue();
if (toAddress.equals(thisAddress)) {
// During local event listener calls, no need to check for include value flag.
// ListenerManager checks internally include value flag for each listener.
// By this way we can handle scenario of successively registered
// a LocalEntryListener (which sets implicitly include-value to true)
// and an EntryListener whose include-value is false.
enqueueEvent(eventType, name, key, /*(includeValue) ? value : null*/ value, callerAddress, true);
} else {
final Packet packet = obtainPacket();
packet.set(name, ClusterOperation.EVENT, key, (includeValue) ? value : null);
packet.lockAddress = callerAddress;
packet.longValue = eventType;
final boolean sent = send(packet, toAddress);
if (!sent)
releasePacket(packet);
}
}
}
}
public Packet createRemotelyProcessablePacket(RemotelyProcessable rp) {
Data value = ThreadContext.get().toData(rp);
Packet packet = obtainPacket();
packet.set("remotelyProcess", ClusterOperation.REMOTELY_PROCESS, null, value);
return packet;
}
public void sendProcessableTo(RemotelyProcessable rp, Connection conn) {
Packet packet = createRemotelyProcessablePacket(rp);
boolean sent = send(packet, conn);
if (!sent) {
releasePacket(packet);
}
}
public boolean sendProcessableTo(final RemotelyProcessable rp, final Address address) {
final Data value = toData(rp);
final Packet packet = obtainPacket();
packet.set("remotelyProcess", ClusterOperation.REMOTELY_PROCESS, null, value);
final boolean sent = send(packet, address);
if (!sent) {
releasePacket(packet);
}
return sent;
}
public void sendProcessableToAll(RemotelyProcessable rp, boolean processLocally) {
if (processLocally) {
rp.setNode(node);
rp.process();
}
Data value = toData(rp);
for (MemberImpl member : lsMembers) {
if (!member.localMember()) {
Packet packet = obtainPacket();
packet.set("remotelyProcess", ClusterOperation.REMOTELY_PROCESS, null, value);
boolean sent = send(packet, member.getAddress());
if (!sent) {
releasePacket(packet);
}
}
}
}
public void executeLocally(Runnable runnable) {
node.executorManager.executeLocally(runnable);
}
protected Address getMasterAddress() {
return node.getMasterAddress();
}
protected MemberImpl getNextMemberAfter(final Address address,
final boolean skipSuperClient,
final int distance) {
return getNextMemberAfter(lsMembers, address, skipSuperClient, distance);
}
protected MemberImpl getNextMemberAfter(final List<MemberImpl> lsMembers,
final Address address,
final boolean skipSuperClient,
final int distance) {
final int size = lsMembers.size();
if (size <= 1)
return null;
int indexOfMember = -1;
for (int i = 0; i < size; i++) {
final MemberImpl member = lsMembers.get(i);
if (member.getAddress().equals(address)) {
indexOfMember = i;
}
}
if (indexOfMember == -1)
return null;
int foundDistance = 0;
for (int i = indexOfMember; i < size + indexOfMember; i++) {
final MemberImpl member = lsMembers.get((1 + i) % size);
if (!(skipSuperClient && member.isLiteMember())) {
foundDistance++;
}
if (foundDistance == distance) {
return member;
}
}
return null;
}
protected boolean isMaster() {
return node.isMaster();
}
protected boolean isActive() {
return node.isActive();
}
protected boolean isLiteMember() {
return node.isLiteMember();
}
protected Packet obtainPacket() {
if (Thread.currentThread() != node.serviceThread) {
return new Packet();
} else {
Packet packet = node.serviceThreadPacketQueue.poll();
if (packet == null) {
packet = new Packet();
} else {
packet.reset();
}
return packet;
}
}
protected boolean releasePacket(Packet packet) {
return Thread.currentThread() == node.serviceThread && node.serviceThreadPacketQueue.offer(packet);
}
protected boolean sendResponse(final Packet packet) {
packet.operation = ClusterOperation.RESPONSE;
if (packet.responseType == RESPONSE_NONE) {
packet.responseType = RESPONSE_SUCCESS;
} else if (packet.responseType == RESPONSE_REDO) {
packet.lockAddress = null;
}
final boolean sent = send(packet, packet.conn);
if (!sent) {
releasePacket(packet);
}
return sent;
}
protected boolean sendResponse(final Packet packet, final Address address) {
packet.conn = node.connectionManager.getOrConnect(address);
return sendResponse(packet);
}
protected boolean sendResponseFailure(final Packet packet) {
packet.operation = ClusterOperation.RESPONSE;
packet.responseType = RESPONSE_FAILURE;
final boolean sent = send(packet, packet.conn);
if (!sent) {
releasePacket(packet);
}
return sent;
}
protected void throwCME(final Object key) {
throw new ConcurrentModificationException("Another thread holds a lock for the key : "
+ key);
}
void enqueueEvent(int eventType, String name, Data key, Data value, Address from, boolean localEvent) {
try {
Member member = getMember(from);
if (member == null) {
member = new MemberImpl(from, thisAddress.equals(from));
}
Data newValue = value;
Data oldValue = null;
if (value != null && getInstanceType(name).isMap()) {
Keys keys = (Keys) toObject(value);
Collection<Data> values = keys.getKeys();
if (values != null) {
Iterator<Data> it = values.iterator();
if (it.hasNext()) {
newValue = it.next();
}
if (it.hasNext()) {
oldValue = it.next();
}
}
}
final DataAwareEntryEvent dataAwareEntryEvent = new DataAwareEntryEvent(member, eventType, name, key, newValue, oldValue, localEvent);
int hash;
if (key != null) {
hash = key.hashCode();
} else {
hash = hashTwo(from.hashCode(), name.hashCode());
}
node.executorManager.getEventExecutorService().executeOrderedRunnable(hash, new Runnable() {
public void run() {
try {
node.listenerManager.callListeners(dataAwareEntryEvent);
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
});
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
public final void checkServiceThread() {
if (Thread.currentThread() != node.serviceThread) {
String msg = "Only ServiceThread can access this method. " + Thread.currentThread();
logger.log(Level.SEVERE, msg);
throw new Error(msg);
}
}
static int hashTwo(int hash1, int hash2) {
return hash1 * 29 + hash2;
}
void fireMapEvent(final Map<Address, Boolean> mapListeners, final String name,
final int eventType, final Data value, Address callerAddress) {
fireMapEvent(mapListeners, name, eventType, null, value, callerAddress);
}
void fireMapEvent(final Map<Address, Boolean> mapListeners, final String name,
final int eventType, final Data oldValue, final Data value, Address callerAddress) {
fireMapEvent(mapListeners, name, eventType, null, oldValue, value, null, callerAddress);
}
void fireMapEvent(final Map<Address, Boolean> mapListeners, final String name,
final int eventType, final Data key, final Data oldValue, final Data value,
Map<Address, Boolean> keyListeners, Address callerAddress) {
if (keyListeners == null && (mapListeners == null || mapListeners.size() == 0)) {
return;
}
try {
Map<Address, Boolean> mapTargetListeners = null;
if (keyListeners != null) {
mapTargetListeners = new HashMap<Address, Boolean>(keyListeners);
}
if (mapListeners != null && mapListeners.size() > 0) {
if (mapTargetListeners == null) {
mapTargetListeners = new HashMap<Address, Boolean>(mapListeners);
} else {
final Set<Map.Entry<Address, Boolean>> entries = mapListeners.entrySet();
for (final Map.Entry<Address, Boolean> entry : entries) {
if (mapTargetListeners.containsKey(entry.getKey())) {
if (entry.getValue()) {
mapTargetListeners.put(entry.getKey(), entry.getValue());
}
} else
mapTargetListeners.put(entry.getKey(), entry.getValue());
}
}
}
if (mapTargetListeners == null || mapTargetListeners.size() == 0) {
return;
}
Data packetValue = value;
if (value != null && getInstanceType(name).isMap()) {
Keys keys = new Keys();
keys.add(value);
if (oldValue != null) {
keys.add(oldValue);
}
packetValue = toData(keys);
}
sendEvents(eventType, name, key, packetValue, mapTargetListeners, callerAddress);
} catch (final Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
MemberImpl getMember(Address address) {
return node.clusterManager.getMember(address);
}
void registerListener(boolean add, String name, Data key, Address address, boolean includeValue) {
if (name.startsWith(Prefix.QUEUE)) {
node.blockingQueueManager.registerListener(add, name, key, address, includeValue);
} else if (name.startsWith(Prefix.TOPIC)) {
node.topicManager.registerListener(add, name, key, address, includeValue);
} else {
node.concurrentMapManager.registerListener(add, name, key, address, includeValue);
}
}
public final void handleResponse(Packet packetResponse) {
final Call call = getRemoteCall(packetResponse.callId);
if (call != null) {
call.handleResponse(packetResponse);
} else {
logger.log(Level.FINEST, packetResponse.operation + " No call for callId " + packetResponse.callId);
releasePacket(packetResponse);
}
}
protected boolean send(Packet packet, Address address) {
if (address == null) return false;
final Connection conn = node.connectionManager.getOrConnect(address);
return conn != null && conn.live() && writePacket(conn, packet);
}
protected final boolean send(Packet packet, Connection conn) {
return conn != null && conn.live() && writePacket(conn, packet);
}
protected final boolean sendOrReleasePacket(Packet packet, Connection conn) {
if (conn != null && conn.live() && writePacket(conn, packet)) {
return true;
}
releasePacket(packet);
return false;
}
private boolean writePacket(Connection conn, Packet packet) {
final MemberImpl memberImpl = getMember(conn.getEndPoint());
if (memberImpl != null) {
memberImpl.didWrite();
}
if (packet.lockAddress != null) {
if (thisAddress.equals(packet.lockAddress)) {
packet.lockAddress = null;
}
}
conn.getWriteHandler().enqueueSocketWritable(packet);
return true;
}
}