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

com.hazelcast.client.proxy.ClientExecutorServiceProxy Maven / Gradle / Ivy

There is a newer version: 3.12.13
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.proxy;

import com.hazelcast.client.impl.clientside.ClientMessageDecoder;
import com.hazelcast.client.impl.protocol.ClientMessage;
import com.hazelcast.client.impl.protocol.codec.ExecutorServiceIsShutdownCodec;
import com.hazelcast.client.impl.protocol.codec.ExecutorServiceShutdownCodec;
import com.hazelcast.client.impl.protocol.codec.ExecutorServiceSubmitToAddressCodec;
import com.hazelcast.client.impl.protocol.codec.ExecutorServiceSubmitToPartitionCodec;
import com.hazelcast.client.spi.ClientContext;
import com.hazelcast.client.spi.ClientPartitionService;
import com.hazelcast.client.spi.ClientProxy;
import com.hazelcast.client.spi.impl.ClientInvocation;
import com.hazelcast.client.spi.impl.ClientInvocationFuture;
import com.hazelcast.client.util.ClientDelegatingFuture;
import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.HazelcastException;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.Member;
import com.hazelcast.core.MemberSelector;
import com.hazelcast.core.MultiExecutionCallback;
import com.hazelcast.core.PartitionAware;
import com.hazelcast.executor.impl.RunnableAdapter;
import com.hazelcast.monitor.LocalExecutorStats;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.spi.serialization.SerializationService;
import com.hazelcast.util.Clock;
import com.hazelcast.util.UuidUtil;
import com.hazelcast.util.executor.CompletedFuture;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

import static com.hazelcast.util.ExceptionUtil.rethrow;
import static com.hazelcast.util.Preconditions.checkNotNull;

/**
 * @author ali 5/24/13
 */
public class ClientExecutorServiceProxy extends ClientProxy implements IExecutorService {

    private static final int MIN_TIME_RESOLUTION_OF_CONSECUTIVE_SUBMITS = 10;
    private static final int MAX_CONSECUTIVE_SUBMITS = 100;
    private static final ClientMessageDecoder SUBMIT_TO_PARTITION_DECODER = new ClientMessageDecoder() {
        @Override
        public  T decodeClientMessage(ClientMessage clientMessage) {
            return (T) ExecutorServiceSubmitToPartitionCodec.decodeResponse(clientMessage).response;
        }
    };

    private static final ClientMessageDecoder SUBMIT_TO_ADDRESS_DECODER = new ClientMessageDecoder() {
        @Override
        public  T decodeClientMessage(ClientMessage clientMessage) {
            return (T) ExecutorServiceSubmitToAddressCodec.decodeResponse(clientMessage).response;
        }
    };

    private final Random random = new Random(-System.currentTimeMillis());
    private final AtomicInteger consecutiveSubmits = new AtomicInteger();
    private volatile long lastSubmitTime;

    public ClientExecutorServiceProxy(String serviceName, String objectId, ClientContext context) {
        super(serviceName, objectId, context);
    }

    // execute on members

    @Override
    public void execute(Runnable command) {
        submit(command);
    }

    @Override
    public void executeOnKeyOwner(Runnable command, Object key) {
        Callable callable = createRunnableAdapter(command);
        submitToKeyOwner(callable, key);
    }

    @Override
    public void executeOnMember(Runnable command, Member member) {
        Callable callable = createRunnableAdapter(command);
        submitToMember(callable, member);
    }

    @Override
    public void executeOnMembers(Runnable command, Collection members) {
        Callable callable = createRunnableAdapter(command);
        for (Member member : members) {
            submitToMember(callable, member);
        }
    }

    @Override
    public void execute(Runnable command, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        executeOnMember(command, members.get(selectedMember));
    }

    @Override
    public void executeOnMembers(Runnable command, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        executeOnMembers(command, members);
    }

    @Override
    public void executeOnAllMembers(Runnable command) {
        Callable callable = createRunnableAdapter(command);
        final Collection memberList = getContext().getClusterService().getMemberList();
        for (Member member : memberList) {
            submitToMember(callable, member);
        }
    }


    // submit to members

    @Override
    public  Future submitToMember(Callable task, Member member) {
        final Address memberAddress = getMemberAddress(member);
        return submitToTargetInternal(toData(task), memberAddress, null, false);
    }

    @Override
    public  Map> submitToMembers(Callable task, Collection members) {
        Map> futureMap = new HashMap>(members.size());
        Data taskData = toData(task);
        for (Member member : members) {
            final Address memberAddress = getMemberAddress(member);
            Future f = submitToTargetInternal(taskData, memberAddress, null, true);
            futureMap.put(member, f);
        }
        return futureMap;
    }

    @Override
    public  Future submit(Callable task, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        return submitToMember(task, members.get(selectedMember));
    }

    @Override
    public  Map> submitToMembers(Callable task, MemberSelector memberSelector) {
        List members = selectMembers(memberSelector);
        return submitToMembers(task, members);
    }

    @Override
    public  Map> submitToAllMembers(Callable task) {
        final Collection memberList = getContext().getClusterService().getMemberList();
        Map> futureMap = new HashMap>(memberList.size());
        Data taskData = toData(task);
        for (Member m : memberList) {
            Future f = submitToTargetInternal(taskData, m.getAddress(), null, true);
            futureMap.put(m, f);
        }
        return futureMap;
    }

    // submit to members callback
    @Override
    public void submitToMember(Runnable command, Member member, ExecutionCallback callback) {
        Callable callable = createRunnableAdapter(command);
        submitToMember(callable, member, callback);
    }

    @Override
    public void submitToMembers(Runnable command, Collection members, MultiExecutionCallback callback) {
        Callable callable = createRunnableAdapter(command);
        MultiExecutionCallbackWrapper multiExecutionCallbackWrapper =
                new MultiExecutionCallbackWrapper(members.size(), callback);
        for (Member member : members) {
            final ExecutionCallbackWrapper executionCallback =
                    new ExecutionCallbackWrapper(multiExecutionCallbackWrapper, member);
            submitToMember(callable, member, executionCallback);
        }
    }

    @Override
    public  void submitToMember(Callable task, Member member, ExecutionCallback callback) {
        final Address memberAddress = getMemberAddress(member);
        submitToTargetInternal(toData(task), memberAddress, callback);
    }

    @Override
    public  void submitToMembers(Callable task, Collection members, MultiExecutionCallback callback) {
        MultiExecutionCallbackWrapper multiExecutionCallbackWrapper =
                new MultiExecutionCallbackWrapper(members.size(), callback);
        Data taskData = toData(task);
        for (Member member : members) {
            final ExecutionCallbackWrapper executionCallback =
                    new ExecutionCallbackWrapper(multiExecutionCallbackWrapper, member);
            submitToTargetInternal(taskData, getMemberAddress(member), executionCallback);
        }
    }

    @Override
    public void submit(Runnable task, MemberSelector memberSelector, ExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        submitToMember(task, members.get(selectedMember), callback);
    }

    @Override
    public void submitToMembers(Runnable task, MemberSelector memberSelector, MultiExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        submitToMembers(task, members, callback);
    }

    @Override
    public  void submit(Callable task, MemberSelector memberSelector, ExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        int selectedMember = random.nextInt(members.size());
        submitToMember(task, members.get(selectedMember), callback);
    }

    @Override
    public  void submitToMembers(Callable task, MemberSelector memberSelector, MultiExecutionCallback callback) {
        List members = selectMembers(memberSelector);
        submitToMembers(task, members, callback);
    }

    @Override
    public void submitToAllMembers(Runnable command, MultiExecutionCallback callback) {
        Callable callable = createRunnableAdapter(command);
        submitToAllMembers(callable, callback);
    }

    @Override
    public  void submitToAllMembers(Callable task, MultiExecutionCallback callback) {
        final Collection memberList = getContext().getClusterService().getMemberList();
        submitToMembers(task, memberList, callback);
    }

    // submit random

    @Override
    public Future submit(Runnable command) {
        final Object partitionKey = getTaskPartitionKey(command);
        Callable callable = createRunnableAdapter(command);
        if (partitionKey != null) {
            return submitToKeyOwner(callable, partitionKey);
        }
        return submitToRandomInternal(callable, null, false);
    }

    @Override
    public  Future submit(Runnable command, T result) {
        final Object partitionKey = getTaskPartitionKey(command);
        Callable callable = createRunnableAdapter(command);
        if (partitionKey != null) {
            return submitToKeyOwnerInternal(callable, partitionKey, result, false);
        }
        return submitToRandomInternal(callable, result, false);
    }

    @Override
    public  Future submit(Callable task) {
        final Object partitionKey = getTaskPartitionKey(task);
        if (partitionKey != null) {
            return submitToKeyOwner(task, partitionKey);
        }
        return submitToRandomInternal(task, null, false);
    }

    @Override
    public  void submit(Runnable command, ExecutionCallback callback) {
        final Object partitionKey = getTaskPartitionKey(command);
        Callable callable = createRunnableAdapter(command);
        if (partitionKey != null) {
            submitToKeyOwnerInternal(callable, partitionKey, callback);
        } else {
            submitToRandomInternal(callable, callback);
        }
    }

    @Override
    public  void submit(Callable task, ExecutionCallback callback) {
        final Object partitionKey = getTaskPartitionKey(task);
        if (partitionKey != null) {
            submitToKeyOwnerInternal(task, partitionKey, callback);
        } else {
            submitToRandomInternal(task, callback);
        }
    }

    // submit to key

    @Override
    public  Future submitToKeyOwner(Callable task, Object key) {
        return submitToKeyOwnerInternal(task, key, null, false);
    }

    @Override
    public void submitToKeyOwner(Runnable command, Object key, ExecutionCallback callback) {
        Callable callable = createRunnableAdapter(command);
        submitToKeyOwner(callable, key, callback);
    }

    @Override
    public  void submitToKeyOwner(Callable task, Object key, ExecutionCallback callback) {
        submitToKeyOwnerInternal(task, key, callback);
    }

    // end

    @Override
    public LocalExecutorStats getLocalExecutorStats() {
        throw new UnsupportedOperationException("Locality is ambiguous for client!");
    }

    @Override
    public void shutdown() {
        ClientMessage request = ExecutorServiceShutdownCodec.encodeRequest(name);
        invoke(request);
    }

    @Override
    public List shutdownNow() {
        shutdown();
        return Collections.emptyList();
    }

    @Override
    public boolean isShutdown() {
        ClientMessage request = ExecutorServiceIsShutdownCodec.encodeRequest(name);
        ClientMessage response = invoke(request);
        ExecutorServiceIsShutdownCodec.ResponseParameters resultParameters =
                ExecutorServiceIsShutdownCodec.decodeResponse(response);
        return resultParameters.response;
    }

    @Override
    public boolean isTerminated() {
        return isShutdown();
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public  List> invokeAll(Collection> tasks) throws InterruptedException {
        final List> futures = new ArrayList>(tasks.size());
        final List> result = new ArrayList>(tasks.size());
        for (Callable task : tasks) {
            futures.add(submitToRandomInternal(task, null, true));
        }
        Executor userExecutor = getContext().getExecutionService().getUserExecutor();
        for (Future future : futures) {
            Object value = retrieveResult(future);
            result.add(new CompletedFuture(getSerializationService(), value, userExecutor));
        }
        return result;
    }

    @Override
    public  List> invokeAll(Collection> tasks, long timeout, TimeUnit unit)
            throws InterruptedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public  T invokeAny(Collection> tasks)
            throws InterruptedException, ExecutionException {
        throw new UnsupportedOperationException();
    }

    @Override
    public  T invokeAny(Collection> tasks, long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        throw new UnsupportedOperationException();
    }

    private Object getTaskPartitionKey(Object task) {
        if (task instanceof PartitionAware) {
            return ((PartitionAware) task).getPartitionKey();
        }
        return null;
    }

    private  RunnableAdapter createRunnableAdapter(Runnable command) {
        if (command == null) {
            throw new NullPointerException();
        }
        return new RunnableAdapter(command);
    }

    private  Future submitToKeyOwnerInternal(Callable task, Object key, T defaultValue, boolean preventSync) {
        checkNotNull(task, "task should not be null");

        String uuid = getUUID();
        int partitionId = getPartitionId(key);
        ClientMessage request =
                ExecutorServiceSubmitToPartitionCodec.encodeRequest(name, uuid, toData(task), partitionId);
        ClientInvocationFuture f = invokeOnPartitionOwner(request, partitionId);
        return checkSync(f, uuid, partitionId, preventSync, defaultValue);
    }

    private  void submitToKeyOwnerInternal(Callable task, Object key, ExecutionCallback callback) {
        checkNotNull(task, "task should not be null");

        String uuid = getUUID();
        int partitionId = getPartitionId(key);
        ClientMessage request = ExecutorServiceSubmitToPartitionCodec.encodeRequest(name, uuid, toData(task), partitionId);
        ClientInvocationFuture f = invokeOnPartitionOwner(request, partitionId);

        ClientDelegatingFuture delegatingFuture = new ClientDelegatingFuture(f, getSerializationService(),
                SUBMIT_TO_PARTITION_DECODER);
        delegatingFuture.andThen(callback);
    }

    private  Future submitToRandomInternal(Callable task, T defaultValue, boolean preventSync) {
        checkNotNull(task, "task should not be null");

        String uuid = getUUID();
        int partitionId = randomPartitionId();
        ClientMessage request =
                ExecutorServiceSubmitToPartitionCodec.encodeRequest(name, uuid, toData(task), partitionId);
        ClientInvocationFuture f = invokeOnPartitionOwner(request, partitionId);
        return checkSync(f, uuid, partitionId, preventSync, defaultValue);
    }

    private  void submitToRandomInternal(Callable task, ExecutionCallback callback) {
        checkNotNull(task, "task should not be null");

        String uuid = getUUID();
        int partitionId = randomPartitionId();
        ClientMessage request =
                ExecutorServiceSubmitToPartitionCodec.encodeRequest(name, uuid, toData(task), partitionId);
        ClientInvocationFuture f = invokeOnPartitionOwner(request, partitionId);
        ClientDelegatingFuture delegatingFuture = new ClientDelegatingFuture(f, getSerializationService(),
                SUBMIT_TO_PARTITION_DECODER);
        delegatingFuture.andThen(callback);
    }

    private  Future submitToTargetInternal(Data taskData, Address address
            , T defaultValue, boolean preventSync) {
        checkNotNull(taskData, "task should not be null");

        String uuid = getUUID();
        ClientMessage request = ExecutorServiceSubmitToAddressCodec.encodeRequest(name, uuid, taskData, address);
        ClientInvocationFuture f = invokeOnTarget(request, address);
        return checkSync(f, uuid, address, preventSync, defaultValue);
    }

    private  void submitToTargetInternal(Data taskData, Address address, ExecutionCallback callback) {
        checkNotNull(taskData, "task should not be null");
        String uuid = getUUID();
        ClientMessage request = ExecutorServiceSubmitToAddressCodec.encodeRequest(name, uuid, taskData, address);
        ClientInvocationFuture f = invokeOnTarget(request, address);
        ClientDelegatingFuture delegatingFuture = new ClientDelegatingFuture(f, getSerializationService(),
                SUBMIT_TO_ADDRESS_DECODER);
        delegatingFuture.andThen(callback);
    }

    @Override
    public String toString() {
        return "IExecutorService{" + "name='" + name + '\'' + '}';
    }

    private  Future checkSync(ClientInvocationFuture f, String uuid, Address address,
                                    boolean preventSync, T defaultValue) {
        boolean sync = isSyncComputation(preventSync);
        if (sync) {
            Object response = retrieveResultFromMessage(f);
            Executor userExecutor = getContext().getExecutionService().getUserExecutor();
            return new CompletedFuture(getSerializationService(), response, userExecutor);
        } else {
            return new IExecutorDelegatingFuture(f, getContext(), uuid, defaultValue,
                    SUBMIT_TO_ADDRESS_DECODER, name, address);
        }
    }

    private  Future checkSync(ClientInvocationFuture f, String uuid, int partitionId,
                                    boolean preventSync, T defaultValue) {
        boolean sync = isSyncComputation(preventSync);
        if (sync) {
            Object response = retrieveResultFromMessage(f);
            Executor userExecutor = getContext().getExecutionService().getUserExecutor();
            return new CompletedFuture(getSerializationService(), response, userExecutor);
        } else {
            return new IExecutorDelegatingFuture(f, getContext(), uuid, defaultValue,
                    SUBMIT_TO_PARTITION_DECODER, name, partitionId);
        }
    }

    private  Object retrieveResult(Future f) {
        Object response;
        try {
            response = f.get();
        } catch (Exception e) {
            response = e;
        }
        return response;
    }

    private Object retrieveResultFromMessage(ClientInvocationFuture f) {
        Object response;
        try {
            SerializationService serializationService = getClient().getSerializationService();
            Data data = ExecutorServiceSubmitToAddressCodec.decodeResponse(f.get()).response;
            response = serializationService.toObject(data);
        } catch (Exception e) {
            response = e;
        }
        return response;
    }

    private boolean isSyncComputation(boolean preventSync) {
        long now = Clock.currentTimeMillis();

        long last = lastSubmitTime;
        lastSubmitTime = now;

        AtomicInteger consecutiveSubmits = this.consecutiveSubmits;

        if (last + MIN_TIME_RESOLUTION_OF_CONSECUTIVE_SUBMITS < now) {
            consecutiveSubmits.set(0);
            return false;
        }

        return !preventSync
                && consecutiveSubmits.incrementAndGet() % MAX_CONSECUTIVE_SUBMITS == 0;
    }

    private List selectMembers(MemberSelector memberSelector) {
        if (memberSelector == null) {
            throw new IllegalArgumentException("memberSelector must not be null");
        }
        List selected = new ArrayList();
        Collection members = getContext().getClusterService().getMemberList();
        for (Member member : members) {
            if (memberSelector.select(member)) {
                selected.add(member);
            }
        }
        if (selected.isEmpty()) {
            throw new RejectedExecutionException("No member selected with memberSelector[" + memberSelector + "]");
        }
        return selected;
    }

    private static final class ExecutionCallbackWrapper implements ExecutionCallback {
        MultiExecutionCallbackWrapper multiExecutionCallbackWrapper;

        Member member;

        private ExecutionCallbackWrapper(MultiExecutionCallbackWrapper multiExecutionCallback, Member member) {
            this.multiExecutionCallbackWrapper = multiExecutionCallback;
            this.member = member;
        }

        @Override
        public void onResponse(T response) {
            multiExecutionCallbackWrapper.onResponse(member, response);
        }

        @Override
        public void onFailure(Throwable t) {
            multiExecutionCallbackWrapper.onResponse(member, t);
        }
    }

    private static final class MultiExecutionCallbackWrapper implements MultiExecutionCallback {

        private final MultiExecutionCallback multiExecutionCallback;
        private final Map values;
        private final AtomicInteger members;

        private MultiExecutionCallbackWrapper(int memberSize, MultiExecutionCallback multiExecutionCallback) {
            this.multiExecutionCallback = multiExecutionCallback;
            this.values = Collections.synchronizedMap(new HashMap(memberSize));
            this.members = new AtomicInteger(memberSize);
        }

        @Override
        public void onResponse(Member member, Object value) {
            multiExecutionCallback.onResponse(member, value);
            values.put(member, value);

            int waitingResponse = members.decrementAndGet();
            if (waitingResponse == 0) {
                onComplete(values);
            }
        }

        @Override
        public void onComplete(Map values) {
            multiExecutionCallback.onComplete(values);
        }
    }

    private ClientInvocationFuture invokeOnPartitionOwner(ClientMessage request, int partitionId) {
        try {
            ClientInvocation clientInvocation = new ClientInvocation(getClient(), request, getName(), partitionId);
            return clientInvocation.invoke();
        } catch (Exception e) {
            throw rethrow(e);
        }
    }

    private ClientInvocationFuture invokeOnTarget(ClientMessage request, Address target) {
        try {
            ClientInvocation invocation = new ClientInvocation(getClient(), request, getName(), target);
            return invocation.invoke();
        } catch (Exception e) {
            throw rethrow(e);
        }
    }

    private String getUUID() {
        return UuidUtil.newUnsecureUuidString();
    }

    private Address getMemberAddress(Member member) {
        Member m = getContext().getClusterService().getMember(member.getUuid());
        if (m == null) {
            throw new HazelcastException(member + " is not available!");
        }
        return m.getAddress();
    }

    private int getPartitionId(Object key) {
        ClientPartitionService partitionService = getContext().getPartitionService();
        return partitionService.getPartitionId(key);
    }

    private int randomPartitionId() {
        ClientPartitionService partitionService = getContext().getPartitionService();
        return random.nextInt(partitionService.getPartitionCount());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy