Package com.hazelcast.spi.impl

Source Code of com.hazelcast.spi.impl.BasicInvocation

/*
* 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.ExecutionCallback;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.OperationTimeoutException;
import com.hazelcast.instance.MemberImpl;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.partition.InternalPartition;
import com.hazelcast.spi.AbstractOperation;
import com.hazelcast.spi.BackupAwareOperation;
import com.hazelcast.spi.BackupCompletionCallback;
import com.hazelcast.spi.Callback;
import com.hazelcast.spi.ExceptionAction;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.InternalCompletableFuture;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.OperationAccessor;
import com.hazelcast.spi.TraceableOperation;
import com.hazelcast.spi.WaitSupport;
import com.hazelcast.spi.exception.CallTimeoutException;
import com.hazelcast.spi.exception.RetryableException;
import com.hazelcast.spi.exception.RetryableIOException;
import com.hazelcast.spi.exception.TargetNotMemberException;
import com.hazelcast.spi.exception.WrongTargetException;
import com.hazelcast.util.Clock;
import com.hazelcast.util.ExceptionUtil;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static com.hazelcast.util.ExceptionUtil.fixRemoteStackTrace;
import static com.hazelcast.util.ValidationUtil.isNotNull;

/**
* The BasicInvocation evaluates a OperationInvocation for the {@link com.hazelcast.spi.impl.BasicOperationService}.
*/
abstract class BasicInvocation implements Callback<Object>, BackupCompletionCallback {

    private static final Object NULL_RESPONSE = new InternalResponse("Invocation::NULL_RESPONSE");

    private static final Object RETRY_RESPONSE = new InternalResponse("Invocation::RETRY_RESPONSE");

    private static final Object WAIT_RESPONSE = new InternalResponse("Invocation::WAIT_RESPONSE");

    private static final Object TIMEOUT_RESPONSE = new InternalResponse("Invocation::TIMEOUT_RESPONSE");

    private static final Object INTERRUPTED_RESPONSE = new InternalResponse("Invocation::INTERRUPTED_RESPONSE");

    private static long decrementTimeout(long timeout, long diff) {
        if (timeout != Long.MAX_VALUE) {
            timeout -= diff;
        }
        return timeout;
    }

    private static final long MIN_TIMEOUT = 10000;

    protected final long callTimeout;
    protected final NodeEngineImpl nodeEngine;
    protected final String serviceName;
    protected final Operation op;
    protected final int partitionId;
    protected final int replicaIndex;
    protected final int tryCount;
    protected final long tryPauseMillis;
    protected final ILogger logger;
    //todo: in the future we could get rid of this object, just let the BasicInvocation implement the Future interface.
    private final InvocationFuture invocationFuture;

    private volatile int invokeCount = 0;
    private volatile Address target;
    private boolean remote = false;
    private final String executorName;
    private final boolean resultDeserialized;

    BasicInvocation(NodeEngineImpl nodeEngine, String serviceName, Operation op, int partitionId,
                    int replicaIndex, int tryCount, long tryPauseMillis, long callTimeout, Callback<Object> callback,
                    String executorName, boolean resultDeserialized) {
        this.logger = nodeEngine.getLogger(BasicInvocation.class);
        this.nodeEngine = nodeEngine;
        this.serviceName = serviceName;
        this.op = op;
        this.partitionId = partitionId;
        this.replicaIndex = replicaIndex;
        this.tryCount = tryCount;
        this.tryPauseMillis = tryPauseMillis;
        this.callTimeout = getCallTimeout(callTimeout);
        this.invocationFuture = new InvocationFuture(callback);
        this.executorName = executorName;
        this.resultDeserialized = resultDeserialized;
    }

    abstract ExceptionAction onException(Throwable t);

    public String getServiceName() {
        return serviceName;
    }

    InternalPartition getPartition() {
        return nodeEngine.getPartitionService().getPartition(partitionId);
    }

    public int getReplicaIndex() {
        return replicaIndex;
    }

    public int getPartitionId() {
        return partitionId;
    }

    private ExecutorService getAsyncExecutor() {
        return nodeEngine.getExecutionService().getExecutor(ExecutionService.ASYNC_EXECUTOR);
    }

    private long getCallTimeout(long callTimeout) {
        if (callTimeout > 0) {
            return callTimeout;
        }

        BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
        final long defaultCallTimeout = operationService.getDefaultCallTimeout();
        if (op instanceof WaitSupport) {
            final long waitTimeoutMillis = op.getWaitTimeout();
            if (waitTimeoutMillis > 0 && waitTimeoutMillis < Long.MAX_VALUE) {
                /*
                 * final long minTimeout = Math.min(defaultCallTimeout, MIN_TIMEOUT);
                 * long callTimeout = Math.min(waitTimeoutMillis, defaultCallTimeout);
                 * callTimeout = Math.max(a, minTimeout);
                 * return callTimeout;
                 *
                 * Below two lines are shortened version of above*
                 * using min(max(x,y),z)=max(min(x,z),min(y,z))
                 */
                final long max = Math.max(waitTimeoutMillis, MIN_TIMEOUT);
                return Math.min(max, defaultCallTimeout);
            }
        }
        return defaultCallTimeout;
    }

    public final InvocationFuture invoke() {
        if (invokeCount > 0) {   // no need to be pessimistic.
            throw new IllegalStateException("An invocation can not be invoked more than once!");
        }

        if (op.getCallId() != 0) {
            throw new IllegalStateException("An operation[" + op + "] can not be used for multiple invocations!");
        }

        try {
            OperationAccessor.setCallTimeout(op, callTimeout);
            OperationAccessor.setCallerAddress(op, nodeEngine.getThisAddress());
            op.setNodeEngine(nodeEngine)
                    .setServiceName(serviceName)
                    .setPartitionId(partitionId)
                    .setReplicaIndex(replicaIndex)
                    .setExecutorName(executorName);
            if (op.getCallerUuid() == null) {
                op.setCallerUuid(nodeEngine.getLocalMember().getUuid());
            }
            BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
            if (!operationService.isInvocationAllowedFromCurrentThread(op) && !OperationAccessor.isMigrationOperation(op)) {
                throw new IllegalThreadStateException(Thread.currentThread() + " cannot make remote call: " + op);
            }
            doInvoke();
        } catch (Exception e) {
            if (e instanceof RetryableException) {
                notify(e);
            } else {
                throw ExceptionUtil.rethrow(e);
            }
        }
        return invocationFuture;
    }


    private void resetAndReInvoke() {
        reinvocations++;
        invokeCount = 0;
        potentialResponse = null;
        expectedBackupCount = -1;
        doInvoke();
    }

    private static Throwable getError(Object obj) {
        if (obj == null) {
            return null;
        }

        if (obj instanceof Throwable) {
            return (Throwable) obj;
        }

        if (!(obj instanceof NormalResponse)) {
            return null;
        }

        NormalResponse response = (NormalResponse) obj;
        if (!(response.getValue() instanceof Throwable)) {
            return null;
        }

        return (Throwable) response.getValue();
    }

    @Override
    public void notify(Object obj) {
        Object response;
        if (obj == null) {
            response = NULL_RESPONSE;
        } else {
            Throwable error = getError(obj);
            if (error != null) {
                if (error instanceof CallTimeoutException) {
                    response = RETRY_RESPONSE;
                    if (logger.isFinestEnabled()) {
                        logger.finest("Call timed-out during wait-notify phase, retrying call: " + toString());
                    }
                    if (op instanceof WaitSupport) {
                        // decrement wait-timeout by call-timeout
                        long waitTimeout = op.getWaitTimeout();
                        waitTimeout -= callTimeout;
                        op.setWaitTimeout(waitTimeout);
                    }
                    invokeCount--;
                } else {
                    final ExceptionAction action = onException(error);
                    final int localInvokeCount = invokeCount;
                    if (action == ExceptionAction.RETRY_INVOCATION && localInvokeCount < tryCount) {
                        response = RETRY_RESPONSE;
                        if (localInvokeCount > 99 && localInvokeCount % 10 == 0) {
                            logger.warning("Retrying invocation: " + toString() + ", Reason: " + error);
                        }
                    } else if (action == ExceptionAction.CONTINUE_WAIT) {
                        response = WAIT_RESPONSE;
                    } else {
                        response = error;
                    }
                }
            } else {
                response = obj;
            }
        }

        if (response == RETRY_RESPONSE) {
            if (invocationFuture.interrupted) {
                invocationFuture.set(INTERRUPTED_RESPONSE);
            } else {
                invocationFuture.set(WAIT_RESPONSE);
                final ExecutionService ex = nodeEngine.getExecutionService();
                // fast retry for the first few reinvocations
                if (invokeCount < 5) {
                    getAsyncExecutor().execute(new ReInvocationTask());
                } else {
                    ex.schedule(ExecutionService.ASYNC_EXECUTOR, new ReInvocationTask(),
                            tryPauseMillis, TimeUnit.MILLISECONDS);
                }
            }
            return;
        }

        if (response == WAIT_RESPONSE) {
            invocationFuture.set(WAIT_RESPONSE);
            return;
        }

        //if a regular response came and there are backups, we need to wait for the backs.
        //when the backups complete, the response will be send by the last backup.
        if (response instanceof NormalResponse && op instanceof BackupAwareOperation) {
            final NormalResponse resp = (NormalResponse) response;
            if (resp.getBackupCount() > 0) {
                waitForBackups(resp.getBackupCount(), 5, TimeUnit.SECONDS, resp);
                return;
            }
        }

        //we don't need to wait for a backup, so we can set the response immediately.
        invocationFuture.set(response);
    }

    private void doInvoke() {
        if (!nodeEngine.isActive()) {
            remote = false;
            notify(new HazelcastInstanceNotActiveException());
            return;
        }

        final Address invTarget = getTarget();
        target = invTarget;
        invokeCount++;
        final Address thisAddress = nodeEngine.getThisAddress();
        if (invTarget == null) {
            remote = false;
            if (nodeEngine.isActive()) {
                notify(new WrongTargetException(thisAddress, null, partitionId, replicaIndex, op.getClass().getName(), serviceName));
            } else {
                notify(new HazelcastInstanceNotActiveException());
            }
            return;
        }

        final MemberImpl member = nodeEngine.getClusterService().getMember(invTarget);
        if (!OperationAccessor.isJoinOperation(op) && member == null) {
            notify(new TargetNotMemberException(invTarget, partitionId, op.getClass().getName(), serviceName));
            return;
        }

        if (op.getPartitionId() != partitionId) {
            notify(new IllegalStateException("Partition id of operation: " + op.getPartitionId() +
                    " is not equal to the partition id of invocation: " + partitionId));
            return;
        }

        if (op.getReplicaIndex() != replicaIndex) {
            notify(new IllegalStateException("Replica index of operation: " + op.getReplicaIndex() +
                    " is not equal to the replica index of invocation: " + replicaIndex));
            return;
        }

        final BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
        OperationAccessor.setInvocationTime(op, nodeEngine.getClusterTime());

        remote = !thisAddress.equals(invTarget);
        if (remote) {
            final RemoteCall call = member != null ? new RemoteCall(member, this) : new RemoteCall(invTarget, this);
            final long callId = operationService.registerRemoteCall(call);
            if (op instanceof BackupAwareOperation) {
                registerBackups((BackupAwareOperation) op, callId);
            }
            OperationAccessor.setCallId(op, callId);
            boolean sent = operationService.send(op, invTarget);
            if (!sent) {
                operationService.deregisterRemoteCall(callId);
                operationService.deregisterBackupCall(callId);
                notify(new RetryableIOException("Packet not sent to -> " + invTarget));
            }
        } else {
            if (op instanceof BackupAwareOperation) {
                final long callId = operationService.newCallId();
                registerBackups((BackupAwareOperation) op, callId);
                OperationAccessor.setCallId(op, callId);
            }
            ResponseHandlerFactory.setLocalResponseHandler(op, this);
            //todo: should move to the operationService.
            if (operationService.isAllowedToRunInCurrentThread(op)) {
                operationService.runOperation(op);
            } else {
                operationService.executeOperation(op);
            }
        }
    }

