View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at:
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  
16  package io.netty.handler.codec.redis;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.Unpooled;
20  import io.netty.util.CharsetUtil;
21  import io.netty.util.collection.LongObjectHashMap;
22  import io.netty.util.collection.LongObjectMap;
23  import io.netty.util.internal.UnstableApi;
24  
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  /**
29   * A default fixed redis message pool.
30   */
31  @UnstableApi
32  public final class FixedRedisMessagePool implements RedisMessagePool {
33  
34      private static final String[] DEFAULT_SIMPLE_STRINGS = {
35              "OK",
36              "PONG",
37              "QUEUED",
38      };
39  
40      private static final String[] DEFAULT_ERRORS = {
41              "ERR",
42              "ERR index out of range",
43              "ERR no such key",
44              "ERR source and destination objects are the same",
45              "ERR syntax error",
46              "BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.",
47              "BUSYKEY Target key name already exists.",
48              "EXECABORT Transaction discarded because of previous errors.",
49              "LOADING Redis is loading the dataset in memory",
50              "MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'.",
51              "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. " +
52              "Commands that may modify the data set are disabled. Please check Redis logs for details " +
53              "about the error.",
54              "NOAUTH Authentication required.",
55              "NOREPLICAS Not enough good slaves to write.",
56              "NOSCRIPT No matching script. Please use EVAL.",
57              "OOM command not allowed when used memory > 'maxmemory'.",
58              "READONLY You can't write against a read only slave.",
59              "WRONGTYPE Operation against a key holding the wrong kind of value",
60      };
61  
62      private static final long MIN_CACHED_INTEGER_NUMBER = RedisConstants.NULL_VALUE; // inclusive
63      private static final long MAX_CACHED_INTEGER_NUMBER = 128; // exclusive
64  
65      // cached integer size cannot larger than `int` range because of Collection.
66      private static final int SIZE_CACHED_INTEGER_NUMBER = (int) (MAX_CACHED_INTEGER_NUMBER - MIN_CACHED_INTEGER_NUMBER);
67  
68      /**
69       * A shared object for {@link FixedRedisMessagePool}.
70       */
71      public static final FixedRedisMessagePool INSTANCE = new FixedRedisMessagePool();
72  
73      // internal caches.
74      private final Map<ByteBuf, SimpleStringRedisMessage> byteBufToSimpleStrings;
75      private final Map<String, SimpleStringRedisMessage> stringToSimpleStrings;
76      private final Map<ByteBuf, ErrorRedisMessage> byteBufToErrors;
77      private final Map<String, ErrorRedisMessage> stringToErrors;
78      private final Map<ByteBuf, IntegerRedisMessage> byteBufToIntegers;
79      private final LongObjectMap<IntegerRedisMessage> longToIntegers;
80      private final LongObjectMap<byte[]> longToByteBufs;
81  
82      /**
83       * Creates a {@link FixedRedisMessagePool} instance.
84       */
85      private FixedRedisMessagePool() {
86          byteBufToSimpleStrings = new HashMap<ByteBuf, SimpleStringRedisMessage>(DEFAULT_SIMPLE_STRINGS.length, 1.0f);
87          stringToSimpleStrings = new HashMap<String, SimpleStringRedisMessage>(DEFAULT_SIMPLE_STRINGS.length, 1.0f);
88          for (String message : DEFAULT_SIMPLE_STRINGS) {
89              ByteBuf key = Unpooled.unmodifiableBuffer(
90                      Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(message.getBytes(CharsetUtil.UTF_8))));
91              SimpleStringRedisMessage cached = new SimpleStringRedisMessage(message);
92              byteBufToSimpleStrings.put(key, cached);
93              stringToSimpleStrings.put(message, cached);
94          }
95  
96          byteBufToErrors = new HashMap<ByteBuf, ErrorRedisMessage>(DEFAULT_ERRORS.length, 1.0f);
97          stringToErrors = new HashMap<String, ErrorRedisMessage>(DEFAULT_ERRORS.length, 1.0f);
98          for (String message : DEFAULT_ERRORS) {
99              ByteBuf key = Unpooled.unmodifiableBuffer(
100                     Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(message.getBytes(CharsetUtil.UTF_8))));
101             ErrorRedisMessage cached = new ErrorRedisMessage(message);
102             byteBufToErrors.put(key, cached);
103             stringToErrors.put(message, cached);
104         }
105 
106         byteBufToIntegers = new HashMap<ByteBuf, IntegerRedisMessage>(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
107         longToIntegers = new LongObjectHashMap<IntegerRedisMessage>(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
108         longToByteBufs = new LongObjectHashMap<byte[]>(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
109         for (long value = MIN_CACHED_INTEGER_NUMBER; value < MAX_CACHED_INTEGER_NUMBER; value++) {
110             byte[] keyBytes = RedisCodecUtil.longToAsciiBytes(value);
111             ByteBuf keyByteBuf = Unpooled.unmodifiableBuffer(Unpooled.unreleasableBuffer(
112                     Unpooled.wrappedBuffer(keyBytes)));
113             IntegerRedisMessage cached = new IntegerRedisMessage(value);
114             byteBufToIntegers.put(keyByteBuf, cached);
115             longToIntegers.put(value, cached);
116             longToByteBufs.put(value, keyBytes);
117         }
118     }
119 
120     @Override
121     public SimpleStringRedisMessage getSimpleString(String content) {
122         return stringToSimpleStrings.get(content);
123     }
124 
125     @Override
126     public SimpleStringRedisMessage getSimpleString(ByteBuf content) {
127         return byteBufToSimpleStrings.get(content);
128     }
129 
130     @Override
131     public ErrorRedisMessage getError(String content) {
132         return stringToErrors.get(content);
133     }
134 
135     @Override
136     public ErrorRedisMessage getError(ByteBuf content) {
137         return byteBufToErrors.get(content);
138     }
139 
140     @Override
141     public IntegerRedisMessage getInteger(long value) {
142         return longToIntegers.get(value);
143     }
144 
145     @Override
146     public IntegerRedisMessage getInteger(ByteBuf content) {
147         return byteBufToIntegers.get(content);
148     }
149 
150     @Override
151     public byte[] getByteBufOfInteger(long value) {
152         return longToByteBufs.get(value);
153     }
154 }