org.redisson.executor.ScheduledExecutorRemoteService.java Source code

Java tutorial

Introduction

Here is the source code for org.redisson.executor.ScheduledExecutorRemoteService.java

Source

/**
 * Copyright 2016 Nikita Koksharov
 *
 * 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 org.redisson.executor;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.redisson.RedissonExecutorService;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.api.RemoteInvocationOptions;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandExecutor;
import org.redisson.remote.RemoteServiceCancelRequest;
import org.redisson.remote.RemoteServiceCancelResponse;
import org.redisson.remote.RemoteServiceRequest;

import io.netty.util.Timeout;
import io.netty.util.TimerTask;

/**
 * 
 * @author Nikita Koksharov
 *
 */
public class ScheduledExecutorRemoteService extends ExecutorRemoteService {

    private String requestId;
    private String schedulerTasksName;
    private String schedulerQueueName;
    private String schedulerChannelName;

    public ScheduledExecutorRemoteService(Codec codec, RedissonClient redisson, String name,
            CommandExecutor commandExecutor) {
        super(codec, redisson, name, commandExecutor);
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    public void setSchedulerTasksName(String schedulerTasksName) {
        this.schedulerTasksName = schedulerTasksName;
    }

    public void setSchedulerChannelName(String schedulerChannelName) {
        this.schedulerChannelName = schedulerChannelName;
    }

    public void setSchedulerQueueName(String scheduledQueueName) {
        this.schedulerQueueName = scheduledQueueName;
    }

    @Override
    protected RFuture<Boolean> addAsync(RBlockingQueue<RemoteServiceRequest> requestQueue,
            RemoteServiceRequest request) {
        Long startTime = (Long) request.getArgs()[3];
        byte[] encodedRequest = encode(request);

        if (requestId != null) {
            return commandExecutor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                    // check if executor service not in shutdown state and previous task exists
                    "if redis.call('exists', KEYS[2]) == 0 and redis.call('hexists', KEYS[5], ARGV[2]) == 1 then "
                            + "redis.call('zadd', KEYS[3], ARGV[1], ARGV[2]);"
                            + "redis.call('hset', KEYS[5], ARGV[2], ARGV[3]);" + "redis.call('incr', KEYS[1]);"
                            // if new task added to queue head then publish its startTime 
                            // to all scheduler workers 
                            + "local v = redis.call('zrange', KEYS[3], 0, 0); " + "if v[1] == ARGV[2] then "
                            + "redis.call('publish', KEYS[4], ARGV[1]); " + "end " + "return 1;" + "end;"
                            + "return 0;",
                    Arrays.<Object>asList(tasksCounterName, statusName, schedulerQueueName, schedulerChannelName,
                            schedulerTasksName),
                    startTime, request.getRequestId(), encodedRequest);
        }

        return commandExecutor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                // check if executor service not in shutdown state
                "if redis.call('exists', KEYS[2]) == 0 then " + "redis.call('zadd', KEYS[3], ARGV[1], ARGV[2]);"
                        + "redis.call('hset', KEYS[5], ARGV[2], ARGV[3]);" + "redis.call('incr', KEYS[1]);"
                        + "local v = redis.call('zrange', KEYS[3], 0, 0); "
                        // if new task added to queue head then publish its startTime 
                        // to all scheduler workers 
                        + "if v[1] == ARGV[2] then " + "redis.call('publish', KEYS[4], ARGV[1]); " + "end "
                        + "return 1;" + "end;" + "return 0;",
                Arrays.<Object>asList(tasksCounterName, statusName, schedulerQueueName, schedulerChannelName,
                        schedulerTasksName),
                startTime, request.getRequestId(), encodedRequest);
    }

    @Override
    protected void awaitResultAsync(final RemoteInvocationOptions optionsCopy, final RemotePromise<Object> result,
            final RemoteServiceRequest request, final String responseName) {
        if (!optionsCopy.isResultExpected()) {
            return;
        }

        Long startTime = 0L;
        if (request != null && request.getArgs() != null && request.getArgs().length > 3) {
            startTime = (Long) request.getArgs()[3];
        }
        long delay = startTime - System.currentTimeMillis();
        if (delay > 0) {
            commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
                @Override
                public void run(Timeout timeout) throws Exception {
                    ScheduledExecutorRemoteService.super.awaitResultAsync(optionsCopy, result, request,
                            responseName);
                }
            }, delay, TimeUnit.MILLISECONDS);
        } else {
            super.awaitResultAsync(optionsCopy, result, request, responseName);
        }
    }

    @Override
    protected boolean remove(RBlockingQueue<RemoteServiceRequest> requestQueue, RemoteServiceRequest request) {
        return commandExecutor.evalWrite(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                // remove from scheduler queue
                "if redis.call('zrem', KEYS[2], ARGV[1]) > 0 then " + "redis.call('hdel', KEYS[6], ARGV[1]); "
                        + "if redis.call('decr', KEYS[3]) == 0 then " + "redis.call('del', KEYS[3]);"
                        + "if redis.call('get', KEYS[4]) == ARGV[2] then " + "redis.call('set', KEYS[4], ARGV[3]);"
                        + "redis.call('publish', KEYS[5], ARGV[3]);" + "end;" + "end;" + "return 1;" + "end;"
                        + "local task = redis.call('hget', KEYS[6], ARGV[1]); "
                        // remove from executor queue
                        + "if task ~= nil and redis.call('lrem', KEYS[1], 1, task) > 0 then "
                        + "redis.call('hdel', KEYS[6], ARGV[1]); " + "if redis.call('decr', KEYS[3]) == 0 then "
                        + "redis.call('del', KEYS[3]);" + "if redis.call('get', KEYS[4]) == ARGV[2] then "
                        + "redis.call('set', KEYS[4], ARGV[3]);" + "redis.call('publish', KEYS[5], ARGV[3]);"
                        + "end;" + "end;" + "return 1;" + "end;"
                        // delete scheduled task
                        + "redis.call('hdel', KEYS[6], ARGV[1]); " + "return 0;",
                Arrays.<Object>asList(requestQueue.getName(), schedulerQueueName, tasksCounterName, statusName,
                        terminationTopicName, schedulerTasksName),
                request.getRequestId(), RedissonExecutorService.SHUTDOWN_STATE,
                RedissonExecutorService.TERMINATED_STATE);
    }

    @Override
    protected String generateRequestId() {
        if (requestId == null) {
            return super.generateRequestId();
        }
        return requestId;
    }

    public boolean cancelExecution(String requestId) {
        Class<?> syncInterface = RemoteExecutorService.class;
        String requestQueueName = getRequestQueueName(syncInterface);
        String cancelRequestName = getCancelRequestQueueName(syncInterface, requestId);

        if (!redisson.getMap(schedulerTasksName, LongCodec.INSTANCE).containsKey(requestId)) {
            return false;
        }

        RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName, getCodec());

        RemoteServiceRequest request = new RemoteServiceRequest(requestId);
        if (remove(requestQueue, request)) {
            return true;
        }

        RBlockingQueue<RemoteServiceCancelRequest> cancelRequestQueue = redisson.getBlockingQueue(cancelRequestName,
                getCodec());
        cancelRequestQueue.putAsync(new RemoteServiceCancelRequest(true, requestId + ":cancel-response"));
        cancelRequestQueue.expireAsync(60, TimeUnit.SECONDS);

        String responseQueueName = getResponseQueueName(syncInterface, requestId + ":cancel-response");
        RBlockingQueue<RemoteServiceCancelResponse> responseQueue = redisson.getBlockingQueue(responseQueueName,
                getCodec());
        try {
            RemoteServiceCancelResponse response = responseQueue.poll(60, TimeUnit.SECONDS);
            if (response == null) {
                return false;
            }
            return response.isCanceled();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

}