    protected abstract Address getTarget();


    private void registerBackups(BackupAwareOperation op, long callId) {
        final long oldCallId = ((Operation) op).getCallId();
        final BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
        if (oldCallId != 0) {
            operationService.deregisterBackupCall(oldCallId);
        }
        operationService.registerBackupCall(callId, this);
    }


    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("BasicInvocation");
        sb.append("{ serviceName='").append(serviceName).append('\'');
        sb.append(", op=").append(op);
        sb.append(", partitionId=").append(partitionId);
        sb.append(", replicaIndex=").append(replicaIndex);
        sb.append(", tryCount=").append(tryCount);
        sb.append(", tryPauseMillis=").append(tryPauseMillis);
        sb.append(", invokeCount=").append(invokeCount);
        sb.append(", callTimeout=").append(callTimeout);
        sb.append(", target=").append(target);
        sb.append('}');
        return sb.toString();
    }

    private volatile int reinvocations = 0;
    private volatile int availableBackups;
    private volatile NormalResponse potentialResponse;
    private volatile int expectedBackupCount;

    @Override
    public void signalOneBackupComplete() {
        synchronized (this) {
            availableBackups++;

            if (expectedBackupCount == -1) {
                return;
            }

            if (expectedBackupCount != availableBackups) {
                return;
            }

            if (potentialResponse != null) {
                invocationFuture.set(potentialResponse);
            }
        }
    }

    private void waitForBackups(int backupCount, long timeout, TimeUnit unit, NormalResponse response) {
        synchronized (this) {
            this.expectedBackupCount = backupCount;

            if (availableBackups == expectedBackupCount) {
                invocationFuture.set(response);
                return;
            }

            this.potentialResponse = response;
        }

        nodeEngine.getExecutionService().schedule(ExecutionService.ASYNC_EXECUTOR, new Runnable() {
            @Override
            public void run() {
                synchronized (BasicInvocation.this) {
                    if (expectedBackupCount == availableBackups) {
                        return;
                    }
                }

                if (nodeEngine.getClusterService().getMember(target) != null) {
                    synchronized (BasicInvocation.this) {
                        if (BasicInvocation.this.potentialResponse != null) {
                            invocationFuture.set(BasicInvocation.this.potentialResponse);
                            BasicInvocation.this.potentialResponse = null;
                        }
                    }
                    return;
                }

                resetAndReInvoke();
            }
        }, timeout, unit);
    }

    public static class IsStillExecuting extends AbstractOperation {

        private long operationCallId;

        IsStillExecuting() {
        }

        private IsStillExecuting(long operationCallId) {
            this.operationCallId = operationCallId;
        }

        @Override
        public void run() throws Exception {
            NodeEngineImpl nodeEngine = (NodeEngineImpl) getNodeEngine();
            BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
            boolean executing = operationService.isOperationExecuting(getCallerAddress(), getCallerUuid(), operationCallId);
            getResponseHandler().sendResponse(executing);
        }

        @Override
        public boolean returnsResponse() {
            return false;
        }

        @Override
        protected void readInternal(ObjectDataInput in) throws IOException {
            super.readInternal(in);
            operationCallId = in.readLong();
        }

        @Override
        protected void writeInternal(ObjectDataOutput out) throws IOException {
            super.writeInternal(out);
            out.writeLong(operationCallId);
        }
    }

    public static class TraceableIsStillExecuting extends AbstractOperation {

        private String serviceName;
        private Object identifier;

        TraceableIsStillExecuting() {
        }

        public TraceableIsStillExecuting(String serviceName, Object identifier) {
            this.serviceName = serviceName;
            this.identifier = identifier;
        }

        @Override
        public void run() throws Exception {
            NodeEngineImpl nodeEngine = (NodeEngineImpl) getNodeEngine();
            BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
            boolean executing = operationService.isOperationExecuting(getCallerAddress(), getCallerUuid(),
                    serviceName, identifier);
            getResponseHandler().sendResponse(executing);
        }

        @Override
        public boolean returnsResponse() {
            return false;
        }

        @Override
        protected void readInternal(ObjectDataInput in) throws IOException {
            super.readInternal(in);
            serviceName = in.readUTF();
            identifier = in.readObject();
        }

        @Override
        protected void writeInternal(ObjectDataOutput out) throws IOException {
            super.writeInternal(out);
            out.writeUTF(serviceName);
            out.writeObject(identifier);
        }
    }

    private class ReInvocationTask implements Runnable {
        public void run() {
            doInvoke();
        }
    }

    private static class ExecutionCallbackNode<E> {
        private final ExecutionCallback<E> callback;
        private final Executor executor;
        private final ExecutionCallbackNode<E> next;

        private ExecutionCallbackNode(ExecutionCallback<E> callback, Executor executor, ExecutionCallbackNode<E> next) {
            this.callback = callback;
            this.executor = executor;
            this.next = next;
        }
    }

    private static class ExecutorCallbackAdapter<E> implements ExecutionCallback<E> {
        private final Callback callback;

        private ExecutorCallbackAdapter(Callback callback) {
            this.callback = callback;
        }

        @Override
        public void onResponse(E response) {
            callback.notify(response);
        }

        @Override
        public void onFailure(Throwable t) {
            callback.notify(t);
        }
    }

    private final class InvocationFuture<E> implements InternalCompletableFuture<E> {

        volatile ExecutionCallbackNode<E> callbackHead;
        volatile Object response;
        volatile boolean interrupted = false;

        private InvocationFuture(final Callback<E> callback) {
            if (callback != null) {
                ExecutorCallbackAdapter<E> adapter = new ExecutorCallbackAdapter<E>(callback);
                callbackHead = new ExecutionCallbackNode<E>(adapter, getAsyncExecutor(), null);
            }
        }

        @Override
        public void andThen(ExecutionCallback<E> callback, Executor executor) {
            isNotNull(callback, "callback");
            isNotNull(executor, "executor");

            synchronized (this) {
                if (response != null) {
                    runAsynchronous(callback, executor);
                    return;
                }

                this.callbackHead = new ExecutionCallbackNode<E>(callback, executor, callbackHead);
            }
        }

        @Override
        public void andThen(ExecutionCallback<E> callback) {
            andThen(callback, getAsyncExecutor());
        }

        private void runAsynchronous(final ExecutionCallback<E> callback, Executor executor) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Object resp = resolveResponse(response);

                        if (resp == null || !(resp instanceof Throwable)) {
                            callback.onResponse((E) resp);
                        } else {
                            callback.onFailure((Throwable) resp);
                        }
                    } catch (Throwable t) {
                        //todo: improved error message
                        logger.severe("Failed to async for " + BasicInvocation.this, t);
                    }
                }
            });
        }

        public void set(Object response) {
            if (response == null) {
                throw new IllegalArgumentException("response can't be null");
            }

            if (response instanceof NormalResponse) {
                response = ((NormalResponse) response).getValue();
            }

            if (response == null) {
                response = NULL_RESPONSE;
            }

            ExecutionCallbackNode<E> callbackChain;
            synchronized (this) {
                if (this.response != null && !(this.response instanceof InternalResponse)) {
                    throw new IllegalArgumentException("The InvocationFuture.set method can only be called once");
                }
                this.response = response;
                if (response == WAIT_RESPONSE) {
                    return;
                }
                callbackChain = callbackHead;
                callbackHead = null;
                this.notifyAll();
            }

            //we need to deregister the backup call to make sure that there is no memory leak.
            BasicOperationService operationService = (BasicOperationService) nodeEngine.operationService;
            operationService.deregisterBackupCall(op.getCallId());

            while (callbackChain != null) {
                runAsynchronous(callbackChain.callback, callbackChain.executor);
                callbackChain = callbackChain.next;
            }
        }

        @Override
        public E get() throws InterruptedException, ExecutionException {
            try {
                return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                logger.severe("Unexpected timeout while processing " + this, e);
                return null;
            }
        }

        @Override
        public E getSafely() {
            try {
                //todo:
                //this method is quite inefficient when there is unchecked exception, because it will be wrapped
                //in a ExecutionException, and then it is unwrapped again.
                return get();
            } catch (Throwable throwable) {
                throw ExceptionUtil.rethrow(throwable);
            }
        }

        @Override
        public E get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
            Object unresolvedResponse = waitForResponse(timeout, unit);
            return (E) resolveResponseOrThrowException(unresolvedResponse);
        }

        private Object waitForResponse(long time, TimeUnit unit) {
            if (response != null && response != WAIT_RESPONSE) {
                return response;
            }
            long timeoutMs = unit.toMillis(time);
            if (timeoutMs < 0) timeoutMs = 0;

            final long maxCallTimeout = callTimeout * 2 > 0 ? callTimeout * 2 : Long.MAX_VALUE;
            final boolean longPolling = timeoutMs > maxCallTimeout;
            int pollCount = 0;

            while (timeoutMs >= 0) {
                final long pollTimeoutMs = Math.min(maxCallTimeout, timeoutMs);
                final long startMs = Clock.currentTimeMillis();

                long lastPollTime = 0;
                pollCount++;
                try {
                    //we should only wait if there is any timeout. We can't call wait with 0, because it is interpreted as infinite.
                    if (pollTimeoutMs > 0) {
                        synchronized (this) {
                            if (response == null || response == WAIT_RESPONSE) {
                                this.wait(pollTimeoutMs);
                            }
                        }
                    }
                    lastPollTime = Clock.currentTimeMillis() - startMs;
                    timeoutMs = decrementTimeout(timeoutMs, lastPollTime);

                    if (response != null) {
                        if (response == WAIT_RESPONSE) {
                            continue;
                        }
                        //if the thread is interrupted, but the response was not an interrupted-response,
                        //we need to restore the interrupt flag.
                        if (response != INTERRUPTED_RESPONSE && interrupted) {
                            Thread.currentThread().interrupt();
                        }
                        return response;
                    }
                } catch (InterruptedException e) {
                    interrupted = true;
                }

                if (!interrupted && /* response == null && */ longPolling) {
                    // no response!
                    final Address target = getTarget();
                    if (nodeEngine.getThisAddress().equals(target)) {
                        // target may change during invocation because of migration!
                        continue;
                    }
                    // TODO: @mm - improve logging (see SystemLogService)
                    logger.warning("No response for " + lastPollTime + " ms. " + toString());

                    boolean executing = isOperationExecuting(target);
                    if (!executing) {
                        if (response != null) {
                            continue;
                        }
                        return newOperationTimeoutException(pollCount,pollTimeoutMs);
                    }
                }
            }
            return TIMEOUT_RESPONSE;
        }

        private Object newOperationTimeoutException(int pollCount, long pollTimeoutMs) {
            boolean hasResponse = potentialResponse != null;
            int backupsExpected = expectedBackupCount;
            int backupsCompleted = availableBackups;

            if (hasResponse) {
                return new OperationTimeoutException("No response for " + (pollTimeoutMs * pollCount) + " ms."
                        + " Aborting invocation! " + toString()
                        + " Not all backups have completed "
                        + " backups-expected: " + backupsExpected
                        + " backups-completed: " + backupsCompleted
                        + " reinvocations: " + reinvocations

                );
            } else {
                return new OperationTimeoutException("No response for " + (pollTimeoutMs * pollCount) + " ms."
                        + " Aborting invocation! " + toString()
                        + " No response has been send "
                        + " backups-expected: " + backupsExpected
                        + " backups-completed: " + backupsCompleted
                        + " reinvocations: " + reinvocations
                );
            }
        }

        private Object resolveResponseOrThrowException(Object unresolvedResponse)
                throws ExecutionException, InterruptedException, TimeoutException {

            Object response = resolveResponse(unresolvedResponse);

            if (response == null || !(response instanceof Throwable)) {
                return response;
            }

            if (response instanceof ExecutionException) {
                throw (ExecutionException) response;
            }

            if (response instanceof TimeoutException) {
                throw (TimeoutException) response;
            }

            if (response instanceof InterruptedException) {
                throw (InterruptedException) response;
            }

            if (response instanceof Error) {
                throw (Error) response;
            }

            // To obey Future contract, we should wrap unchecked exceptions with ExecutionExceptions.
            throw new ExecutionException((Throwable) response);
        }

        private Object resolveResponse(Object unresolvedResponse) {
            if (unresolvedResponse == NULL_RESPONSE) {
                return null;
            }

            if (unresolvedResponse == TIMEOUT_RESPONSE) {
                return new TimeoutException("Call " + BasicInvocation.this + " encountered a timeout");
            }

            if (unresolvedResponse == INTERRUPTED_RESPONSE) {
                return new InterruptedException("Call " + BasicInvocation.this + " was interrupted");
            }

            Object response = unresolvedResponse;
            if (resultDeserialized && response instanceof Data) {
                response = nodeEngine.toObject(response);
                if (response == null) {
                    return null;
                }
            }

            if (response instanceof NormalResponse) {
                NormalResponse responseObj = (NormalResponse) response;
                response = responseObj.getValue();

                if (response == null) {
                    return null;
                }

                //it could be that the value of the response is Data.
                if (resultDeserialized && response instanceof Data) {
                    response = nodeEngine.toObject(response);
                    if (response == null) {
                        return null;
                    }
                }
            }

            if (response instanceof Throwable) {
                Throwable throwable = ((Throwable) response);
                if (remote) {
                    fixRemoteStackTrace((Throwable) response, Thread.currentThread().getStackTrace());
                }
                return throwable;
            }

            return response;
        }

        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            return false;
        }

        @Override
        public boolean isCancelled() {
            return false;
        }

        @Override
        public boolean isDone() {
            return response != null;
        }

        private boolean isOperationExecuting(Address target) {
            // ask if op is still being executed?
            Boolean executing = Boolean.FALSE;
            try {
                Operation isStillExecuting;
                if (op instanceof TraceableOperation) {
                    TraceableOperation traceable = (TraceableOperation) op;
                    isStillExecuting = new TraceableIsStillExecuting(serviceName, traceable.getTraceIdentifier());
                } else {
                    isStillExecuting = new IsStillExecuting(op.getCallId());
                }
                final BasicInvocation inv = new BasicTargetInvocation(nodeEngine, serviceName, isStillExecuting,
                        target, 0, 0, 5000, null, null, true);
                Future f = inv.invoke();
                // TODO: @mm - improve logging (see SystemLogService)
                logger.warning("Asking if operation execution has been started: " + toString());
                executing = (Boolean) nodeEngine.toObject(f.get(5000, TimeUnit.MILLISECONDS));
            } catch (Exception e) {
                logger.warning("While asking 'is-executing': " + toString(), e);
            }
            // TODO: @mm - improve logging (see SystemLogService)
            logger.warning("'is-executing': " + executing + " -> " + toString());
            return executing;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("InvocationFuture{");
            sb.append("invocation=").append(BasicInvocation.this.toString());
            sb.append(", done=").append(isDone());
            sb.append('}');
            return sb.toString();
        }
    }

    private static class InternalResponse {

        String toString;

        private InternalResponse(String toString) {
            this.toString = toString;
        }

        public String toString() {
            return toString;
        }

    }
}
TOP

Related Classes of com.hazelcast.spi.impl.BasicInvocation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');