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

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

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2020, 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.impl;

import com.hazelcast.client.impl.clientside.ClientMessageDecoder;
import com.hazelcast.client.impl.protocol.ClientMessage;
import com.hazelcast.client.impl.spi.impl.ClientInvocationFuture;
import com.hazelcast.internal.serialization.SerializationService;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.spi.impl.DelegatingCompletableFuture;

import javax.annotation.Nonnull;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import static com.hazelcast.internal.util.ExceptionUtil.sneakyThrow;
import static java.util.Objects.requireNonNull;

/**
 * The Client Delegating Future is used to delegate {@link
 * ClientInvocationFuture} to a user type to be used with further computation stages or
 * {@code get()}. It converts {@link ClientMessage} coming from {@link
 * ClientInvocationFuture} to a user object.
 *
 * @param  Value type that the user expects
 */
@SuppressWarnings("checkstyle:methodcount")
public class ClientDelegatingFuture extends DelegatingCompletableFuture {

    private static final AtomicReferenceFieldUpdater DECODED_RESPONSE =
            AtomicReferenceFieldUpdater.newUpdater(ClientDelegatingFuture.class, Object.class, "decodedResponse");

    final boolean deserializeResponse;
    private final ClientMessageDecoder clientMessageDecoder;
    private volatile Object decodedResponse = VOID;

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

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

    public ClientDelegatingFuture(ClientInvocationFuture clientInvocationFuture,
                                  SerializationService serializationService,
                                  ClientMessageDecoder clientMessageDecoder) {
        this(clientInvocationFuture, serializationService, clientMessageDecoder, null, true);
    }

    public ClientDelegatingFuture(ClientInvocationFuture clientInvocationFuture,
                                  SerializationService serializationService,
                                  ClientMessageDecoder clientMessageDecoder, boolean deserializeResponse) {
        this(clientInvocationFuture, serializationService, clientMessageDecoder, null, deserializeResponse);
    }

