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

org.redisson.RedissonScript Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.40.2
Show newest version
/**
 * Copyright (c) 2013-2024 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;

import org.redisson.api.RFuture;
import org.redisson.api.RScript;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.misc.CompletableFutureWrapper;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 
 * @author Nikita Koksharov
 *
 */
public class RedissonScript implements RScript {

    private final Codec codec;
    private final CommandAsyncExecutor commandExecutor;

    public RedissonScript(CommandAsyncExecutor commandExecutor) {
        this.commandExecutor = commandExecutor;
        this.codec = commandExecutor.getServiceManager().getCfg().getCodec();
    }
    
    public RedissonScript(CommandAsyncExecutor commandExecutor, Codec codec) {
        this.commandExecutor = commandExecutor;
        this.codec = commandExecutor.getServiceManager().getCodec(codec);
    }

    @Override
    public String scriptLoad(String luaScript) {
        return commandExecutor.get(scriptLoadAsync(luaScript));
    }

    public String scriptLoad(String key, String luaScript) {
        return commandExecutor.get(scriptLoadAsync(key, luaScript));
    }

    @Override
    public RFuture scriptLoadAsync(String luaScript) {
        List> futures = commandExecutor.executeAllAsync(RedisCommands.SCRIPT_LOAD, luaScript);
        CompletableFuture f = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        CompletableFuture s = f.thenApply(r -> futures.get(0).getNow(null));
        return new CompletableFutureWrapper<>(s);
    }

    @Override
    public RFuture scriptLoadAsync(String key, String luaScript) {
        return commandExecutor.writeAsync(key, StringCodec.INSTANCE, RedisCommands.SCRIPT_LOAD, luaScript);
    }

    @Override
    public  R eval(Mode mode, String luaScript, ReturnType returnType) {
        return eval(mode, luaScript, returnType, Collections.emptyList());
    }

    @Override
    public  R eval(Mode mode, String luaScript, ReturnType returnType, List keys, Object... values) {
        String key = getKey(keys);
        return eval(key, mode, luaScript, returnType, keys, values);
    }

    private static String getKey(List keys) {
        String key = null;
        if (!keys.isEmpty()) {
            if (keys.get(0) instanceof byte[]) {
                key = new String((byte[]) keys.get(0));
            } else {
                key = keys.get(0).toString();
            }
        }
        return key;
    }

    @Override
    public  RFuture evalAsync(Mode mode, String luaScript, ReturnType returnType, List keys, Object... values) {
        String key = getKey(keys);
        return evalAsync(key, mode, luaScript, returnType, keys, values);
    }

    @Override
    public  R evalSha(Mode mode, String shaDigest, ReturnType returnType) {
        return evalSha(null, mode, shaDigest, returnType, Collections.emptyList());
    }

    @Override
    public  R evalSha(Mode mode, String shaDigest, ReturnType returnType, List keys, Object... values) {
        return evalSha(null, mode, shaDigest, returnType, keys, values);
    }

    @Override
    public  RFuture evalShaAsync(Mode mode, String shaDigest, ReturnType returnType, List keys, Object... values) {
        return evalShaAsync(null, mode, codec, shaDigest, returnType, keys, values);
    }

    public  RFuture evalShaAsync(String key, Mode mode, Codec codec, String shaDigest, ReturnType returnType, List keys, Object... values) {
        RedissonScript script = new RedissonScript(commandExecutor, codec);
        return script.evalShaAsync(key, mode, shaDigest, returnType, keys, values);
    }

    @Override
    public void scriptKill() {
        commandExecutor.get(scriptKillAsync());
    }

    public void scriptKill(String key) {
        commandExecutor.get(scriptKillAsync(key));
    }

    @Override
    public RFuture scriptKillAsync() {
        return commandExecutor.writeAllVoidAsync(RedisCommands.SCRIPT_KILL);
    }

    public RFuture scriptKillAsync(String key) {
        return commandExecutor.writeAsync(key, RedisCommands.SCRIPT_KILL);
    }

    @Override
    public List scriptExists(String... shaDigests) {
        return commandExecutor.get(scriptExistsAsync(shaDigests));
    }

    @Override
    public RFuture> scriptExistsAsync(String... shaDigests) {
        List>> futures = commandExecutor.executeAllAsync(RedisCommands.SCRIPT_EXISTS, (Object[]) shaDigests);
        CompletableFuture f = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        CompletableFuture> s = f.thenApply(r -> {
            List result = futures.get(0).getNow(new ArrayList<>());
            for (CompletableFuture> future : futures.subList(1, futures.size())) {
                List l = future.getNow(new ArrayList<>());
                for (int i = 0; i < l.size(); i++) {
                    result.set(i, result.get(i) | l.get(i));
                }
            }
            return result;
        });
        return new CompletableFutureWrapper<>(s);
    }

    public List scriptExists(String key, String... shaDigests) {
        return commandExecutor.get(scriptExistsAsync(key, shaDigests));
    }

    public RFuture> scriptExistsAsync(String key, String... shaDigests) {
        return commandExecutor.writeAsync(key, RedisCommands.SCRIPT_EXISTS, shaDigests);
    }

    @Override
    public void scriptFlush() {
        commandExecutor.get(scriptFlushAsync());
    }

    public void scriptFlush(String key) {
        commandExecutor.get(scriptFlushAsync(key));
    }

    @Override
    public RFuture scriptFlushAsync() {
        return commandExecutor.writeAllVoidAsync(RedisCommands.SCRIPT_FLUSH);
    }

    public RFuture scriptFlushAsync(String key) {
        return commandExecutor.writeAsync(key, RedisCommands.SCRIPT_FLUSH);
    }

    @Override
    public  RFuture evalShaAsync(Mode mode, String shaDigest, ReturnType returnType) {
        return evalShaAsync(null, mode, codec, shaDigest, returnType, Collections.emptyList());
    }

    @Override
    public  RFuture evalAsync(Mode mode, String luaScript, ReturnType returnType) {
        return evalAsync(null, mode, luaScript, returnType, Collections.emptyList());
    }

    private List encode(Collection values, Codec codec) {
        List result = new ArrayList(values.size());
        for (Object object : values) {
            result.add(commandExecutor.encode(codec, object));
        }
        return result;
    }
    
    @Override
    public  RFuture evalShaAsync(String key, Mode mode, String shaDigest, ReturnType returnType,
            List keys, Object... values) {
        RedisCommand command = new RedisCommand(returnType.getCommand(), "EVALSHA");
        String mappedKey = commandExecutor.getServiceManager().getConfig().getNameMapper().map(key);
        List mappedKeys = keys.stream()
                                        .map(k -> {
                                            if (k instanceof String) {
                                                return commandExecutor.getServiceManager().getConfig().getNameMapper().map(k.toString());
                                            }
                                            return k;
                                        })
                                        .collect(Collectors.toList());
        if (mode == Mode.READ_ONLY && commandExecutor.isEvalShaROSupported()) {
            RedisCommand cmd = new RedisCommand(returnType.getCommand(), "EVALSHA_RO");
            RFuture f = commandExecutor.evalReadAsync(mappedKey, codec, cmd, shaDigest, mappedKeys, encode(Arrays.asList(values), codec).toArray());
            CompletableFuture result = new CompletableFuture<>();
            f.whenComplete((r, e) -> {
                if (e != null && e.getMessage().startsWith("ERR unknown command")) {
                    commandExecutor.setEvalShaROSupported(false);
                    RFuture s = evalShaAsync(mappedKey, mode, shaDigest, returnType, mappedKeys, values);
                    commandExecutor.transfer(s.toCompletableFuture(), result);
                    return;
                }
                commandExecutor.transfer(f.toCompletableFuture(), result);
            });
            return new CompletableFutureWrapper<>(result);
        }
        return commandExecutor.evalWriteAsync(mappedKey, codec, command, shaDigest, mappedKeys, encode(Arrays.asList(values), codec).toArray());
    }

    @Override
    public  RFuture evalAsync(String key, Mode mode, String luaScript, ReturnType returnType, List keys,
            Object... values) {
        String mappedKey = commandExecutor.getServiceManager().getConfig().getNameMapper().map(key);
        List mappedKeys = keys.stream()
                                        .map(k -> {
                                            if (k instanceof String) {
                                                return commandExecutor.getServiceManager().getConfig().getNameMapper().map(k.toString());
                                            }
                                            return k;
                                        })
                                        .collect(Collectors.toList());
        if (mode == Mode.READ_ONLY) {
            return commandExecutor.evalReadAsync(mappedKey, codec, returnType.getCommand(), luaScript, mappedKeys, encode(Arrays.asList(values), codec).toArray());
        }
        return commandExecutor.evalWriteAsync(mappedKey, codec, returnType.getCommand(), luaScript, mappedKeys, encode(Arrays.asList(values), codec).toArray());
    }

    @Override
    public  R evalSha(String key, Mode mode, String shaDigest, ReturnType returnType, List keys,
            Object... values) {
        return commandExecutor.get(evalShaAsync(key, mode, shaDigest, returnType, keys, values));
    }

    @Override
    public  R eval(String key, Mode mode, String luaScript, ReturnType returnType, List keys,
            Object... values) {
        return commandExecutor.get(evalAsync(key, mode, luaScript, returnType, keys, values));
    }

    @Override
    public  R eval(Mode mode, String luaScript, ReturnType returnType, Function, R> resultMapper, Object... values) {
        return commandExecutor.get(evalAsync(mode, luaScript, returnType, resultMapper, values));
    }

    @Override
    public  RFuture evalAsync(Mode mode, String luaScript, ReturnType returnType, Function, R> resultMapper, Object... values) {
        List args = new ArrayList<>();
        args.add(luaScript);
        args.add(0);
        for (Object object : values) {
            args.add(commandExecutor.encode(codec, object));
        }

        List> futures;
        if (mode == Mode.READ_ONLY) {
            futures = commandExecutor.readAllAsync(codec, returnType.getCommand(), args.toArray());
        } else {
            futures = commandExecutor.writeAllAsync(codec, returnType.getCommand(), args.toArray());
        }

        CompletableFuture r = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        CompletableFuture res = r.thenApply(v -> {
            List l = futures.stream().map(f -> f.join()).collect(Collectors.toList());
            return resultMapper.apply(l);
        });
        return new CompletableFutureWrapper<>(res);
    }

    @Override
    public  R evalSha(Mode mode, String shaDigest, ReturnType returnType, Function, R> resultMapper, Object... values) {
        return commandExecutor.get(evalShaAsync(mode, shaDigest, returnType, resultMapper, values));
    }

    @Override
    public  RFuture evalShaAsync(Mode mode, String shaDigest, ReturnType returnType, Function, R> resultMapper, Object... values) {
        List args = new ArrayList<>();
        args.add(shaDigest);
        args.add(0);
        for (Object object : values) {
            args.add(commandExecutor.encode(codec, object));
        }

        if (mode == Mode.READ_ONLY && commandExecutor.isEvalShaROSupported()) {
            RedisCommand cmd = new RedisCommand(returnType.getCommand(), "EVALSHA_RO");
            List> futures = commandExecutor.readAllAsync(codec, cmd, args.toArray());

            CompletableFuture r = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
            CompletableFuture rr = r.handle((res, e) -> {
                if (e != null) {
                    if (e.getMessage().startsWith("ERR unknown command")) {
                        commandExecutor.setEvalShaROSupported(false);
                        return evalShaAsync(mode, shaDigest, returnType, resultMapper, values);
                    }

                    CompletableFuture ex = new CompletableFuture<>();
                    ex.completeExceptionally(e);
                    return ex;
                }

                List l = futures.stream().map(f -> f.join()).collect(Collectors.toList());
                R result = resultMapper.apply(l);
                return CompletableFuture.completedFuture(result);
            }).thenCompose(ff -> ff);
            return new CompletableFutureWrapper<>(rr);
        }

        List> futures = commandExecutor.readAllAsync(codec, returnType.getCommand(), args.toArray());
        CompletableFuture r = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        CompletableFuture res = r.thenApply(v -> {
            List l = futures.stream().map(f -> f.join()).collect(Collectors.toList());
            return resultMapper.apply(l);
        });
        return new CompletableFutureWrapper<>(res);
    }
}