org.redisson.RedissonMap.java Source code

Java tutorial

Introduction

Here is the source code for org.redisson.RedissonMap.java

Source

/**
 * Copyright 2014 Nikita Koksharov, Nickolay Borbit
 *
 * 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 io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;

import java.math.BigDecimal;
import java.util.*;

import org.redisson.async.AsyncOperation;
import org.redisson.async.OperationListener;
import org.redisson.async.ResultOperation;
import org.redisson.async.SyncOperation;
import org.redisson.connection.ConnectionManager;
import org.redisson.core.Predicate;
import org.redisson.core.RMap;

import com.lambdaworks.redis.RedisAsyncConnection;
import com.lambdaworks.redis.RedisConnection;
import com.lambdaworks.redis.output.MapScanResult;

/**
 * Distributed and concurrent implementation of {@link java.util.concurrent.ConcurrentMap}
 * and {@link java.util.Map}
 *
 * @author Nikita Koksharov
 *
 * @param <K> key
 * @param <V> value
 */
//TODO implement watching by keys instead of map name
public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {

    protected RedissonMap(ConnectionManager connectionManager, String name) {
        super(connectionManager, name);
    }

    @Override
    public int size() {
        return connectionManager.read(getName(), new ResultOperation<Long, V>() {
            @Override
            public Future<Long> execute(RedisAsyncConnection<Object, V> async) {
                return async.hlen(getName());
            }
        }).intValue();
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public boolean containsKey(final Object key) {
        return connectionManager.read(getName(), new ResultOperation<Boolean, V>() {
            @Override
            public Future<Boolean> execute(RedisAsyncConnection<Object, V> async) {
                return async.hexists(getName(), key);
            }
        });
    }

    @Override
    public boolean containsValue(final Object value) {
        List<V> list = connectionManager.read(getName(), new ResultOperation<List<V>, V>() {
            @Override
            public Future<List<V>> execute(RedisAsyncConnection<Object, V> async) {
                return async.hvals(getName());
            }
        });
        return list.contains(value);
    }

    @Override
    public Map<K, V> getAll(final Set<K> keys) {
        if (keys.size() == 0) {
            return Collections.emptyMap();
        }
        final Object[] keysArray = keys.toArray();
        List<V> list = connectionManager.read(getName(), new ResultOperation<List<V>, V>() {
            @Override
            protected Future<List<V>> execute(RedisAsyncConnection<Object, V> async) {
                return async.hmget(getName(), keysArray);
            }
        });

        Map<K, V> result = new HashMap<K, V>(list.size());
        for (int index = 0; index < keysArray.length; index++) {
            V value = list.get(index);
            if (value == null) {
                continue;
            }
            result.put((K) keysArray[index], value);
        }
        return result;
    }

    @Override
    public V get(Object key) {
        return connectionManager.get(getAsync((K) key));
    }

    @Override
    public V put(K key, V value) {
        return connectionManager.get(putAsync(key, value));
    }

    @Override
    public V remove(Object key) {
        return connectionManager.get(removeAsync((K) key));
    }

    @Override
    public void putAll(final Map<? extends K, ? extends V> map) {
        connectionManager.write(getName(), new ResultOperation<String, Object>() {
            @Override
            public Future<String> execute(RedisAsyncConnection<Object, Object> async) {
                return async.hmset(getName(), (Map<Object, Object>) map);
            }
        });
    }

    @Override
    public void clear() {
        delete();
    }

    @Override
    public Set<K> keySet() {
        return (Set<K>) connectionManager.read(getName(), new ResultOperation<Set<Object>, V>() {
            @Override
            public Future<Set<Object>> execute(RedisAsyncConnection<Object, V> async) {
                return async.hkeys(getName());
            }
        });
    }

    @Override
    public Collection<V> values() {
        return connectionManager.read(getName(), new ResultOperation<List<V>, V>() {
            @Override
            public Future<List<V>> execute(RedisAsyncConnection<Object, V> async) {
                return async.hvals(getName());
            }
        });
    }

    @Override
    public Set<java.util.Map.Entry<K, V>> entrySet() {
        Map<Object, Object> map = connectionManager.read(getName(),
                new ResultOperation<Map<Object, Object>, Object>() {
                    @Override
                    public Future<Map<Object, Object>> execute(RedisAsyncConnection<Object, Object> async) {
                        return async.hgetall(getName());
                    }
                });

        Map<K, V> result = new HashMap<K, V>();
        for (java.util.Map.Entry<Object, Object> entry : map.entrySet()) {
            result.put((K) entry.getKey(), (V) entry.getValue());
        }
        return result.entrySet();
    }

    @Override
    public V putIfAbsent(final K key, final V value) {
        //        while (true) {
        //            Boolean res = connection.hsetnx(getName(), key, value);
        //            if (!res) {
        //                V result = (V) connection.hget(getName(), key);
        //                if (result != null) {
        //                    return result;
        //                }
        //            } else {
        //                return null;
        //            }
        //        }

        return connectionManager.write(getName(), new AsyncOperation<V, V>() {
            @Override
            public void execute(final Promise<V> promise, final RedisAsyncConnection<Object, V> async) {
                final AsyncOperation<V, V> timeoutCallback = this;
                async.hsetnx(getName(), key, value)
                        .addListener(new OperationListener<V, V, Boolean>(promise, async, timeoutCallback) {
                            @Override
                            public void onOperationComplete(Future<Boolean> future) throws Exception {
                                if (future.get()) {
                                    promise.setSuccess(null);
                                    return;
                                }

                                async.hget(getName(), key).addListener(
                                        new OperationListener<V, V, V>(promise, async, timeoutCallback) {
                                            @Override
                                            public void onOperationComplete(Future<V> future) throws Exception {
                                                V prev = future.get();
                                                if (prev != null) {
                                                    promise.setSuccess(prev);
                                                } else {
                                                    timeoutCallback.execute(promise, async);
                                                }
                                            }
                                        });
                            }
                        });
            }
        });
    }

    private boolean isEquals(RedisConnection<Object, Object> connection, Object key, Object value) {
        Object val = connection.hget(getName(), key);
        return (value != null && value.equals(val)) || (value == null && val == null);
    }

    @Override
    public boolean remove(final Object key, final Object value) {
        return connectionManager.write(getName(), new SyncOperation<Object, Boolean>() {
            @Override
            public Boolean execute(RedisConnection<Object, Object> connection) {
                while (true) {
                    connection.watch(getName());
                    if (connection.hexists(getName(), key) && isEquals(connection, key, value)) {
                        connection.multi();
                        connection.hdel(getName(), key);
                        if (connection.exec().size() == 1) {
                            return true;
                        }
                    } else {
                        connection.unwatch();
                        return false;
                    }
                }
            }
        });
    }

    @Override
    public boolean replace(final K key, final V oldValue, final V newValue) {
        return connectionManager.write(getName(), new SyncOperation<Object, Boolean>() {
            @Override
            public Boolean execute(RedisConnection<Object, Object> connection) {
                while (true) {
                    connection.watch(getName());
                    if (connection.hexists(getName(), key) && isEquals(connection, key, oldValue)) {
                        connection.multi();
                        connection.hset(getName(), key, newValue);
                        if (connection.exec().size() == 1) {
                            return true;
                        }
                    } else {
                        connection.unwatch();
                        return false;
                    }
                }
            }
        });
    }

    @Override
    public V replace(final K key, final V value) {
        return connectionManager.write(getName(), new SyncOperation<V, V>() {
            @Override
            public V execute(RedisConnection<Object, V> connection) {
                while (true) {
                    connection.watch(getName());
                    if (connection.hexists(getName(), key)) {
                        V prev = connection.hget(getName(), key);
                        connection.multi();
                        connection.hset(getName(), key, value);
                        if (connection.exec().size() == 1) {
                            return prev;
                        }
                    } else {
                        connection.unwatch();
                        return null;
                    }
                }
            }
        });
    }

    @Override
    public Future<V> getAsync(final K key) {
        return connectionManager.readAsync(getName(), new ResultOperation<V, V>() {
            @Override
            public Future<V> execute(RedisAsyncConnection<Object, V> async) {
                return async.hget(getName(), key);
            }
        });
    }

    @Override
    public Future<V> putAsync(final K key, final V value) {
        //        while (true) {
        //            connection.watch(getName());
        //            V prev = (V) connection.hget(getName(), key);
        //            connection.multi();
        //            connection.hset(getName(), key, value);
        //            if (connection.exec().size() == 1) {
        //                return prev;
        //            }
        //        }

        return connectionManager.writeAsync(getName(), new AsyncOperation<V, V>() {
            @Override
            public void execute(final Promise<V> promise, RedisAsyncConnection<Object, V> async) {
                putAsync(key, value, promise, async, this);
            }
        });
    }

    private void putAsync(final K key, final V value, final Promise<V> promise,
            final RedisAsyncConnection<Object, V> async, final AsyncOperation<V, V> timeoutCallback) {
        async.watch(getName()).addListener(new OperationListener<V, V, String>(promise, async, timeoutCallback) {
            @Override
            public void onOperationComplete(Future<String> future) throws Exception {

                async.hget(getName(), key)
                        .addListener(new OperationListener<V, V, V>(promise, async, timeoutCallback) {
                            @Override
                            public void onOperationComplete(Future<V> future) throws Exception {

                                final V prev = future.get();
                                async.multi().addListener(
                                        new OperationListener<V, V, String>(promise, async, timeoutCallback) {
                                            @Override
                                            public void onOperationComplete(Future<String> future)
                                                    throws Exception {

                                                async.hset(getName(), key, value)
                                                        .addListener(new OperationListener<V, V, Boolean>(promise,
                                                                async, timeoutCallback) {
                                                            @Override
                                                            public void onOperationComplete(Future<Boolean> future)
                                                                    throws Exception {

                                                                async.exec().addListener(
                                                                        new OperationListener<V, V, List<Object>>(
                                                                                promise, async, timeoutCallback) {
                                                                            @Override
                                                                            public void onOperationComplete(
                                                                                    Future<List<Object>> future)
                                                                                    throws Exception {

                                                                                if (future.get().size() == 1) {
                                                                                    promise.setSuccess(prev);
                                                                                } else {
                                                                                    timeoutCallback.execute(promise,
                                                                                            async);
                                                                                }
                                                                            }
                                                                        });
                                                            }
                                                        });
                                            }
                                        });
                            }
                        });
            }

        });
    }

    @Override
    public Future<V> removeAsync(final K key) {
        //        while (true) {
        //            connection.watch(getName());
        //            V prev = (V) connection.hget(getName(), key);
        //            connection.multi();
        //            connection.hdel(getName(), key);
        //            if (connection.exec().size() == 1) {
        //                return prev;
        //            }
        //        }

        return connectionManager.writeAsync(getName(), new AsyncOperation<V, V>() {
            @Override
            public void execute(final Promise<V> promise, RedisAsyncConnection<Object, V> async) {
                removeAsync(key, promise, async, this);
            }
        });
    }

    private void removeAsync(final K key, final Promise<V> promise, final RedisAsyncConnection<Object, V> async,
            final AsyncOperation<V, V> timeoutCallback) {
        async.watch(getName()).addListener(new OperationListener<V, V, String>(promise, async, timeoutCallback) {
            @Override
            public void onOperationComplete(Future<String> future) throws Exception {

                async.hget(getName(), key)
                        .addListener(new OperationListener<V, V, V>(promise, async, timeoutCallback) {
                            @Override
                            public void onOperationComplete(Future<V> future) throws Exception {
                                final V prev = future.get();

                                async.multi().addListener(
                                        new OperationListener<V, V, String>(promise, async, timeoutCallback) {
                                            @Override
                                            public void onOperationComplete(Future<String> future)
                                                    throws Exception {

                                                async.hdel(getName(), key)
                                                        .addListener(new OperationListener<V, V, Long>(promise,
                                                                async, timeoutCallback) {
                                                            @Override
                                                            public void onOperationComplete(Future<Long> future)
                                                                    throws Exception {

                                                                async.exec().addListener(
                                                                        new OperationListener<V, V, List<Object>>(
                                                                                promise, async, timeoutCallback) {
                                                                            @Override
                                                                            public void onOperationComplete(
                                                                                    Future<List<Object>> future)
                                                                                    throws Exception {

                                                                                if (future.get().size() == 1) {
                                                                                    promise.setSuccess(prev);
                                                                                } else {
                                                                                    timeoutCallback.execute(promise,
                                                                                            async);
                                                                                }
                                                                            }
                                                                        });

                                                            }
                                                        });
                                            }
                                        });
                            }
                        });
            }
        });
    }

    @Override
    public Future<Boolean> fastPutAsync(final K key, final V value) {
        return connectionManager.writeAsync(getName(), new ResultOperation<Boolean, V>() {
            @Override
            public Future<Boolean> execute(RedisAsyncConnection<Object, V> async) {
                return async.hset(getName(), key, value);
            }
        });
    }

    @Override
    public boolean fastPut(K key, V value) {
        return connectionManager.get(fastPutAsync(key, value));
    }

    @Override
    public Future<Long> fastRemoveAsync(final K... keys) {
        if (keys != null && keys.length > 0) {
            return connectionManager.writeAsync(getName(), new ResultOperation<Long, V>() {
                @Override
                public Future<Long> execute(RedisAsyncConnection<Object, V> async) {
                    return async.hdel(getName(), keys);
                }
            });
        } else {
            return connectionManager.getGroup().next().newSucceededFuture(0L);
        }
    }

    @Override
    public long fastRemove(K... keys) {
        return connectionManager.get(fastRemoveAsync(keys));
    }

    private MapScanResult<Object, V> scanIterator(final long startPos) {
        return connectionManager.read(getName(), new ResultOperation<MapScanResult<Object, V>, V>() {
            @Override
            public Future<MapScanResult<Object, V>> execute(RedisAsyncConnection<Object, V> async) {
                return async.hscan(getName(), startPos);
            }
        });
    }

    private Iterator<Map.Entry<K, V>> iterator() {
        return new Iterator<Map.Entry<K, V>>() {

            private Iterator<Map.Entry<K, V>> iter;
            private long iterPos = 0;

            private boolean removeExecuted;
            private Map.Entry<K, V> value;

            @Override
            public boolean hasNext() {
                if (iter == null || (!iter.hasNext() && iterPos != 0)) {
                    MapScanResult<Object, V> res = scanIterator(iterPos);
                    iter = ((Map<K, V>) res.getMap()).entrySet().iterator();
                    iterPos = res.getPos();
                }
                return iter.hasNext();
            }

            @Override
            public Map.Entry<K, V> next() {
                if (!hasNext()) {
                    throw new NoSuchElementException("No such element at index");
                }

                value = iter.next();
                removeExecuted = false;
                return value;
            }

            @Override
            public void remove() {
                if (removeExecuted) {
                    throw new IllegalStateException("Element been already deleted");
                }

                // lazy init iterator
                hasNext();
                iter.remove();
                RedissonMap.this.fastRemove(value.getKey());
                removeExecuted = true;
            }

        };
    }

    @Override
    public Map<K, V> filterKeys(Predicate<K> predicate) {
        Map<K, V> result = new HashMap<K, V>();
        for (Iterator<Map.Entry<K, V>> iterator = iterator(); iterator.hasNext();) {
            Map.Entry<K, V> entry = iterator.next();
            if (predicate.apply(entry.getKey())) {
                result.put(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    @Override
    public Map<K, V> filterValues(Predicate<V> predicate) {
        Map<K, V> result = new HashMap<K, V>();
        for (Iterator<Map.Entry<K, V>> iterator = iterator(); iterator.hasNext();) {
            Map.Entry<K, V> entry = iterator.next();
            if (predicate.apply(entry.getValue())) {
                result.put(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    public Map<K, V> filterEntries(Predicate<Map.Entry<K, V>> predicate) {
        Map<K, V> result = new HashMap<K, V>();
        for (Iterator<Map.Entry<K, V>> iterator = iterator(); iterator.hasNext();) {
            Map.Entry<K, V> entry = iterator.next();
            if (predicate.apply(entry)) {
                result.put(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    @Override
    public V addAndGet(final K key, final V value) {
        String res = connectionManager.write(getName(), new ResultOperation<String, V>() {

            @Override
            protected Future<String> execute(RedisAsyncConnection<Object, V> async) {
                Number val = (Number) value;
                return async.hincrbyfloat(getName(), key, new BigDecimal(val.toString()).toPlainString());
            }
        });

        if (value instanceof Long) {
            Object obj = Long.parseLong(res);
            return (V) obj;
        }
        if (value instanceof Integer) {
            Object obj = Integer.parseInt(res);
            return (V) obj;
        }
        if (value instanceof Float) {
            Object obj = Float.parseFloat(res);
            return (V) obj;
        }
        if (value instanceof Double) {
            Object obj = Double.parseDouble(res);
            return (V) obj;
        }
        if (value instanceof BigDecimal) {
            Object obj = new BigDecimal(res);
            return (V) obj;
        }
        throw new IllegalStateException("Wrong value type!");
    }

}