    @Override
    public V getNow(V valueIfAbsent) {
        // if there is an explicit value set, we use that
        if (result != null) {
            return (V) result;
        }

        // if there already is a deserialized value set, use it.
        if (deserializedValue != VOID) {
            return (V) deserializedValue;
        }

        // otherwise, do not cache the value returned from future.getNow
        // because it might be the default valueIfAbsent
        Object value = future.getNow(valueIfAbsent);
        if (value instanceof ClientMessage) {
            return resolve(value);
        } else {
            return (value instanceof Data && deserializeResponse)
                    ? serializationService.toObject(value) : (V) value;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected V resolve(Object object) {
        if (result != null) {
            return (V) result;
        }

        // if there already is a deserialized value set, use it.
        if (deserializedValue != VOID) {
            return (V) deserializedValue;
        }

        ClientMessage clientMessage = (ClientMessage) object;
        Object decoded = decodeResponse(clientMessage);
        if (deserializeResponse) {
            decoded = serializationService.toObject(decoded);
        }
        cacheDeserializedValue(decoded);

        return (V) decoded;
    }

    private Object resolveAny(Object o) {
        if (o instanceof ClientMessage) {
            return resolve(o);
        }
        if (deserializeResponse) {
            return serializationService.toObject(o);
        }
        return o;
    }

    private Object decodeResponse(ClientMessage clientMessage) {
        if (decodedResponse != VOID) {
            return decodedResponse;
        }
        //Since frames are read, there should be no need to re-read to client message
        Object newDecodedResponse = clientMessageDecoder.decodeClientMessage(clientMessage);

        DECODED_RESPONSE.compareAndSet(this, VOID, newDecodedResponse);
        return newDecodedResponse;
    }

    protected ClientInvocationFuture getFuture() {
        return (ClientInvocationFuture) future;
    }

    @Override
    public  CompletableFuture thenApply(Function fn) {
        return future.thenApplyAsync(new DeserializingFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenApplyAsync(Function fn) {
        return future.thenApplyAsync(new DeserializingFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenApplyAsync(Function fn, Executor executor) {
        return future.thenApplyAsync(new DeserializingFunction<>(fn), executor);
    }

    @Override
    public CompletableFuture thenAccept(Consumer action) {
        return future.thenAcceptAsync(new DeserializingConsumer(action), defaultExecutor());
    }

    @Override
    public CompletableFuture thenAcceptAsync(Consumer action) {
        return future.thenAcceptAsync(new DeserializingConsumer(action), defaultExecutor());
    }

    @Override
    public CompletableFuture thenAcceptAsync(Consumer action, Executor executor) {
        return future.thenAcceptAsync(new DeserializingConsumer(action), executor);
    }

    @Override
    public CompletableFuture thenRun(Runnable action) {
        return future.thenRunAsync(action, defaultExecutor());
    }

    @Override
    public CompletableFuture thenRunAsync(Runnable action) {
        return future.thenRunAsync(action, defaultExecutor());
    }

    @Override
    public CompletableFuture thenRunAsync(Runnable action, Executor executor) {
        return future.thenRunAsync(action, executor);
    }

    @Override
    public  CompletableFuture thenCombine(CompletionStage other,
                                                     BiFunction fn) {
        return future.thenCombineAsync(other, new DeserializingBiFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenCombineAsync(CompletionStage other,
                                                          BiFunction fn) {
        return future.thenCombineAsync(other, new DeserializingBiFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenCombineAsync(CompletionStage other,
                                                          BiFunction fn, Executor executor) {
        return future.thenCombineAsync(other, new DeserializingBiFunction<>(fn), executor);
    }

    @Override
    public  CompletableFuture thenAcceptBoth(CompletionStage other,
                                                      BiConsumer action) {
        return future.thenAcceptBothAsync(other, new DeserializingBiConsumer<>(action), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenAcceptBothAsync(CompletionStage other,
                                                           BiConsumer action) {
        return future.thenAcceptBothAsync(other, new DeserializingBiConsumer<>(action), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenAcceptBothAsync(CompletionStage other,
                                                           BiConsumer action, Executor executor) {
        return future.thenAcceptBothAsync(other, new DeserializingBiConsumer<>(action), executor);
    }

    @Override
    public CompletableFuture runAfterBoth(CompletionStage other, Runnable action) {
        return future.runAfterBothAsync(other, action, defaultExecutor());
    }

    @Override
    public CompletableFuture runAfterBothAsync(CompletionStage other, Runnable action) {
        return future.runAfterBothAsync(other, action, defaultExecutor());
    }

    @Override
    public CompletableFuture runAfterBothAsync(CompletionStage other, Runnable action, Executor executor) {
        return future.runAfterBothAsync(other, action, executor);
    }

    @SuppressWarnings("unchecked")
    @Override
    public  CompletableFuture applyToEitherAsync(CompletionStage other, Function fn) {
        return applyToEitherAsync(future, other, new DeserializingFunction(fn), defaultExecutor());
    }

    @SuppressWarnings("unchecked")
    @Override
    public  CompletableFuture applyToEither(CompletionStage other, Function fn) {
         return applyToEitherAsync(future, other, new DeserializingFunction(fn), defaultExecutor());
    }

    @SuppressWarnings("unchecked")
    @Override
    public  CompletableFuture applyToEitherAsync(CompletionStage other, Function fn,
                                                       Executor executor) {
        return applyToEitherAsync(future, other, new DeserializingFunction(fn), executor);
    }

    @Override
    public CompletableFuture acceptEither(CompletionStage other, Consumer action) {
        return acceptEitherAsync(future, other, new DeserializingConsumer(action), defaultExecutor());
    }

    @Override
    public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action) {
        return acceptEitherAsync(future, other, new DeserializingConsumer(action), defaultExecutor());
    }

    @Override
    public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action,
                                                     Executor executor) {
        return acceptEitherAsync(future, other, new DeserializingConsumer(action), executor);
    }

    @Override
    public CompletableFuture runAfterEither(CompletionStage other, Runnable action) {
        return future.runAfterEitherAsync(other, action, defaultExecutor());
    }

    @Override
    public CompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action) {
        return future.runAfterEitherAsync(other, action, defaultExecutor());
    }

    @Override
    public CompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor) {
        return future.runAfterEitherAsync(other, action, executor);
    }

    @Override
    public  CompletableFuture thenCompose(Function> fn) {
        return future.thenComposeAsync(new DeserializingFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenComposeAsync(Function> fn) {
        return future.thenComposeAsync(new DeserializingFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture thenComposeAsync(Function> fn, Executor executor) {
        return future.thenComposeAsync(new DeserializingFunction<>(fn), executor);
    }

    @Override
    public CompletableFuture whenComplete(BiConsumer action) {
        return future.handleAsync(new WhenCompleteAdapter(action), defaultExecutor());
    }

    @Override
    public CompletableFuture whenCompleteAsync(BiConsumer action) {
        return future.handleAsync(new WhenCompleteAdapter(action), defaultExecutor());
    }

    @Override
    public CompletableFuture whenCompleteAsync(BiConsumer action, Executor executor) {
        return future.handleAsync(new WhenCompleteAdapter(action), executor);
    }

    @Override
    public  CompletableFuture handle(BiFunction fn) {
        return future.handleAsync(new DeserializingBiFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture handleAsync(BiFunction fn) {
        return future.handleAsync(new DeserializingBiFunction<>(fn), defaultExecutor());
    }

    @Override
    public  CompletableFuture handleAsync(BiFunction fn, Executor executor) {
        return future.handleAsync(new DeserializingBiFunction<>(fn), executor);
    }

    @Override
    public CompletableFuture toCompletableFuture() {
        return this;
    }

    @Override
    public CompletableFuture exceptionally(Function fn) {
        return future.handleAsync(new ExceptionallyAdapter(fn), defaultExecutor());
    }

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

    @Override
    public boolean isCancelled() {
        return future.isCancelled();
    }

    @Override
    public boolean isCompletedExceptionally() {
        return future.isCompletedExceptionally();
    }

    @Override
    public void obtrudeValue(V value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void obtrudeException(Throwable ex) {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getNumberOfDependents() {
        return future.getNumberOfDependents();
    }

    @Override
    public String toString() {
        return future.toString();
    }

    @SuppressWarnings("unchecked")
    private static CompletableFuture acceptEitherAsync(CompletableFuture stage, CompletionStage other,
                                                             Consumer action, Executor executor) {
        return stage.acceptEitherAsync(other, action, executor);
    }

    @SuppressWarnings("unchecked")
    private static CompletableFuture applyToEitherAsync(CompletableFuture stage, CompletionStage other,
                                                        Function fn, Executor executor) {
        return stage.applyToEitherAsync(other, fn, executor);
    }

    class DeserializingFunction implements Function {
        private final Function delegate;

        DeserializingFunction(@Nonnull Function delegate) {
            requireNonNull(delegate);
            this.delegate = delegate;
        }

        @Override
        public R apply(ClientMessage e) {
            return delegate.apply(resolve(e));
        }
    }

    class DeserializingConsumer implements Consumer {
        private final Consumer delegate;

        DeserializingConsumer(@Nonnull Consumer delegate) {
            requireNonNull(delegate);
            this.delegate = delegate;
        }

        @Override
        public void accept(ClientMessage e) {
            V resolved = resolve(e);
            delegate.accept(resolved);
        }

        @Override
        public String toString() {
            return "DeserializingConsumer{" + "delegate=" + delegate + '}';
        }
    }

    class DeserializingBiFunction implements BiFunction {
        private final BiFunction delegate;

        DeserializingBiFunction(@Nonnull BiFunction delegate) {
            requireNonNull(delegate);
            this.delegate = delegate;
        }

        @Override
        public R apply(ClientMessage t, U u) {
            V resolved = t == null ? null : resolve(t);
            return delegate.apply(resolved, (U) resolveAny(u));
        }
    }

    class DeserializingBiConsumer implements BiConsumer {
        private final BiConsumer delegate;

        DeserializingBiConsumer(@Nonnull BiConsumer delegate) {
            requireNonNull(delegate);
            this.delegate = delegate;
        }

        @Override
        public void accept(ClientMessage t, U u) {
            V resolved = t == null ? null : resolve(t);
            delegate.accept(resolved, (U) resolveAny(u));
        }
    }

    // adapts a BiConsumer to a BiFunction for implementation
    // of whenComplete methods
    class WhenCompleteAdapter implements BiFunction {
        private final BiConsumer delegate;

        WhenCompleteAdapter(@Nonnull BiConsumer delegate) {
            requireNonNull(delegate);
            this.delegate = delegate;
        }

        @Override
        public V apply(ClientMessage message, Throwable t) {
            V resolved = message == null ? null : resolve(message);
            Throwable delegateException = null;
            try {
                delegate.accept(resolved, t);
            } catch (Throwable throwable) {
                delegateException = throwable;
            }
            // implement whenComplete exception handling scheme:
            // - if original future was cancelled, throw wrapped in CompletionException
            // - if t != null, throw it
            // - if delegateException != null, throw it
            // - otherwise return resolved value
            if (t != null) {
                if (t instanceof CancellationException) {
                    throw new CompletionException(t);
                } else {
                    throw sneakyThrow(t);
                }
            } else if (delegateException != null) {
                throw sneakyThrow(delegateException);
            } else {
                return resolved;
            }
        }
    }

    // adapts a Consumer to a BiFunction for implementation of exceptionally()
    class ExceptionallyAdapter implements BiFunction {
        private final Function delegate;

        ExceptionallyAdapter(@Nonnull Function delegate) {
            requireNonNull(delegate);
            this.delegate = delegate;
        }

        @Override
        public V apply(ClientMessage message, Throwable t) {
            V resolved = message == null ? null : resolve(message);
            if (t == null) {
                return resolved;
            }
            try {
                return delegate.apply(t);
            } catch (Throwable throwable) {
                throw throwable;
            }
        }
    }
}