All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.spi.impl.BasicOperationService Maven / Gradle / Ivy

There is a newer version: 5.0-BETA-1
Show newest version
/*
 * Copyright (c) 2008-2013, Hazelcast, 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.
 */

package com.hazelcast.spi.impl;

import com.hazelcast.core.HazelcastException;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.MemberLeftException;
import com.hazelcast.instance.MemberImpl;
import com.hazelcast.instance.Node;
import com.hazelcast.instance.OutOfMemoryErrorDispatcher;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.Connection;
import com.hazelcast.nio.Packet;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.partition.InternalPartition;
import com.hazelcast.partition.InternalPartitionService;
import com.hazelcast.partition.ReplicaErrorLogger;
import com.hazelcast.spi.BackupAwareOperation;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.ExecutionTracingService;
import com.hazelcast.spi.InternalCompletableFuture;
import com.hazelcast.spi.InvocationBuilder;
import com.hazelcast.spi.Notifier;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.OperationAccessor;
import com.hazelcast.spi.OperationFactory;
import com.hazelcast.spi.OperationService;
import com.hazelcast.spi.PartitionAwareOperation;
import com.hazelcast.spi.ReadonlyOperation;
import com.hazelcast.spi.ResponseHandler;
import com.hazelcast.spi.UrgentSystemOperation;
import com.hazelcast.spi.WaitSupport;
import com.hazelcast.spi.annotation.PrivateApi;
import com.hazelcast.spi.exception.CallTimeoutException;
import com.hazelcast.spi.exception.CallerNotMemberException;
import com.hazelcast.spi.exception.PartitionMigratingException;
import com.hazelcast.spi.exception.WrongTargetException;
import com.hazelcast.spi.impl.PartitionIteratingOperation.PartitionResponse;
import com.hazelcast.util.Clock;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static com.hazelcast.spi.OperationAccessor.isJoinOperation;
import static com.hazelcast.spi.OperationAccessor.setCallId;

