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

com.hazelcast.client.util.ClientDelegatingFuture Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2016, 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.client.util;

import com.hazelcast.client.impl.ClientMessageDecoder;
import com.hazelcast.client.impl.protocol.ClientMessage;
import com.hazelcast.client.spi.impl.ClientInvocationFuture;
import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.spi.InternalCompletableFuture;
import com.hazelcast.spi.serialization.SerializationService;
import com.hazelcast.util.ExceptionUtil;

import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Client Delegating Future is used to delegate ClientInvocationFuture to user to be used with
 * andThen or get. It converts ClientMessage coming from ClientInvocationFuture to user object
 *
 * @param  Value type that user expecting
 */
public class ClientDelegatingFuture implements InternalCompletableFuture {

    private final ClientInvocationFuture future;
    private final SerializationService serializationService;
    private final ClientMessageDecoder clientMessageDecoder;
    private final V defaultValue;
    private final Object mutex = new Object();
    private Throwable error;
    private V deserializedValue;
    /**
     * mutex object is used as an initial NIL value
     */
    private volatile Object response = mutex;
    private volatile boolean done;

    public ClientDelegatingFuture(ClientInvocationFuture clientInvocationFuture,
                                  SerializationService serializationService,
                                  ClientMessageDecoder clientMessageDecoder, V defaultValue) {
        this.future = clientInvocationFuture;
        this.serializationService = serializationService;
        this.clientMessageDecoder = clientMessageDecoder;
        this.defaultValue = defaultValue;
    }

    public ClientDelegatingFuture(ClientInvocationFuture clientInvocationFuture,
                                  SerializationService serializationService, ClientMessageDecoder clientMessageDecoder) {
        this.future = clientInvocationFuture;
        this.serializationService = serializationService;
        this.clientMessageDecoder = clientMessageDecoder;
        this.defaultValue = null;
    }

    public  void andThenInternal(final ExecutionCallback callback) {
        future.andThen(new DelegatingExecutionCallback(callback, false));
    }

    @Override
    public void andThen(final ExecutionCallback callback) {
        future.andThen(new DelegatingExecutionCallback(callback, true));
    }

    @Override
    public void andThen(final ExecutionCallback callback, Executor executor) {
        future.andThen(new DelegatingExecutionCallback(callback, true), executor);
    }

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

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

    @Override
    public final boolean isDone() {
        return done ? done : future.isDone();
    }

    public Object getResponse() {
        return response;
    }

    @Override
    public V get() throws InterruptedException, ExecutionException {
        try {
            return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            return ExceptionUtil.sneakyThrow(e);
        }
    }

    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        if (!done || !isResponseSet()) {
            synchronized (mutex) {
                if (!done || !isResponseSet()) {
                    try {
                        response = resolveMessageToValue(future.get(timeout, unit));
                        if (deserializedValue == null) {
                            deserializedValue = serializationService.toObject(response);
                        }
                    } catch (InterruptedException e) {
                        error = e;
                    } catch (ExecutionException e) {
                        error = e;
                    }
                    done = true;
                }
            }
        }
        if (error != null) {
            if (error instanceof CancellationException) {
                throw (CancellationException) error;
            }
            if (error.getCause() instanceof CancellationException) {
                throw (CancellationException) error.getCause();
            }
            if (error instanceof ExecutionException) {
                throw (ExecutionException) error;
            }
            if (error instanceof InterruptedException) {
                throw (InterruptedException) error;
            }
            // should not happen!
            throw new ExecutionException(error);
        }
        return getResult();
    }

    @Override
    public V join() {
        try {
            return get();
        } catch (Exception e) {
            throw ExceptionUtil.rethrow(e);
        }
    }

    @Override
    public V getSafely() {
        return join();
    }

    private V getResult() {
        if (defaultValue != null) {
            return defaultValue;
        }

        // If value is already deserialized, use it.
        if (deserializedValue != null) {
            return deserializedValue;
        }
        // Otherwise, it is possible that received data may not be deserialized
        // if "shouldDeserializeData" flag is not true in any of registered "DelegatingExecutionCallback".
        // So, be sure that value is deserialized before returning to caller.
        deserializedValue = serializationService.toObject(response);
        return deserializedValue;
    }

    private Object resolveMessageToValue(ClientMessage message) {
        return clientMessageDecoder.decodeClientMessage(message);
    }

    protected void setError(Throwable error) {
        this.error = error;
    }

    protected void setDone() {
        this.done = true;
    }

    protected ClientInvocationFuture getFuture() {
        return future;
    }

    private boolean isResponseSet() {
        return response != mutex;
    }

    class DelegatingExecutionCallback implements ExecutionCallback {

        private final ExecutionCallback callback;
        private final boolean shouldDeserializeData;

        DelegatingExecutionCallback(ExecutionCallback callback, boolean shouldDeserializeData) {
            this.callback = callback;
            this.shouldDeserializeData = shouldDeserializeData;
        }

        @Override
        public void onResponse(ClientMessage message) {
            if (!done || !isResponseSet()) {
                synchronized (mutex) {
                    if (!done || !isResponseSet()) {
                        response = resolveMessageToValue(message);
                        if (shouldDeserializeData && deserializedValue == null) {
                            deserializedValue = serializationService.toObject(response);
                        }
                        done = true;
                    }
                }
            }
            if (shouldDeserializeData) {
                callback.onResponse((T) deserializedValue);
            } else {
                callback.onResponse((T) response);
            }
        }

        @Override
        public void onFailure(Throwable t) {
            if (!done) {
                synchronized (mutex) {
                    if (!done) {
                        error = t;
                        done = true;
                    }
                }
            }
            callback.onFailure(t);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy