io.codis.nedis.util.NedisUtils.java Source code

Java tutorial

Introduction

Here is the source code for io.codis.nedis.util.NedisUtils.java

Source

/**
 * Copyright (c) 2015 CodisLabs.
 *
 * 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 io.codis.nedis.util;

import io.codis.nedis.AsyncCloseable;
import io.codis.nedis.ConnectionManagement;
import io.codis.nedis.NedisClient;
import io.codis.nedis.NedisClientPool;
import io.codis.nedis.protocol.BlockingListsCommands;
import io.codis.nedis.protocol.TransactionsCommands;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.naming.OperationNotSupportedException;

import org.apache.commons.lang3.tuple.Pair;

/**
 * @author Apache9
 */
public class NedisUtils {

    public static final int DEFAULT_REDIS_PORT = 6379;

    public static byte[] toBytes(double value) {
        return toBytes(Double.toString(value));
    }

    public static byte[] toBytes(int value) {
        return toBytes(Integer.toString(value));
    }

    public static byte[] toBytes(long value) {
        return toBytes(Long.toString(value));
    }

    private static final byte[] TRUE = new byte[] { '1' };

    private static final byte[] FALSE = new byte[] { '0' };

    public static byte[] toBytes(boolean value) {
        return value ? TRUE : FALSE;
    }

    public static byte[] toBytes(String value) {
        return value.getBytes(StandardCharsets.UTF_8);
    }

    public static byte[] toBytesExclusive(double value) {
        return toBytes("(" + Double.toString(value));
    }

    public static String bytesToString(byte[] value) {
        return new String(value, StandardCharsets.UTF_8);
    }

    public static double bytesToDouble(byte[] value) {
        return Double.parseDouble(bytesToString(value));
    }

    public static byte[][] toParamsReverse(byte[][] tailParams, byte[]... headParams) {
        return toParams(headParams, tailParams);
    }

    public static byte[][] toParams(byte[][] headParams, byte[]... tailParams) {
        byte[][] params = Arrays.copyOf(headParams, headParams.length + tailParams.length);
        System.arraycopy(tailParams, 0, params, headParams.length, tailParams.length);
        return params;
    }

    public static EventExecutor getEventExecutor(Future<?> future) {
        Class<?> clazz = future.getClass();
        for (;;) {
            try {
                Method method = clazz.getDeclaredMethod("executor");
                method.setAccessible(true);
                return (EventExecutor) method.invoke(future);
            } catch (NoSuchMethodException e) {
            } catch (SecurityException | IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
            clazz = clazz.getSuperclass();
            if (clazz == null) {
                return null;
            }
        }
    }

    private static final class Invoker implements InvocationHandler {

        private final NedisClientPool pool;

        public Invoker(NedisClientPool pool) {
            this.pool = pool;
        }

        private void resetTimeout(final NedisClient client, long previousTimeoutMs) {
            client.setTimeout(previousTimeoutMs).addListener(new FutureListener<Long>() {

                @Override
                public void operationComplete(Future<Long> future) throws Exception {
                    if (future.getNow() != null) {
                        client.release();
                    }
                }

            });
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        private void callBlocking(final NedisClient client, Method method, Object[] args,
                final long previousTimeoutMs, final Promise promise)
                throws IllegalAccessException, InvocationTargetException {
            ((Future) method.invoke(client, args)).addListener(new FutureListener() {

                @Override
                public void operationComplete(Future future) throws Exception {
                    if (future.isSuccess()) {
                        promise.trySuccess(future.getNow());
                    } else {
                        promise.tryFailure(future.cause());
                    }
                    resetTimeout(client, previousTimeoutMs);
                }
            });
        }

        private void setInfiniteTimeout(final NedisClient client, final Method method, final Object[] args,
                @SuppressWarnings("rawtypes") final Promise promise) {
            client.setTimeout(0L).addListener(new FutureListener<Long>() {

                @Override
                public void operationComplete(Future<Long> future) throws Exception {
                    // will not fail, but could be null
                    Long previousTimeoutMs = future.get();
                    if (previousTimeoutMs == null) {
                        promise.tryFailure(new IllegalStateException("already closed"));
                    } else {
                        callBlocking(client, method, args, previousTimeoutMs.longValue(), promise);
                    }
                }
            });
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        private void call(final NedisClient client, Method method, Object[] args, final Promise promise)
                throws IllegalAccessException, InvocationTargetException {
            ((Future) method.invoke(client, args)).addListener(new FutureListener() {

                @Override
                public void operationComplete(Future future) throws Exception {
                    if (future.isSuccess()) {
                        promise.trySuccess(future.getNow());
                    } else {
                        promise.tryFailure(future.cause());
                    }
                    client.release();
                }
            });
        }

        @Override
        public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
            // delegate Object methods like toString, hashCode, etc.
            if (method.getDeclaringClass().equals(Object.class)) {
                return method.invoke(this, args);
            }
            if (method.getDeclaringClass().equals(AsyncCloseable.class)) {
                return method.invoke(pool, args);
            }
            if (method.getDeclaringClass().equals(ConnectionManagement.class)) {
                throw new OperationNotSupportedException(
                        "Can not call connection related methods on pooled client");
            }
            if (method.getDeclaringClass().equals(TransactionsCommands.class)) {
                throw new OperationNotSupportedException(
                        "Can not call transaction related methods on pooled client");
            }
            Future<NedisClient> clientFuture = pool.acquire();
            @SuppressWarnings("rawtypes")
            final Promise promise = getEventExecutor(clientFuture).newPromise();
            clientFuture.addListener(new FutureListener<NedisClient>() {

                @Override
                public void operationComplete(Future<NedisClient> future) throws Exception {
                    if (future.isSuccess()) {
                        if (method.getDeclaringClass().equals(BlockingListsCommands.class)) {
                            setInfiniteTimeout(future.getNow(), method, args, promise);
                        } else {
                            call(future.getNow(), method, args, promise);
                        }
                    } else {
                        promise.tryFailure(future.cause());
                    }
                }
            });
            return promise;
        }

    }

    /**
     * Return a {@link NedisClient} which does acquire-execute-release automatically. You do not
     * need to call release when using the returned {@link NedisClient}.
     */
    public static NedisClient newPooledClient(NedisClientPool pool) {
        return (NedisClient) Proxy.newProxyInstance(NedisClient.class.getClassLoader(),
                new Class<?>[] { NedisClient.class }, new Invoker(pool));
    }

    public static final Comparator<byte[]> BYTES_COMPARATOR = new Comparator<byte[]>() {

        @Override
        public int compare(byte[] o1, byte[] o2) {
            for (int i = 0, j = 0; i < o1.length && j < o2.length; i++, j++) {
                int a = (o1[i] & 0xff);
                int b = (o2[j] & 0xff);
                if (a != b) {
                    return a - b;
                }
            }
            return o1.length - o2.length;
        }
    };

    public static <T> Map<byte[], T> newBytesKeyMap() {
        return new TreeMap<byte[], T>(BYTES_COMPARATOR);
    }

    public static Set<byte[]> newBytesSet() {
        return new TreeSet<byte[]>(BYTES_COMPARATOR);
    }

    private static Pair<EventLoopGroup, Class<? extends Channel>> DEFAULT_EVENT_LOOP_CONFIG;

    public static synchronized Pair<EventLoopGroup, Class<? extends Channel>> defaultEventLoopConfig() {
        if (DEFAULT_EVENT_LOOP_CONFIG == null) {
            DEFAULT_EVENT_LOOP_CONFIG = Pair.<EventLoopGroup, Class<? extends Channel>>of(new NioEventLoopGroup(),
                    NioSocketChannel.class);
        }
        return DEFAULT_EVENT_LOOP_CONFIG;
    }
}