/**
 * This is the Basic InternalOperationService and depends on Java 6.
 * 

* All the classes that begin with 'Basic' are implementation detail that depend on the * {@link com.hazelcast.spi.impl.BasicOperationService}. *

*

System Operation

* When a {@link com.hazelcast.spi.UrgentSystemOperation} is invoked on this OperationService, it will be executed with a * high urgency by making use of a urgent queue. So when the system is under load, and the operation queues are * filled, then system operations are executed before normal operation. The advantage is that when a system is under * pressure, it still is able to do things like recognizing new members in the cluster and moving partitions around. *

* When a UrgentSystemOperation is send to a remote machine, it is wrapped in a {@link Packet} and the packet is marked as a * urgent packet. When this packet is received on the remove OperationService, the urgent flag is checked and if * needed, the operation is set on the urgent queue. So local and remote execution of System operations will obey * the urgency. * * @see com.hazelcast.spi.impl.BasicInvocation * @see com.hazelcast.spi.impl.BasicInvocationBuilder * @see com.hazelcast.spi.impl.BasicPartitionInvocation * @see com.hazelcast.spi.impl.BasicTargetInvocation */ final class BasicOperationService implements InternalOperationService { private final AtomicLong executedOperationsCount = new AtomicLong(); private final NodeEngineImpl nodeEngine; private final Node node; private final ILogger logger; private final AtomicLong callIdGen = new AtomicLong(1); private final Map executingCalls; final ConcurrentMap invocations; private final long defaultCallTimeout; private final ExecutionService executionService; final BasicOperationScheduler scheduler; BasicOperationService(NodeEngineImpl nodeEngine) { this.nodeEngine = nodeEngine; this.node = nodeEngine.getNode(); this.logger = node.getLogger(OperationService.class); this.defaultCallTimeout = node.getGroupProperties().OPERATION_CALL_TIMEOUT_MILLIS.getLong(); this.executionService = nodeEngine.getExecutionService(); int coreSize = Runtime.getRuntime().availableProcessors(); boolean reallyMultiCore = coreSize >= 8; int concurrencyLevel = reallyMultiCore ? coreSize * 4 : 16; this.executingCalls = new ConcurrentHashMap(1000, 0.75f, concurrencyLevel); this.invocations = new ConcurrentHashMap(1000, 0.75f, concurrencyLevel); this.scheduler = new BasicOperationScheduler(node, executionService, new BasicOperationProcessorImpl()); } @Override public int getPartitionOperationThreadCount() { return scheduler.partitionOperationThreads.length; } @Override public int getGenericOperationThreadCount() { return scheduler.genericOperationThreads.length; } @Override public int getRunningOperationsCount() { return executingCalls.size(); } @Override public long getExecutedOperationCount() { return executedOperationsCount.get(); } @Override public int getRemoteOperationsCount() { return invocations.size(); } @Override public int getResponseQueueSize() { return scheduler.getResponseQueueSize(); } @Override public int getOperationExecutorQueueSize() { return scheduler.getOperationExecutorQueueSize(); } @Override public int getPriorityOperationExecutorQueueSize() { return scheduler.getPriorityOperationExecutorQueueSize(); } @Override public InvocationBuilder createInvocationBuilder(String serviceName, Operation op, int partitionId) { if (partitionId < 0) { throw new IllegalArgumentException("Partition id cannot be negative!"); } return new BasicInvocationBuilder(nodeEngine, serviceName, op, partitionId); } @Override public InvocationBuilder createInvocationBuilder(String serviceName, Operation op, Address target) { if (target == null) { throw new IllegalArgumentException("Target cannot be null!"); } return new BasicInvocationBuilder(nodeEngine, serviceName, op, target); } @PrivateApi @Override public void receive(final Packet packet) { scheduler.execute(packet); } /** * Runs operation in calling thread. * * @param op */ @Override //todo: move to BasicOperationScheduler public void runOperationOnCallingThread(Operation op) { if (scheduler.isAllowedToRunInCurrentThread(op)) { processOperation(op); } else { throw new IllegalThreadStateException("Operation: " + op + " cannot be run in current thread! -> " + Thread.currentThread()); } } /** * Executes operation in operation executor pool. * * @param op */ @Override public void executeOperation(final Operation op) { scheduler.execute(op); } @Override @SuppressWarnings("unchecked") public InternalCompletableFuture invokeOnPartition(String serviceName, Operation op, int partitionId) { return new BasicPartitionInvocation(nodeEngine, serviceName, op, partitionId, InvocationBuilder.DEFAULT_REPLICA_INDEX, InvocationBuilder.DEFAULT_TRY_COUNT, InvocationBuilder.DEFAULT_TRY_PAUSE_MILLIS, InvocationBuilder.DEFAULT_CALL_TIMEOUT, null, null, InvocationBuilder.DEFAULT_DESERIALIZE_RESULT).invoke(); } @Override @SuppressWarnings("unchecked") public InternalCompletableFuture invokeOnTarget(String serviceName, Operation op, Address target) { return new BasicTargetInvocation(nodeEngine, serviceName, op, target, InvocationBuilder.DEFAULT_TRY_COUNT, InvocationBuilder.DEFAULT_TRY_PAUSE_MILLIS, InvocationBuilder.DEFAULT_CALL_TIMEOUT, null, null, InvocationBuilder.DEFAULT_DESERIALIZE_RESULT).invoke(); } // =============================== processing response =============================== private void processResponsePacket(Packet packet) { try { final Data data = packet.getData(); final Response response = (Response) nodeEngine.toObject(data); if (response instanceof NormalResponse) { notifyRemoteCall((NormalResponse) response); } else if (response instanceof BackupResponse) { notifyBackupCall(response.getCallId()); } else { throw new IllegalStateException("Unrecognized response type: " + response); } } catch (Throwable e) { logger.severe("While processing response...", e); } } // TODO: @mm - operations those do not return response can cause memory leaks! Call->Invocation->Operation->Data private void notifyRemoteCall(NormalResponse response) { BasicInvocation invocation = invocations.get(response.getCallId()); if (invocation == null) { throw new HazelcastException("No invocation for response:" + response); } invocation.notify(response); } @Override public void notifyBackupCall(long callId) { try { final BasicInvocation invocation = invocations.get(callId); if (invocation != null) { invocation.signalOneBackupComplete(); } } catch (Exception e) { ReplicaErrorLogger.log(e, logger); } } // =============================== processing operation =============================== private void processOperationPacket(Packet packet) { final Connection conn = packet.getConn(); try { final Address caller = conn.getEndPoint(); final Data data = packet.getData(); final Object object = nodeEngine.toObject(data); final Operation op = (Operation) object; op.setNodeEngine(nodeEngine); OperationAccessor.setCallerAddress(op, caller); OperationAccessor.setConnection(op, conn); ResponseHandlerFactory.setRemoteResponseHandler(nodeEngine, op); if (!isJoinOperation(op) && node.clusterService.getMember(op.getCallerAddress()) == null) { final Exception error = new CallerNotMemberException(op.getCallerAddress(), op.getPartitionId(), op.getClass().getName(), op.getServiceName()); handleOperationError(op, error); } else { String executorName = op.getExecutorName(); if (executorName == null) { processOperation(op); } else { ExecutorService executor = executionService.getExecutor(executorName); if (executor == null) { throw new IllegalStateException("Could not found executor with name: " + executorName); } executor.execute(new LocalOperationProcessor(op)); } } } catch (Throwable e) { logger.severe(e); } } /** * Runs operation in calling thread. */ private void processOperation(final Operation op) { executedOperationsCount.incrementAndGet(); RemoteCallKey callKey = null; try { if (isCallTimedOut(op)) { Object response = new CallTimeoutException(op.getClass().getName(), op.getInvocationTime(), op.getCallTimeout()); op.getResponseHandler().sendResponse(response); return; } callKey = beforeCallExecution(op); final int partitionId = op.getPartitionId(); if (op instanceof PartitionAwareOperation) { if (partitionId < 0) { throw new IllegalArgumentException("Partition id cannot be negative! -> " + partitionId); } final InternalPartition internalPartition = nodeEngine.getPartitionService().getPartition(partitionId); if (retryDuringMigration(op) && internalPartition.isMigrating()) { throw new PartitionMigratingException(node.getThisAddress(), partitionId, op.getClass().getName(), op.getServiceName()); } final Address owner = internalPartition.getReplicaAddress(op.getReplicaIndex()); if (op.validatesTarget() && !node.getThisAddress().equals(owner)) { throw new WrongTargetException(node.getThisAddress(), owner, partitionId, op.getReplicaIndex(), op.getClass().getName(), op.getServiceName()); } } OperationAccessor.setStartTime(op, Clock.currentTimeMillis()); op.beforeRun(); if (op instanceof WaitSupport) { WaitSupport waitSupport = (WaitSupport) op; if (waitSupport.shouldWait()) { nodeEngine.waitNotifyService.await(waitSupport); return; } } op.run(); final boolean returnsResponse = op.returnsResponse(); Object response = null; if (op instanceof BackupAwareOperation) { final BackupAwareOperation backupAwareOp = (BackupAwareOperation) op; int syncBackupCount = 0; if (backupAwareOp.shouldBackup()) { syncBackupCount = sendBackups(backupAwareOp); } if (returnsResponse) { response = new NormalResponse(op.getResponse(), op.getCallId(), syncBackupCount, op.isUrgent()); } } if (returnsResponse) { if (response == null) { response = op.getResponse(); } final ResponseHandler responseHandler = op.getResponseHandler(); if (responseHandler == null) { throw new IllegalStateException("ResponseHandler should not be null!"); } responseHandler.sendResponse(response); } try { op.afterRun(); if (op instanceof Notifier) { final Notifier notifier = (Notifier) op; if (notifier.shouldNotify()) { nodeEngine.waitNotifyService.notify(notifier); } } } catch (Throwable e) { // passed the response phase // `afterRun` and `notifier` errors cannot be sent to the caller anymore // just log the error logOperationError(op, e); } } catch (Throwable e) { handleOperationError(op, e); } finally { afterCallExecution(op, callKey); } } private static boolean retryDuringMigration(Operation op) { return !(op instanceof ReadonlyOperation || OperationAccessor.isMigrationOperation(op)); } @PrivateApi public boolean isCallTimedOut(Operation op) { if (op.returnsResponse() && op.getCallId() != 0) { final long callTimeout = op.getCallTimeout(); final long invocationTime = op.getInvocationTime(); final long expireTime = invocationTime + callTimeout; if (expireTime > 0 && expireTime < Long.MAX_VALUE) { final long now = nodeEngine.getClusterTime(); if (expireTime < now) { return true; } } } return false; } private RemoteCallKey beforeCallExecution(Operation op) { RemoteCallKey callKey = null; if (op.getCallId() != 0 && op.returnsResponse()) { callKey = new RemoteCallKey(op); RemoteCallKey current; if ((current = executingCalls.put(callKey, callKey)) != null) { logger.warning("Duplicate Call record! -> " + callKey + " / " + current + " == " + op.getClass().getName()); } } return callKey; } private void afterCallExecution(Operation op, RemoteCallKey callKey) { if (callKey != null && op.getCallId() != 0 && op.returnsResponse()) { if (executingCalls.remove(callKey) == null) { logger.severe("No Call record has been found: -> " + callKey + " == " + op.getClass().getName()); } } } private int sendBackups(BackupAwareOperation backupAwareOp) throws Exception { Operation op = (Operation) backupAwareOp; boolean returnsResponse = op.returnsResponse(); InternalPartitionService partitionService = nodeEngine.getPartitionService(); int maxBackupCount = InternalPartition.MAX_BACKUP_COUNT; int maxPossibleBackupCount = Math.min(partitionService.getMemberGroupsSize() - 1, maxBackupCount); int requestedSyncBackupCount = backupAwareOp.getSyncBackupCount() > 0 ? Math.min(maxBackupCount, backupAwareOp.getSyncBackupCount()) : 0; int requestedAsyncBackupCount = backupAwareOp.getAsyncBackupCount() > 0 ? Math.min(maxBackupCount - requestedSyncBackupCount, backupAwareOp.getAsyncBackupCount()) : 0; int totalRequestedBackupCount = requestedSyncBackupCount + requestedAsyncBackupCount; if (totalRequestedBackupCount == 0) { return 0; } int partitionId = op.getPartitionId(); long[] replicaVersions = partitionService.incrementPartitionReplicaVersions(partitionId, totalRequestedBackupCount); int syncBackupCount = Math.min(maxPossibleBackupCount, requestedSyncBackupCount); int asyncBackupCount = Math.min(maxPossibleBackupCount - syncBackupCount, requestedAsyncBackupCount); if (!returnsResponse) { asyncBackupCount += syncBackupCount; syncBackupCount = 0; } int totalBackupCount = syncBackupCount + asyncBackupCount; if (totalBackupCount == 0) { return 0; } int sentSyncBackupCount = 0; String serviceName = op.getServiceName(); InternalPartition partition = partitionService.getPartition(partitionId); for (int replicaIndex = 1; replicaIndex <= totalBackupCount; replicaIndex++) { Address target = partition.getReplicaAddress(replicaIndex); if (target != null) { if (target.equals(node.getThisAddress())) { throw new IllegalStateException("Normally shouldn't happen! Owner node and backup node " + "are the same! " + partition); } else { Operation backupOp = backupAwareOp.getBackupOperation(); if (backupOp == null) { throw new IllegalArgumentException("Backup operation should not be null!"); } backupOp.setPartitionId(partitionId).setReplicaIndex(replicaIndex).setServiceName(serviceName); Data backupOpData = nodeEngine.getSerializationService().toData(backupOp); boolean isSyncBackup = replicaIndex <= syncBackupCount; Backup backup = new Backup(backupOpData, op.getCallerAddress(), replicaVersions, isSyncBackup); backup.setPartitionId(partitionId).setReplicaIndex(replicaIndex).setServiceName(serviceName) .setCallerUuid(nodeEngine.getLocalMember().getUuid()); OperationAccessor.setCallId(backup, op.getCallId()); send(backup, target); if (isSyncBackup) { sentSyncBackupCount++; } } } } return sentSyncBackupCount; } private void handleOperationError(Operation op, Throwable e) { if (e instanceof OutOfMemoryError) { OutOfMemoryErrorDispatcher.onOutOfMemory((OutOfMemoryError) e); } op.logError(e); ResponseHandler responseHandler = op.getResponseHandler(); if (op.returnsResponse() && responseHandler != null) { try { if (node.isActive()) { responseHandler.sendResponse(e); } else if (responseHandler.isLocal()) { responseHandler.sendResponse(new HazelcastInstanceNotActiveException()); } } catch (Throwable t) { logger.warning("While sending op error... op: " + op + ", error: " + e, t); } } } private void logOperationError(Operation op, Throwable e) { if (e instanceof OutOfMemoryError) { OutOfMemoryErrorDispatcher.onOutOfMemory((OutOfMemoryError) e); } op.logError(e); } @Override public Map invokeOnAllPartitions(String serviceName, OperationFactory operationFactory) throws Exception { Map> memberPartitions = nodeEngine.getPartitionService().getMemberPartitionsMap(); return invokeOnPartitions(serviceName, operationFactory, memberPartitions); } @Override public Map invokeOnPartitions(String serviceName, OperationFactory operationFactory, Collection partitions) throws Exception { final Map> memberPartitions = new HashMap>(3); for (int partition : partitions) { Address owner = nodeEngine.getPartitionService().getPartitionOwner(partition); if (!memberPartitions.containsKey(owner)) { memberPartitions.put(owner, new ArrayList()); } memberPartitions.get(owner).add(partition); } return invokeOnPartitions(serviceName, operationFactory, memberPartitions); } private Map invokeOnPartitions(String serviceName, OperationFactory operationFactory, Map> memberPartitions) throws Exception { final Thread currentThread = Thread.currentThread(); if (currentThread instanceof BasicOperationScheduler.OperationThread) { throw new IllegalThreadStateException(currentThread + " cannot make invocation on multiple partitions!"); } final Map responses = new HashMap(memberPartitions.size()); for (Map.Entry> mp : memberPartitions.entrySet()) { final Address address = mp.getKey(); final List partitions = mp.getValue(); final PartitionIteratingOperation pi = new PartitionIteratingOperation(partitions, operationFactory); Future future = createInvocationBuilder(serviceName, pi, address).setTryCount(10).setTryPauseMillis(300).invoke(); responses.put(address, future); } Map partitionResults = new HashMap( nodeEngine.getPartitionService().getPartitionCount()); int x = 0; for (Map.Entry response : responses.entrySet()) { try { Future future = response.getValue(); PartitionResponse result = (PartitionResponse) nodeEngine.toObject(future.get()); partitionResults.putAll(result.asMap()); } catch (Throwable t) { if (logger.isFinestEnabled()) { logger.finest(t); } else { logger.warning(t.getMessage()); } List partitions = memberPartitions.get(response.getKey()); for (Integer partition : partitions) { partitionResults.put(partition, t); } } x++; } final List failedPartitions = new LinkedList(); for (Map.Entry partitionResult : partitionResults.entrySet()) { int partitionId = partitionResult.getKey(); Object result = partitionResult.getValue(); if (result instanceof Throwable) { failedPartitions.add(partitionId); } } for (Integer failedPartition : failedPartitions) { Future f = createInvocationBuilder(serviceName, operationFactory.createOperation(), failedPartition).invoke(); partitionResults.put(failedPartition, f); } for (Integer failedPartition : failedPartitions) { Future f = (Future) partitionResults.get(failedPartition); Object result = f.get(); partitionResults.put(failedPartition, result); } return partitionResults; } @Override public boolean send(final Operation op, final Address target) { if (target == null) { throw new IllegalArgumentException("Target is required!"); } if (nodeEngine.getThisAddress().equals(target)) { throw new IllegalArgumentException("Target is this node! -> " + target + ", op: " + op); } return send(op, node.getConnectionManager().getOrConnect(target)); } @Override public boolean send(Response response, Address target) { if (target == null) { throw new IllegalArgumentException("Target is required!"); } if (nodeEngine.getThisAddress().equals(target)) { throw new IllegalArgumentException("Target is this node! -> " + target + ", response: " + response); } Data data = nodeEngine.toData(response); Packet packet = new Packet(data, nodeEngine.getPortableContext()); packet.setHeader(Packet.HEADER_OP); packet.setHeader(Packet.HEADER_RESPONSE); if (response.isUrgent()) { packet.setHeader(Packet.HEADER_URGENT); } return nodeEngine.send(packet, node.getConnectionManager().getOrConnect(target)); } private boolean send(final Operation op, final Connection connection) { Data data = nodeEngine.toData(op); //enable this line to get some logging of sizes of operations. //System.out.println(op.getClass()+" "+data.bufferSize()); final int partitionId = scheduler.getPartitionIdForExecution(op); Packet packet = new Packet(data, partitionId, nodeEngine.getPortableContext()); packet.setHeader(Packet.HEADER_OP); if (op instanceof UrgentSystemOperation) { packet.setHeader(Packet.HEADER_URGENT); } return nodeEngine.send(packet, connection); } public long registerInvocation(BasicInvocation invocation) { long callId = callIdGen.getAndIncrement(); Operation op = invocation.op; if (op.getCallId() != 0) { invocations.remove(op.getCallId()); } invocations.put(callId, invocation); setCallId(invocation.op, callId); return callId; } public void deregisterInvocation(long id) { invocations.remove(id); } @PrivateApi long getDefaultCallTimeout() { return defaultCallTimeout; } @PrivateApi boolean isOperationExecuting(Address callerAddress, String callerUuid, long operationCallId) { return executingCalls.containsKey(new RemoteCallKey(callerAddress, callerUuid, operationCallId)); } @PrivateApi boolean isOperationExecuting(Address callerAddress, String callerUuid, String serviceName, Object identifier) { Object service = nodeEngine.getService(serviceName); if (service == null) { logger.severe("Not able to find operation execution info. Invalid service: " + serviceName); return false; } if (service instanceof ExecutionTracingService) { return ((ExecutionTracingService) service).isOperationExecuting(callerAddress, callerUuid, identifier); } logger.severe("Not able to find operation execution info. Invalid service: " + service); return false; } @Override public void onMemberLeft(final MemberImpl member) { // postpone notifying calls since real response may arrive in the mean time. nodeEngine.getExecutionService().schedule(new Runnable() { public void run() { final Iterator iter = invocations.values().iterator(); while (iter.hasNext()) { final BasicInvocation invocation = iter.next(); if (invocation.isCallTarget(member)) { iter.remove(); invocation.notify(new MemberLeftException(member)); } } } }, 1111, TimeUnit.MILLISECONDS); } @Override public void shutdown() { logger.finest("Stopping operation threads..."); final Object response = new HazelcastInstanceNotActiveException(); for (BasicInvocation invocation : invocations.values()) { invocation.notify(response); } invocations.clear(); scheduler.shutdown(); } public class BasicOperationProcessorImpl implements BasicOperationProcessor { @Override public void process(Object o) { if (o == null) { throw new IllegalArgumentException(); } else if (o instanceof Operation) { processOperation((Operation) o); } else if (o instanceof Packet) { Packet packet = (Packet) o; if (packet.isHeaderSet(Packet.HEADER_RESPONSE)) { processResponsePacket(packet); } else { processOperationPacket(packet); } } else if (o instanceof Runnable) { ((Runnable) o).run(); } else { throw new IllegalArgumentException("Unrecognized task:" + o); } } } /** * Process the operation that has been send locally to this OperationService. */ private class LocalOperationProcessor implements Runnable { private final Operation op; private LocalOperationProcessor(Operation op) { this.op = op; } @Override public void run() { processOperation(op); } } private static class RemoteCallKey { private final long time = Clock.currentTimeMillis(); private final Address callerAddress; // human readable caller private final String callerUuid; private final long callId; private RemoteCallKey(Address callerAddress, String callerUuid, long callId) { if (callerUuid == null) { throw new IllegalArgumentException("Caller UUID is required!"); } this.callerAddress = callerAddress; if (callerAddress == null) { throw new IllegalArgumentException("Caller address is required!"); } this.callerUuid = callerUuid; this.callId = callId; } private RemoteCallKey(final Operation op) { callerUuid = op.getCallerUuid(); if (callerUuid == null) { throw new IllegalArgumentException("Caller UUID is required! -> " + op); } callerAddress = op.getCallerAddress(); if (callerAddress == null) { throw new IllegalArgumentException("Caller address is required! -> " + op); } callId = op.getCallId(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RemoteCallKey callKey = (RemoteCallKey) o; if (callId != callKey.callId) return false; if (!callerUuid.equals(callKey.callerUuid)) return false; return true; } @Override public int hashCode() { int result = callerUuid.hashCode(); result = 31 * result + (int) (callId ^ (callId >>> 32)); return result; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("RemoteCallKey"); sb.append("{callerAddress=").append(callerAddress); sb.append(", callerUuid=").append(callerUuid); sb.append(", callId=").append(callId); sb.append(", time=").append(time); sb.append('}'); return sb.toString(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy