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

org.redisson.transaction.BaseTransactionalSet Maven / Gradle / Ivy

There is a newer version: 0.40.13
Show newest version
/**
 * Copyright 2018 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.transaction;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.redisson.RedissonMultiLock;
import org.redisson.RedissonObject;
import org.redisson.RedissonSet;
import org.redisson.api.RCollectionAsync;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RObject;
import org.redisson.api.RSet;
import org.redisson.api.SortOrder;
import org.redisson.client.RedisClient;
import org.redisson.client.protocol.decoder.ListScanResult;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.misc.Hash;
import org.redisson.misc.HashValue;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.transaction.operation.DeleteOperation;
import org.redisson.transaction.operation.TouchOperation;
import org.redisson.transaction.operation.TransactionalOperation;
import org.redisson.transaction.operation.UnlinkOperation;
import org.redisson.transaction.operation.set.MoveOperation;

import io.netty.buffer.ByteBuf;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;

/**
 * 
 * @author Nikita Koksharov
 *
 * @param  value type
 */
public abstract class BaseTransactionalSet extends BaseTransactionalObject {

    static final Object NULL = new Object();
    
    private final long timeout;
    final Map state = new HashMap();
    final List operations;
    final RCollectionAsync set;
    final RObject object;
    final String name;
    final CommandAsyncExecutor commandExecutor;
    Boolean deleted;
    
    public BaseTransactionalSet(CommandAsyncExecutor commandExecutor, long timeout, List operations, RCollectionAsync set) {
        this.commandExecutor = commandExecutor;
        this.timeout = timeout;
        this.operations = operations;
        this.set = set;
        this.object = (RObject) set;
        this.name = object.getName();
    }

    private HashValue toHash(Object value) {
        ByteBuf state = ((RedissonObject)set).encode(value);
        try {
            return new HashValue(Hash.hash128(state));
        } finally {
            state.release();
        }
    }
    
    public RFuture isExistsAsync() {
        if (deleted != null) {
            return RedissonPromise.newSucceededFuture(!deleted);
        }
        
        return set.isExistsAsync();
    }
    
    public RFuture unlinkAsync(CommandAsyncExecutor commandExecutor) {
        return deleteAsync(commandExecutor, new UnlinkOperation(name));
    }
    
    public RFuture touchAsync(CommandAsyncExecutor commandExecutor) {
        final RPromise result = new RedissonPromise();
        if (deleted != null && deleted) {
            operations.add(new TouchOperation(name));
            result.trySuccess(false);
            return result;
        }
        
        set.isExistsAsync().addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (!future.isSuccess()) {
                    result.tryFailure(future.cause());
                    return;
                }
                
                operations.add(new TouchOperation(name));
                boolean exists = future.getNow();
                if (!exists) {
                    for (Object value : state.values()) {
                        if (value != NULL) {
                            exists = true;
                            break;
                        }
                    }
                }
                result.trySuccess(exists);
            }
        });
                
        return result;
    }

    public RFuture deleteAsync(CommandAsyncExecutor commandExecutor) {
        return deleteAsync(commandExecutor, new DeleteOperation(name));
    }

    protected RFuture deleteAsync(CommandAsyncExecutor commandExecutor, final TransactionalOperation operation) {
        final RPromise result = new RedissonPromise();
        if (deleted != null) {
            operations.add(operation);
            result.trySuccess(!deleted);
            deleted = true;
            return result;
        }
        
        set.isExistsAsync().addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (!future.isSuccess()) {
                    result.tryFailure(future.cause());
                    return;
                }
                
                operations.add(operation);
                for (HashValue key : state.keySet()) {
                    state.put(key, NULL);
                }
                deleted = true;
                result.trySuccess(future.getNow());
            }
        });
        return result;
    }
    
    public RFuture containsAsync(Object value) {
        for (Object val : state.values()) {
            if (val != NULL && isEqual(val, value)) {
                return RedissonPromise.newSucceededFuture(true);
            }
        }
        
        return set.containsAsync(value);
    }
    
    protected abstract ListScanResult scanIteratorSource(String name, RedisClient client,
            long startPos, String pattern, int count);
    
    protected ListScanResult scanIterator(String name, RedisClient client,
            long startPos, String pattern, int count) {
        ListScanResult res = scanIteratorSource(name, client, startPos, pattern, count);
        Map newstate = new HashMap(state);
        for (Iterator iterator = res.getValues().iterator(); iterator.hasNext();) {
            Object entry = iterator.next();
            Object value = newstate.remove(toHash(entry));
            if (value == NULL) {
                iterator.remove();
            }
        }
        
        if (startPos == 0) {
            for (Entry entry : newstate.entrySet()) {
                if (entry.getValue() == NULL) {
                    continue;
                }
                res.getValues().add(entry.getValue());
            }
        }
        
        return res;
    }
    
    protected abstract RFuture> readAllAsyncSource();
    
    public RFuture> readAllAsync() {
        final RPromise> result = new RedissonPromise>();
        RFuture> future = readAllAsyncSource();
        future.addListener(new FutureListener>() {

            @Override
            public void operationComplete(Future> future) throws Exception {
                if (!future.isSuccess()) {
                    result.tryFailure(future.cause());
                    return;
                }
                
                Set set = future.getNow();
                Map newstate = new HashMap(state);
                for (Iterator iterator = set.iterator(); iterator.hasNext();) {
                    V key = iterator.next();
                    Object value = newstate.remove(toHash(key));
                    if (value == NULL) {
                        iterator.remove();
                    }
                }
                
                for (Object value : newstate.values()) {
                    if (value == NULL) {
                        continue;
                    }
                    set.add((V) value);
                }
                
                result.trySuccess(set);
            }
        });

        return result;
    }
    
    public RFuture addAsync(V value) {
        final TransactionalOperation operation = createAddOperation(value);
        return addAsync(value, operation);
    }
    
    public RFuture addAsync(final V value, final TransactionalOperation operation) {
        final RPromise result = new RedissonPromise();
        executeLocked(result, value, new Runnable() {
            @Override
            public void run() {
                final HashValue keyHash = toHash(value);
                Object entry = state.get(keyHash);
                if (entry != null) {
                    operations.add(operation);
                    state.put(keyHash, value);
                    if (deleted != null) {
                        deleted = false;
                    }
                    
                    result.trySuccess(entry == NULL);
                    return;
                }
                
                set.containsAsync(value).addListener(new FutureListener() {
                    @Override
                    public void operationComplete(Future future) throws Exception {
                        if (!future.isSuccess()) {
                            result.tryFailure(future.cause());
                            return;
                        }
                        
                        operations.add(operation);
                        state.put(keyHash, value);
                        if (deleted != null) {
                            deleted = false;
                        }
                        result.trySuccess(!future.getNow());
                    }
                });
            }
        });
        return result;
    }

    protected abstract TransactionalOperation createAddOperation(V value);
    
    public RFuture removeRandomAsync() {
        throw new UnsupportedOperationException();
    }
    
    public RFuture> removeRandomAsync(int amount) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture moveAsync(final String destination, final V value) {
        RSet destinationSet = new RedissonSet(object.getCodec(), commandExecutor, destination, null);
        
        final RPromise result = new RedissonPromise();
        RLock destinationLock = getLock(destinationSet, value);
        RLock lock = getLock(set, value);
        final RedissonMultiLock multiLock = new RedissonMultiLock(destinationLock, lock);
        final long threadId = Thread.currentThread().getId();
        multiLock.lockAsync(timeout, TimeUnit.MILLISECONDS).addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (!future.isSuccess()) {
                    multiLock.unlockAsync(threadId);
                    result.tryFailure(future.cause());
                    return;
                }
                
                final HashValue keyHash = toHash(value);
                Object currentValue = state.get(keyHash);
                if (currentValue != null) {
                    operations.add(createMoveOperation(destination, value, threadId));
                    if (currentValue == NULL) {
                        result.trySuccess(false);
                    } else {
                        state.put(keyHash, NULL);
                        result.trySuccess(true);
                    }
                    return;
                }
                
                set.containsAsync(value).addListener(new FutureListener() {
                    @Override
                    public void operationComplete(Future future) throws Exception {
                        if (!future.isSuccess()) {
                            result.tryFailure(future.cause());
                            return;
                        }
                        
                        operations.add(createMoveOperation(destination, value, threadId));
                        if (future.getNow()) {
                            state.put(keyHash, NULL);
                        }

                        result.trySuccess(future.getNow());
                    }
                });
            }

        });
        
        return result;
    }

    protected abstract MoveOperation createMoveOperation(String destination, V value, long threadId);

    protected abstract RLock getLock(RCollectionAsync set, V value);
    
    public RFuture removeAsync(final Object value) {
        final RPromise result = new RedissonPromise();
        executeLocked(result, (V)value, new Runnable() {
            @Override
            public void run() {
                final HashValue keyHash = toHash(value);
                Object currentValue = state.get(keyHash);
                if (currentValue != null) {
                    operations.add(createRemoveOperation(value));
                    if (currentValue == NULL) {
                        result.trySuccess(false);
                    } else {
                        state.put(keyHash, NULL);
                        result.trySuccess(true);
                    }
                    return;
                }

                set.containsAsync(value).addListener(new FutureListener() {
                    @Override
                    public void operationComplete(Future future) throws Exception {
                        if (!future.isSuccess()) {
                            result.tryFailure(future.cause());
                            return;
                        }
                        
                        operations.add(createRemoveOperation(value));
                        if (future.getNow()) {
                            state.put(keyHash, NULL);
                        }

                        result.trySuccess(future.getNow());
                    }
                });
            }

        });
        return result;
        
    }

    protected abstract TransactionalOperation createRemoveOperation(Object value);
    
    public RFuture containsAllAsync(Collection c) {
        List coll = new ArrayList(c);
        for (Iterator iterator = coll.iterator(); iterator.hasNext();) {
            Object value = iterator.next();
            for (Object val : state.values()) {
                if (val != NULL && isEqual(val, value)) {
                    iterator.remove();
                    break;
                }
            }
        }
        
        return set.containsAllAsync(coll);
    }

    public RFuture addAllAsync(final Collection c) {
        final RPromise result = new RedissonPromise();
        executeLocked(result, new Runnable() {
            @Override
            public void run() {
                containsAllAsync(c).addListener(new FutureListener() {
                    @Override
                    public void operationComplete(Future future) throws Exception {
                        if (!future.isSuccess()) {
                            result.tryFailure(future.cause());
                            return;
                        }

                        for (V value : c) {
                            operations.add(createAddOperation(value));
                            HashValue keyHash = toHash(value);
                            state.put(keyHash, value);
                        }
                        
                        if (deleted != null) {
                            deleted = false;
                        }
                        
                        result.trySuccess(!future.getNow());
                    }
                });
            }
        }, c);
        return result;
    }
    
    public RFuture retainAllAsync(Collection c) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture removeAllAsync(final Collection c) {
        final RPromise result = new RedissonPromise();
        executeLocked(result, new Runnable() {
            @Override
            public void run() {
                containsAllAsync(c).addListener(new FutureListener() {
                    @Override
                    public void operationComplete(Future future) throws Exception {
                        if (!future.isSuccess()) {
                            result.tryFailure(future.cause());
                            return;
                        }

                        for (Object value : c) {
                            operations.add(createRemoveOperation(value));
                            HashValue keyHash = toHash(value);
                            state.put(keyHash, NULL);
                        }
                        
                        result.trySuccess(!future.getNow());
                    }
                });
            }
        }, c);
        return result;
    }
    
    public RFuture unionAsync(String... names) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture diffAsync(String... names) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture intersectionAsync(String... names) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture> readSortAsync(SortOrder order) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture> readSortAsync(SortOrder order, int offset, int count) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture> readSortAsync(String byPattern, SortOrder order) {
        throw new UnsupportedOperationException();
    }

    public  RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) {
        throw new UnsupportedOperationException();
    }

    public RFuture> readSortAlphaAsync(SortOrder order) {
        throw new UnsupportedOperationException();
    }

    public RFuture> readSortAlphaAsync(SortOrder order, int offset, int count) {
        throw new UnsupportedOperationException();
    }

    public RFuture>  readSortAlphaAsync(String byPattern, SortOrder order) {
        throw new UnsupportedOperationException();
    }

    public RFuture>  readSortAlphaAsync(String byPattern, SortOrder order, int offset, int count) {
        throw new UnsupportedOperationException();
    }

    public  RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order) {
        throw new UnsupportedOperationException();
    }

    public  RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) {
        throw new UnsupportedOperationException();
    }

    public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) {
        throw new UnsupportedOperationException();
    }

    public RFuture> readUnionAsync(String... names) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture> readDiffAsync(String... names) {
        throw new UnsupportedOperationException();
    }
    
    public RFuture> readIntersectionAsync(String... names) {
        throw new UnsupportedOperationException();
    }
    
    private boolean isEqual(Object value, Object oldValue) {
        ByteBuf valueBuf = ((RedissonObject)set).encode(value);
        ByteBuf oldValueBuf = ((RedissonObject)set).encode(oldValue);
        
        try {
            return valueBuf.equals(oldValueBuf);
        } finally {
            valueBuf.readableBytes();
            oldValueBuf.readableBytes();
        }
    }
    
    protected  void executeLocked(final RPromise promise, Object value, final Runnable runnable) {
        RLock lock = getLock(set, (V) value);
        executeLocked(promise, runnable, lock);
    }

    protected  void executeLocked(final RPromise promise, final Runnable runnable, RLock lock) {
        lock.lockAsync(timeout, TimeUnit.MILLISECONDS).addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (future.isSuccess()) {
                    runnable.run();
                } else {
                    promise.tryFailure(future.cause());
                }
            }
        });
    }
    
    protected  void executeLocked(final RPromise promise, final Runnable runnable, Collection values) {
        List locks = new ArrayList(values.size());
        for (Object value : values) {
            RLock lock = getLock(set, (V) value);
            locks.add(lock);
        }
        final RedissonMultiLock multiLock = new RedissonMultiLock(locks.toArray(new RLock[locks.size()]));
        final long threadId = Thread.currentThread().getId();
        multiLock.lockAsync(timeout, TimeUnit.MILLISECONDS).addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (future.isSuccess()) {
                    runnable.run();
                } else {
                    multiLock.unlockAsync(threadId);
                    promise.tryFailure(future.cause());
                }
            }
        });
    }
    
}