From 548d65b314f13fabe2e498e31d8e899e7348fc07 Mon Sep 17 00:00:00 2001 From: rinoux Date: Fri, 9 Mar 2018 16:31:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A0JIRA=E4=BB=BB=E5=8A=A1=203rd=E5=8A=A0?= =?UTF-8?q?=E5=85=A5jedis=E7=9A=84=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.third_step6.gradle | 17 +- .../pool2/BaseKeyedPooledObjectFactory.java | 118 + .../org/apache/commons/pool2/BaseObject.java | 45 + .../apache/commons/pool2/BaseObjectPool.java | 125 + .../pool2/BasePooledObjectFactory.java | 104 + .../apache/commons/pool2/KeyedObjectPool.java | 230 + .../pool2/KeyedPooledObjectFactory.java | 148 + .../org/apache/commons/pool2/ObjectPool.java | 178 + .../org/apache/commons/pool2/PoolUtils.java | 1824 ++++++++ .../apache/commons/pool2/PooledObject.java | 218 + .../commons/pool2/PooledObjectFactory.java | 137 + .../commons/pool2/PooledObjectState.java | 86 + .../pool2/SwallowedExceptionListener.java | 36 + .../org/apache/commons/pool2/TrackedUse.java | 36 + .../apache/commons/pool2/UsageTracking.java | 39 + .../commons/pool2/impl/AbandonedConfig.java | 289 ++ .../pool2/impl/BaseGenericObjectPool.java | 1339 ++++++ .../pool2/impl/BaseObjectPoolConfig.java | 718 +++ .../apache/commons/pool2/impl/CallStack.java | 54 + .../commons/pool2/impl/CallStackUtils.java | 89 + .../pool2/impl/DefaultEvictionPolicy.java | 53 + .../pool2/impl/DefaultPooledObject.java | 297 ++ .../pool2/impl/DefaultPooledObjectInfo.java | 114 + .../impl/DefaultPooledObjectInfoMBean.java | 115 + .../commons/pool2/impl/EvictionConfig.java | 114 + .../commons/pool2/impl/EvictionPolicy.java | 45 + .../commons/pool2/impl/EvictionTimer.java | 129 + .../pool2/impl/GenericKeyedObjectPool.java | 1625 +++++++ .../impl/GenericKeyedObjectPoolConfig.java | 195 + .../impl/GenericKeyedObjectPoolMXBean.java | 205 + .../commons/pool2/impl/GenericObjectPool.java | 1203 +++++ .../pool2/impl/GenericObjectPoolConfig.java | 155 + .../pool2/impl/GenericObjectPoolMXBean.java | 219 + .../impl/InterruptibleReentrantLock.java | 57 + .../pool2/impl/LinkedBlockingDeque.java | 1425 ++++++ .../commons/pool2/impl/NoOpCallStack.java | 48 + .../commons/pool2/impl/PoolImplUtils.java | 126 + .../pool2/impl/PooledSoftReference.java | 98 + .../pool2/impl/SecurityManagerCallStack.java | 129 + .../pool2/impl/SoftReferenceObjectPool.java | 453 ++ .../pool2/impl/ThrowableCallStack.java | 86 + .../apache/commons/pool2/impl/package.html | 42 + .../org/apache/commons/pool2/overview.html | 50 + .../org/apache/commons/pool2/package.html | 63 + .../commons/pool2/proxy/BaseProxyHandler.java | 121 + .../pool2/proxy/CglibProxyHandler.java | 54 + .../commons/pool2/proxy/CglibProxySource.java | 82 + .../commons/pool2/proxy/JdkProxyHandler.java | 53 + .../commons/pool2/proxy/JdkProxySource.java | 85 + .../pool2/proxy/ProxiedKeyedObjectPool.java | 134 + .../pool2/proxy/ProxiedObjectPool.java | 126 + .../commons/pool2/proxy/ProxySource.java | 51 + .../apache/commons/pool2/proxy/package.html | 37 + .../redis/clients/jedis/BinaryClient.java | 1205 ++++++ .../redis/clients/jedis/BinaryJedis.java | 3850 +++++++++++++++++ .../clients/jedis/BinaryJedisCluster.java | 1977 +++++++++ .../clients/jedis/BinaryJedisPubSub.java | 127 + .../clients/jedis/BinaryShardedJedis.java | 925 ++++ .../fr/third/redis/clients/jedis/BitOP.java | 11 + .../redis/clients/jedis/BitPosParams.java | 27 + .../fr/third/redis/clients/jedis/Builder.java | 5 + .../redis/clients/jedis/BuilderFactory.java | 482 +++ .../fr/third/redis/clients/jedis/Client.java | 1123 +++++ .../redis/clients/jedis/ClusterReset.java | 5 + .../third/redis/clients/jedis/Connection.java | 328 ++ .../redis/clients/jedis/DebugParams.java | 31 + .../redis/clients/jedis/GeoCoordinate.java | 47 + .../clients/jedis/GeoRadiusResponse.java | 37 + .../fr/third/redis/clients/jedis/GeoUnit.java | 13 + .../redis/clients/jedis/HostAndPort.java | 153 + .../fr/third/redis/clients/jedis/Jedis.java | 3582 +++++++++++++++ .../redis/clients/jedis/JedisCluster.java | 1979 +++++++++ .../clients/jedis/JedisClusterCommand.java | 170 + .../jedis/JedisClusterConnectionHandler.java | 73 + .../clients/jedis/JedisClusterInfoCache.java | 268 ++ .../redis/clients/jedis/JedisFactory.java | 157 + .../redis/clients/jedis/JedisMonitor.java | 16 + .../third/redis/clients/jedis/JedisPool.java | 258 ++ .../clients/jedis/JedisPoolAbstract.java | 27 + .../redis/clients/jedis/JedisPoolConfig.java | 13 + .../redis/clients/jedis/JedisPubSub.java | 179 + .../clients/jedis/JedisSentinelPool.java | 348 ++ .../redis/clients/jedis/JedisShardInfo.java | 252 ++ .../JedisSlotBasedConnectionHandler.java | 82 + .../redis/clients/jedis/ListPosition.java | 12 + .../fr/third/redis/clients/jedis/Module.java | 41 + .../clients/jedis/MultiKeyPipelineBase.java | 692 +++ .../third/redis/clients/jedis/Pipeline.java | 160 + .../redis/clients/jedis/PipelineBase.java | 1692 ++++++++ .../third/redis/clients/jedis/Protocol.java | 281 ++ .../fr/third/redis/clients/jedis/Queable.java | 34 + .../third/redis/clients/jedis/Response.java | 77 + .../third/redis/clients/jedis/ScanParams.java | 78 + .../third/redis/clients/jedis/ScanResult.java | 48 + .../redis/clients/jedis/ShardedJedis.java | 934 ++++ .../clients/jedis/ShardedJedisPipeline.java | 77 + .../redis/clients/jedis/ShardedJedisPool.java | 120 + .../redis/clients/jedis/SortingParams.java | 159 + .../redis/clients/jedis/Transaction.java | 94 + .../fr/third/redis/clients/jedis/Tuple.java | 80 + .../fr/third/redis/clients/jedis/ZParams.java | 48 + .../commands/AdvancedBinaryJedisCommands.java | 24 + .../jedis/commands/AdvancedJedisCommands.java | 25 + .../clients/jedis/commands/BasicCommands.java | 142 + .../jedis/commands/BasicRedisPipeline.java | 51 + .../commands/BinaryJedisClusterCommands.java | 306 ++ .../jedis/commands/BinaryJedisCommands.java | 312 ++ .../jedis/commands/BinaryRedisPipeline.java | 279 ++ .../commands/BinaryScriptingCommands.java | 29 + .../BinaryScriptingCommandsPipeline.java | 22 + .../jedis/commands/ClusterCommands.java | 49 + .../jedis/commands/ClusterPipeline.java | 25 + .../clients/jedis/commands/Commands.java | 333 ++ .../JedisClusterBinaryScriptingCommands.java | 55 + .../jedis/commands/JedisClusterCommands.java | 301 ++ .../JedisClusterScriptingCommands.java | 60 + .../clients/jedis/commands/JedisCommands.java | 319 ++ .../jedis/commands/ModuleCommands.java | 11 + .../commands/MultiKeyBinaryCommands.java | 87 + .../MultiKeyBinaryJedisClusterCommands.java | 81 + .../commands/MultiKeyBinaryRedisPipeline.java | 81 + .../jedis/commands/MultiKeyCommands.java | 160 + .../commands/MultiKeyCommandsPipeline.java | 80 + .../MultiKeyJedisClusterCommands.java | 81 + .../jedis/commands/ProtocolCommand.java | 7 + .../clients/jedis/commands/RedisPipeline.java | 271 ++ .../jedis/commands/ScriptingCommands.java | 23 + .../commands/ScriptingCommandsPipeline.java | 19 + .../jedis/commands/SentinelCommands.java | 22 + .../jedis/exceptions/InvalidURIException.java | 19 + .../exceptions/JedisAskDataException.java | 20 + .../jedis/exceptions/JedisBusyException.java | 19 + .../exceptions/JedisClusterException.java | 17 + .../JedisClusterMaxAttemptsException.java | 17 + .../JedisClusterOperationException.java | 17 + .../exceptions/JedisConnectionException.java | 17 + .../jedis/exceptions/JedisDataException.java | 17 + .../jedis/exceptions/JedisException.java | 17 + .../JedisExhaustedPoolException.java | 20 + .../exceptions/JedisMovedDataException.java | 19 + .../JedisNoReachableClusterNodeException.java | 17 + .../exceptions/JedisNoScriptException.java | 11 + .../exceptions/JedisRedirectionException.java | 36 + .../clients/jedis/params/GeoRadiusParam.java | 79 + .../redis/clients/jedis/params/Params.java | 54 + .../redis/clients/jedis/params/SetParams.java | 84 + .../clients/jedis/params/ZAddParams.java | 69 + .../clients/jedis/params/ZIncrByParams.java | 72 + .../jedis/util/ByteArrayComparator.java | 24 + .../redis/clients/jedis/util/Hashing.java | 39 + .../redis/clients/jedis/util/IOUtils.java | 20 + .../clients/jedis/util/JedisByteHashMap.java | 147 + .../clients/jedis/util/JedisClusterCRC16.java | 89 + .../jedis/util/JedisClusterHashTagUtil.java | 36 + .../clients/jedis/util/JedisURIHelper.java | 57 + .../clients/jedis/util/KeyMergeUtil.java | 21 + .../redis/clients/jedis/util/MurmurHash.java | 148 + .../third/redis/clients/jedis/util/Pool.java | 191 + .../clients/jedis/util/RedisInputStream.java | 206 + .../clients/jedis/util/RedisOutputStream.java | 136 + .../redis/clients/jedis/util/SafeEncoder.java | 43 + .../redis/clients/jedis/util/ShardInfo.java | 20 + .../redis/clients/jedis/util/Sharded.java | 108 + .../redis/clients/jedis/util/Slowlog.java | 60 + 164 files changed, 41211 insertions(+), 6 deletions(-) create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObject.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/BasePooledObjectFactory.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedPooledObjectFactory.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/ObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/PoolUtils.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObject.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectFactory.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectState.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/SwallowedExceptionListener.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/TrackedUse.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/UsageTracking.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/AbandonedConfig.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseGenericObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStack.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStackUtils.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObject.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfo.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfoMBean.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionConfig.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionPolicy.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionTimer.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolConfig.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMXBean.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolConfig.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolMXBean.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/InterruptibleReentrantLock.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/LinkedBlockingDeque.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/NoOpCallStack.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PoolImplUtils.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PooledSoftReference.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SecurityManagerCallStack.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/ThrowableCallStack.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/package.html create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/overview.html create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/package.html create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/BaseProxyHandler.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxyHandler.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxySource.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxyHandler.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxySource.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedKeyedObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedObjectPool.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxySource.java create mode 100755 fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/package.html create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryClient.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedis.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisCluster.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisPubSub.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryShardedJedis.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BitOP.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BitPosParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Builder.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/BuilderFactory.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Client.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ClusterReset.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Connection.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/DebugParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/GeoCoordinate.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/GeoRadiusResponse.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/GeoUnit.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/HostAndPort.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Jedis.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisCluster.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterCommand.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterConnectionHandler.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterInfoCache.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisFactory.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisMonitor.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPool.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolAbstract.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolConfig.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPubSub.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSentinelPool.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisShardInfo.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSlotBasedConnectionHandler.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ListPosition.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Module.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/MultiKeyPipelineBase.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Pipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/PipelineBase.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Protocol.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Queable.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Response.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ScanParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ScanResult.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedis.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPool.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/SortingParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Transaction.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/Tuple.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/ZParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedBinaryJedisCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedJedisCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicRedisPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisClusterCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryRedisPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommandsPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/Commands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterBinaryScriptingCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterScriptingCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ModuleCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ProtocolCommand.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/RedisPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommandsPipeline.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/commands/SentinelCommands.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/InvalidURIException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisAskDataException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisBusyException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterMaxAttemptsException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterOperationException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisConnectionException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisDataException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisExhaustedPoolException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisMovedDataException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoReachableClusterNodeException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoScriptException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisRedirectionException.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/params/GeoRadiusParam.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/params/Params.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/params/SetParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZAddParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZIncrByParams.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/ByteArrayComparator.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/Hashing.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/IOUtils.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisByteHashMap.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterCRC16.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterHashTagUtil.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisURIHelper.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/KeyMergeUtil.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/MurmurHash.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/Pool.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisInputStream.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisOutputStream.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/SafeEncoder.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/ShardInfo.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/Sharded.java create mode 100755 fine-jedis/src/com/fr/third/redis/clients/jedis/util/Slowlog.java diff --git a/build.third_step6.gradle b/build.third_step6.gradle index d87976633..6fc167808 100644 --- a/build.third_step6.gradle +++ b/build.third_step6.gradle @@ -4,19 +4,19 @@ tasks.withType(JavaCompile){ options.encoding = 'UTF-8' destinationDir = file('build/classes/6') } -//Ö¸¶¨¹¹½¨µÄjdk°æ±¾ +//ָ��������jdk�汾 sourceCompatibility=1.5 def jarname="fine-third-10.0.jar" def classesDir='build/classes/6' def ftpreport='E:/ftp/share/report/' -//½âѹlibϵÄjarµ½classesÎļþ¼Ð +//��ѹlib�µ�jar��classes�ļ��� jar{ baseName="fine-third_6-10.0" } def srcDir="." -//ÉèÖÃÔ´Âë·¾¶ +//����Դ��·�� sourceSets{ main{ java{ @@ -30,6 +30,9 @@ sourceSets{ "${srcDir}/fine-lucene/resources", "${srcDir}/fine-cglib/src", "${srcDir}/fine-cglib/resources", + "${srcDir}/fine-jedis/src", + "${srcDir}/fine-jedis/resources", + ] } } @@ -41,12 +44,12 @@ repositories{ mavenCentral() } -//»ñȡʲô·ÖÖ§Ãû +//��ȡʲô��֧�� FileTree files =fileTree(dir:'./',include:'build.*.gradle') def buildDir=files[0].path.substring(0,files[0].path.lastIndexOf ('/')) def branchName=buildDir.substring(buildDir.lastIndexOf ('/')+1) -//Ö¸¶¨ÒÀÀµ +//ָ������ dependencies{ compile fileTree(dir:"${srcDir}/fine-jackson/lib",include:'**/*.jar') compile fileTree(dir:"${srcDir}/fine-ehcache/lib",include:'**/*.jar') @@ -56,7 +59,7 @@ dependencies{ testCompile 'junit:junit:4.12' } -//Ö¸Ã÷ÎÞ·¨±àÒëÎļþËùÔÚ·¾¶ +//ָ���޷������ļ�����·�� def dataContent ={def dir -> copySpec{ from ("${dir}"){ @@ -80,6 +83,8 @@ task copyFiles(type:Copy,dependsOn:'compileJava'){ with dataContent.call("${srcDir}/fine-lucene/resources") with dataContent.call("${srcDir}/fine-cglib/src") with dataContent.call("${srcDir}/fine-cglib/resources") + with dataContent.call("${srcDir}/fine-jedis/src") + with dataContent.call("${srcDir}/fine-jedis/resources") into "${classesDir}" } } diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java new file mode 100755 index 000000000..65b362ac4 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseKeyedPooledObjectFactory.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * A base implementation of KeyedPooledObjectFactory. + *

+ * All operations defined here are essentially no-op's. + *

+ * This class is immutable, and therefore thread-safe. + * + * @see KeyedPooledObjectFactory + * + * @param The type of keys managed by this factory. + * @param Type of element managed by this factory. + * + * @since 2.0 + */ +public abstract class BaseKeyedPooledObjectFactory extends BaseObject + implements KeyedPooledObjectFactory { + + /** + * Create an instance that can be served by the pool. + * + * @param key the key used when constructing the object + * @return an instance that can be served by the pool + * + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. + */ + public abstract V create(K key) + throws Exception; + + /** + * Wrap the provided instance with an implementation of + * {@link PooledObject}. + * + * @param value the instance to wrap + * + * @return The provided instance, wrapped by a {@link PooledObject} + */ + public abstract PooledObject wrap(V value); + + @Override + public PooledObject makeObject(final K key) throws Exception { + return wrap(create(key)); + } + + /** + * Destroy an instance no longer needed by the pool. + *

+ * The default implementation is a no-op. + * + * @param key the key used when selecting the instance + * @param p a {@code PooledObject} wrapping the instance to be destroyed + */ + @Override + public void destroyObject(final K key, final PooledObject p) + throws Exception { + // The default implementation is a no-op. + } + + /** + * Ensures that the instance is safe to be returned by the pool. + *

+ * The default implementation always returns {@code true}. + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be validated + * @return always true in the default implementation + */ + @Override + public boolean validateObject(final K key, final PooledObject p) { + return true; + } + + /** + * Reinitialize an instance to be returned by the pool. + *

+ * The default implementation is a no-op. + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be activated + */ + @Override + public void activateObject(final K key, final PooledObject p) + throws Exception { + // The default implementation is a no-op. + } + + /** + * Uninitialize an instance to be returned to the idle object pool. + *

+ * The default implementation is a no-op. + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be passivated + */ + @Override + public void passivateObject(final K key, final PooledObject p) + throws Exception { + // The default implementation is a no-op. + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObject.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObject.java new file mode 100755 index 000000000..7f45b43ba --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObject.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * A base class for common functionality. + * + * @since 2.4.3 + */ +public abstract class BaseObject { + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append(" ["); + toStringAppendFields(builder); + builder.append("]"); + return builder.toString(); + } + + /** + * Used by sub-classes to include the fields defined by the sub-class in the + * {@link #toString()} output. + * + * @param builder Field names and values are appended to this object + */ + protected void toStringAppendFields(final StringBuilder builder) { + // do nothing by default, needed for b/w compatibility. + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObjectPool.java new file mode 100755 index 000000000..f764f8799 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BaseObjectPool.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * A simple base implementation of {@link ObjectPool}. + * Optional operations are implemented to either do nothing, return a value + * indicating it is unsupported or throw {@link UnsupportedOperationException}. + *

+ * This class is intended to be thread-safe. + * + * @param Type of element pooled in this pool. + * + * @since 2.0 + */ +public abstract class BaseObjectPool extends BaseObject implements ObjectPool { + + @Override + public abstract T borrowObject() throws Exception; + + @Override + public abstract void returnObject(T obj) throws Exception; + + @Override + public abstract void invalidateObject(T obj) throws Exception; + + /** + * Not supported in this base implementation. + * + * @return a negative value. + */ + @Override + public int getNumIdle() { + return -1; + } + + /** + * Not supported in this base implementation. + * + * @return a negative value. + */ + @Override + public int getNumActive() { + return -1; + } + + /** + * Not supported in this base implementation. + * + * @throws UnsupportedOperationException if the pool does not implement this + * method + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Not supported in this base implementation. Subclasses should override + * this behavior. + * + * @throws UnsupportedOperationException if the pool does not implement this + * method + */ + @Override + public void addObject() throws Exception, UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + *

+ * This affects the behavior of isClosed and + * assertOpen. + */ + @Override + public void close() { + closed = true; + } + + /** + * Has this pool instance been closed. + * + * @return true when this pool has been closed. + */ + public final boolean isClosed() { + return closed; + } + + /** + * Throws an IllegalStateException when this pool has been + * closed. + * + * @throws IllegalStateException when this pool has been closed. + * + * @see #isClosed() + */ + protected final void assertOpen() throws IllegalStateException { + if (isClosed()) { + throw new IllegalStateException("Pool not open"); + } + } + + private volatile boolean closed = false; + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + builder.append("closed="); + builder.append(closed); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BasePooledObjectFactory.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BasePooledObjectFactory.java new file mode 100755 index 000000000..ac0dae5fe --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/BasePooledObjectFactory.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * A base implementation of PoolableObjectFactory. + *

+ * All operations defined here are essentially no-op's. + *

+ * This class is immutable, and therefore thread-safe + * + * @param Type of element managed in this factory. + * + * @see PooledObjectFactory + * @see BaseKeyedPooledObjectFactory + * + * @since 2.0 + */ +public abstract class BasePooledObjectFactory extends BaseObject implements PooledObjectFactory { + /** + * Creates an object instance, to be wrapped in a {@link PooledObject}. + *

This method must support concurrent, multi-threaded + * activation.

+ * + * @return an instance to be served by the pool + * + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. + */ + public abstract T create() throws Exception; + + /** + * Wrap the provided instance with an implementation of + * {@link PooledObject}. + * + * @param obj the instance to wrap + * + * @return The provided instance, wrapped by a {@link PooledObject} + */ + public abstract PooledObject wrap(T obj); + + @Override + public PooledObject makeObject() throws Exception { + return wrap(create()); + } + + /** + * No-op. + * + * @param p ignored + */ + @Override + public void destroyObject(final PooledObject p) + throws Exception { + // The default implementation is a no-op. + } + + /** + * This implementation always returns {@code true}. + * + * @param p ignored + * + * @return {@code true} + */ + @Override + public boolean validateObject(final PooledObject p) { + return true; + } + + /** + * No-op. + * + * @param p ignored + */ + @Override + public void activateObject(final PooledObject p) throws Exception { + // The default implementation is a no-op. + } + + /** + * No-op. + * + * @param p ignored + */ + @Override + public void passivateObject(final PooledObject p) + throws Exception { + // The default implementation is a no-op. + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedObjectPool.java new file mode 100755 index 000000000..d281d87bf --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedObjectPool.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +import java.io.Closeable; +import java.util.NoSuchElementException; + +/** + * A "keyed" pooling interface. + *

+ * A keyed pool maintains a pool of instances for each key value. + *

+ * Example of use: + *

 Object obj = null;
+ * Object key = "Key";
+ *
+ * try {
+ *     obj = pool.borrowObject(key);
+ *     //...use the object...
+ * } catch(Exception e) {
+ *     // invalidate the object
+ *     pool.invalidateObject(key, obj);
+ *     // do not return the object to the pool twice
+ *     obj = null;
+ * } finally {
+ *     // make sure the object is returned to the pool
+ *     if(null != obj) {
+ *         pool.returnObject(key, obj);
+ *     }
+ * }
+ *

+ * {@link KeyedObjectPool} implementations may choose to store at most + * one instance per key value, or may choose to maintain a pool of instances + * for each key (essentially creating a {@link java.util.Map Map} of + * {@link ObjectPool pools}). + *

+ * See {@link org.apache.commons.pool2.impl.GenericKeyedObjectPool + * GenericKeyedObjectPool} for an implementation. + * + * @param The type of keys maintained by this pool. + * @param Type of element pooled in this pool. + * + * @see KeyedPooledObjectFactory + * @see ObjectPool + * @see org.apache.commons.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool + * + * @since 2.0 + */ +public interface KeyedObjectPool extends Closeable { + /** + * Obtains an instance from this pool for the specified key. + *

+ * Instances returned from this method will have been either newly created + * with {@link KeyedPooledObjectFactory#makeObject makeObject} or will be + * a previously idle object and have been activated with + * {@link KeyedPooledObjectFactory#activateObject activateObject} and then + * (optionally) validated with + * {@link KeyedPooledObjectFactory#validateObject validateObject}. + *

+ * By contract, clients must return the borrowed object + * using {@link #returnObject returnObject}, + * {@link #invalidateObject invalidateObject}, or a related method as + * defined in an implementation or sub-interface, using a key + * that is {@link Object#equals equivalent} to the one used to borrow the + * instance in the first place. + *

+ * The behaviour of this method when the pool has been exhausted is not + * strictly specified (although it may be specified by implementations). + * + * @param key the key used to obtain the object + * + * @return an instance from this pool. + * + * @throws IllegalStateException + * after {@link #close close} has been called on this pool + * @throws Exception + * when {@link KeyedPooledObjectFactory#makeObject + * makeObject} throws an exception + * @throws NoSuchElementException + * when the pool is exhausted and cannot or will not return + * another instance + */ + V borrowObject(K key) throws Exception, NoSuchElementException, IllegalStateException; + + /** + * Return an instance to the pool. By contract, obj + * must have been obtained using + * {@link #borrowObject borrowObject} or a related method as defined in an + * implementation or sub-interface using a key that is + * equivalent to the one used to borrow the instance in the first place. + * + * @param key the key used to obtain the object + * @param obj a {@link #borrowObject borrowed} instance to be returned. + * + * @throws IllegalStateException + * if an attempt is made to return an object to the pool that + * is in any state other than allocated (i.e. borrowed). + * Attempting to return an object more than once or attempting + * to return an object that was never borrowed from the pool + * will trigger this exception. + * + * @throws Exception if an instance cannot be returned to the pool + */ + void returnObject(K key, V obj) throws Exception; + + /** + * Invalidates an object from the pool. + *

+ * By contract, obj must have been obtained + * using {@link #borrowObject borrowObject} or a related method as defined + * in an implementation or sub-interface using a key that is + * equivalent to the one used to borrow the Object in the first + * place. + *

+ * This method should be used when an object that has been borrowed is + * determined (due to an exception or other problem) to be invalid. + * + * @param key the key used to obtain the object + * @param obj a {@link #borrowObject borrowed} instance to be returned. + * + * @throws Exception if the instance cannot be invalidated + */ + void invalidateObject(K key, V obj) throws Exception; + + /** + * Create an object using the {@link KeyedPooledObjectFactory factory} or + * other implementation dependent mechanism, passivate it, and then place it + * in the idle object pool. addObject is useful for + * "pre-loading" a pool with idle objects (Optional operation). + * + * @param key the key a new instance should be added to + * + * @throws Exception + * when {@link KeyedPooledObjectFactory#makeObject} fails. + * @throws IllegalStateException + * after {@link #close} has been called on this pool. + * @throws UnsupportedOperationException + * when this pool cannot add new idle objects. + */ + void addObject(K key) throws Exception, IllegalStateException, + UnsupportedOperationException; + + /** + * Returns the number of instances corresponding to the given + * key currently idle in this pool. Returns a negative value if + * this information is not available. + * + * @param key the key to query + * @return the number of instances corresponding to the given + * key currently idle in this pool. + */ + int getNumIdle(K key); + + /** + * Returns the number of instances currently borrowed from but not yet + * returned to the pool corresponding to the given key. + * Returns a negative value if this information is not available. + * + * @param key the key to query + * @return the number of instances currently borrowed from but not yet + * returned to the pool corresponding to the given key. += */ + int getNumActive(K key); + + /** + * Returns the total number of instances currently idle in this pool. + * Returns a negative value if this information is not available. + * @return the total number of instances currently idle in this pool. + = */ + int getNumIdle(); + + /** + * Returns the total number of instances current borrowed from this pool but + * not yet returned. Returns a negative value if this information is not + * available. + * @return the total number of instances current borrowed from this pool but + * not yet returned. + */ + int getNumActive(); + + /** + * Clears the pool, removing all pooled instances (optional operation). + * + * @throws UnsupportedOperationException when this implementation doesn't + * support the operation + * + * @throws Exception if the pool cannot be cleared + */ + void clear() throws Exception, UnsupportedOperationException; + + /** + * Clears the specified pool, removing all pooled instances corresponding to + * the given key (optional operation). + * + * @param key the key to clear + * + * @throws UnsupportedOperationException when this implementation doesn't + * support the operation + * + * @throws Exception if the key cannot be cleared + */ + void clear(K key) throws Exception, UnsupportedOperationException; + + /** + * Close this pool, and free any resources associated with it. + *

+ * Calling {@link #addObject addObject} or + * {@link #borrowObject borrowObject} after invoking this method on a pool + * will cause them to throw an {@link IllegalStateException}. + *

+ * Implementations should silently fail if not all resources can be freed. + */ + @Override + void close(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedPooledObjectFactory.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedPooledObjectFactory.java new file mode 100755 index 000000000..688304881 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/KeyedPooledObjectFactory.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * An interface defining life-cycle methods for + * instances to be served by a {@link KeyedObjectPool}. + *

+ * By contract, when an {@link KeyedObjectPool} + * delegates to a {@link KeyedPooledObjectFactory}, + *

    + *
  1. + * {@link #makeObject} is called whenever a new instance is needed. + *
  2. + *
  3. + * {@link #activateObject} is invoked on every instance that has been + * {@link #passivateObject passivated} before it is + * {@link KeyedObjectPool#borrowObject borrowed} from the pool. + *
  4. + *
  5. + * {@link #validateObject} may be invoked on {@link #activateObject activated} + * instances to make sure they can be + * {@link KeyedObjectPool#borrowObject borrowed} from the pool. + * validateObject may also be used to test an + * instance being {@link KeyedObjectPool#returnObject returned} to the pool + * before it is {@link #passivateObject passivated}. It will only be invoked + * on an activated instance. + *
  6. + *
  7. + * {@link #passivateObject passivateObject} + * is invoked on every instance when it is returned to the pool. + *
  8. + *
  9. + * {@link #destroyObject destroyObject} + * is invoked on every instance when it is being "dropped" from the + * pool (whether due to the response from validateObject, + * or for reasons specific to the pool implementation.) There is no + * guarantee that the instance being destroyed will + * be considered active, passive or in a generally consistent state. + *
  10. + *
+ * {@link KeyedPooledObjectFactory} must be thread-safe. The only promise + * an {@link KeyedObjectPool} makes is that the same instance of an object will + * not be passed to more than one method of a + * KeyedPoolableObjectFactory at a time. + *

+ * While clients of a {@link KeyedObjectPool} borrow and return instances of + * the underlying value type V, the factory methods act on instances of + * {@link PooledObject PooledObject<V>}. These are the object wrappers that + * pools use to track and maintain state informations about the objects that + * they manage. + * + * @see KeyedObjectPool + * @see BaseKeyedPooledObjectFactory + * + * @param The type of keys managed by this factory. + * @param Type of element managed by this factory. + * + * @since 2.0 + */ +public interface KeyedPooledObjectFactory { + /** + * Create an instance that can be served by the pool and + * wrap it in a {@link PooledObject} to be managed by the pool. + * + * @param key the key used when constructing the object + * + * @return a {@code PooledObject} wrapping an instance that can + * be served by the pool. + * + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. + */ + PooledObject makeObject(K key) throws Exception; + + /** + * Destroy an instance no longer needed by the pool. + *

+ * It is important for implementations of this method to be aware that there + * is no guarantee about what state obj will be in and the + * implementation should be prepared to handle unexpected errors. + *

+ * Also, an implementation must take in to consideration that instances lost + * to the garbage collector may never be destroyed. + * + * @param key the key used when selecting the instance + * @param p a {@code PooledObject} wrapping the instance to be destroyed + * + * @throws Exception should be avoided as it may be swallowed by + * the pool implementation. + * + * @see #validateObject + * @see KeyedObjectPool#invalidateObject + */ + void destroyObject(K key, PooledObject p) throws Exception; + + /** + * Ensures that the instance is safe to be returned by the pool. + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be validated + * + * @return false if obj is not valid and should + * be dropped from the pool, true otherwise. + */ + boolean validateObject(K key, PooledObject p); + + /** + * Reinitialize an instance to be returned by the pool. + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be activated + * + * @throws Exception if there is a problem activating obj, + * this exception may be swallowed by the pool. + * + * @see #destroyObject + */ + void activateObject(K key, PooledObject p) throws Exception; + + /** + * Uninitialize an instance to be returned to the idle object pool. + * + * @param key the key used when selecting the object + * @param p a {@code PooledObject} wrapping the instance to be passivated + * + * @throws Exception if there is a problem passivating obj, + * this exception may be swallowed by the pool. + * + * @see #destroyObject + */ + void passivateObject(K key, PooledObject p) throws Exception; +} + diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/ObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/ObjectPool.java new file mode 100755 index 000000000..123bc0a88 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/ObjectPool.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +import java.io.Closeable; +import java.util.NoSuchElementException; + +/** + * A pooling simple interface. + *

+ * Example of use: + *

 Object obj = null;
+ *
+ * try {
+ *     obj = pool.borrowObject();
+ *     try {
+ *         //...use the object...
+ *     } catch(Exception e) {
+ *         // invalidate the object
+ *         pool.invalidateObject(obj);
+ *         // do not return the object to the pool twice
+ *         obj = null;
+ *     } finally {
+ *         // make sure the object is returned to the pool
+ *         if(null != obj) {
+ *             pool.returnObject(obj);
+ *        }
+ *     }
+ * } catch(Exception e) {
+ *       // failed to borrow an object
+ * }
+ *

+ * See {@link BaseObjectPool} for a simple base implementation. + * + * @param Type of element pooled in this pool. + * + * @see PooledObjectFactory + * @see KeyedObjectPool + * @see BaseObjectPool + * + * @since 2.0 + */ +public interface ObjectPool extends Closeable { + /** + * Obtains an instance from this pool. + *

+ * Instances returned from this method will have been either newly created + * with {@link PooledObjectFactory#makeObject} or will be a previously + * idle object and have been activated with + * {@link PooledObjectFactory#activateObject} and then validated with + * {@link PooledObjectFactory#validateObject}. + *

+ * By contract, clients must return the borrowed instance + * using {@link #returnObject}, {@link #invalidateObject}, or a related + * method as defined in an implementation or sub-interface. + *

+ * The behaviour of this method when the pool has been exhausted + * is not strictly specified (although it may be specified by + * implementations). + * + * @return an instance from this pool. + * + * @throws IllegalStateException + * after {@link #close close} has been called on this pool. + * @throws Exception + * when {@link PooledObjectFactory#makeObject} throws an + * exception. + * @throws NoSuchElementException + * when the pool is exhausted and cannot or will not return + * another instance. + */ + T borrowObject() throws Exception, NoSuchElementException, + IllegalStateException; + + /** + * Return an instance to the pool. By contract, obj + * must have been obtained using {@link #borrowObject()} or + * a related method as defined in an implementation or sub-interface. + * + * @param obj a {@link #borrowObject borrowed} instance to be returned. + * + * @throws IllegalStateException + * if an attempt is made to return an object to the pool that + * is in any state other than allocated (i.e. borrowed). + * Attempting to return an object more than once or attempting + * to return an object that was never borrowed from the pool + * will trigger this exception. + * + * @throws Exception if an instance cannot be returned to the pool + */ + void returnObject(T obj) throws Exception; + + /** + * Invalidates an object from the pool. + *

+ * By contract, obj must have been obtained + * using {@link #borrowObject} or a related method as defined in an + * implementation or sub-interface. + *

+ * This method should be used when an object that has been borrowed is + * determined (due to an exception or other problem) to be invalid. + * + * @param obj a {@link #borrowObject borrowed} instance to be disposed. + * + * @throws Exception if the instance cannot be invalidated + */ + void invalidateObject(T obj) throws Exception; + + /** + * Create an object using the {@link PooledObjectFactory factory} or other + * implementation dependent mechanism, passivate it, and then place it in + * the idle object pool. addObject is useful for "pre-loading" + * a pool with idle objects. (Optional operation). + * + * @throws Exception + * when {@link PooledObjectFactory#makeObject} fails. + * @throws IllegalStateException + * after {@link #close} has been called on this pool. + * @throws UnsupportedOperationException + * when this pool cannot add new idle objects. + */ + void addObject() throws Exception, IllegalStateException, + UnsupportedOperationException; + + /** + * Return the number of instances currently idle in this pool. This may be + * considered an approximation of the number of objects that can be + * {@link #borrowObject borrowed} without creating any new instances. + * Returns a negative value if this information is not available. + * @return the number of instances currently idle in this pool. + */ + int getNumIdle(); + + /** + * Return the number of instances currently borrowed from this pool. Returns + * a negative value if this information is not available. + * @return the number of instances currently borrowed from this pool. + */ + int getNumActive(); + + /** + * Clears any objects sitting idle in the pool, releasing any associated + * resources (optional operation). Idle objects cleared must be + * {@link PooledObjectFactory#destroyObject(PooledObject)}. + * + * @throws UnsupportedOperationException + * if this implementation does not support the operation + * + * @throws Exception if the pool cannot be cleared + */ + void clear() throws Exception, UnsupportedOperationException; + + /** + * Close this pool, and free any resources associated with it. + *

+ * Calling {@link #addObject} or {@link #borrowObject} after invoking this + * method on a pool will cause them to throw an {@link IllegalStateException}. + *

+ * Implementations should silently fail if not all resources can be freed. + */ + @Override + void close(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PoolUtils.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PoolUtils.java new file mode 100755 index 000000000..ed63f1af5 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PoolUtils.java @@ -0,0 +1,1824 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * This class consists exclusively of static methods that operate on or return + * ObjectPool or KeyedObjectPool related interfaces. + * + * @since 2.0 + */ +public final class PoolUtils { + + /** + * Timer used to periodically check pools idle object count. Because a + * {@link Timer} creates a {@link Thread}, an IODH is used. + */ + static class TimerHolder { + static final Timer MIN_IDLE_TIMER = new Timer(true); + } + + /** + * PoolUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);. + * This constructor is public to permit tools that require a JavaBean + * instance to operate. + */ + public PoolUtils() { + } + + /** + * Should the supplied Throwable be re-thrown (eg if it is an instance of + * one of the Throwables that should never be swallowed). Used by the pool + * error handling for operations that throw exceptions that normally need to + * be ignored. + * + * @param t + * The Throwable to check + * @throws ThreadDeath + * if that is passed in + * @throws VirtualMachineError + * if that is passed in + */ + public static void checkRethrow(final Throwable t) { + if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; + } + if (t instanceof VirtualMachineError) { + throw (VirtualMachineError) t; + } + // All other instances of Throwable will be silently swallowed + } + + /** + * Periodically check the idle object count for the pool. At most one idle + * object will be added per period. If there is an exception when calling + * {@link ObjectPool#addObject()} then no more checks will be performed. + * + * @param pool + * the pool to check periodically. + * @param minIdle + * if the {@link ObjectPool#getNumIdle()} is less than this then + * add an idle object. + * @param period + * the frequency to check the number of idle objects in a pool, + * see {@link Timer#schedule(TimerTask, long, long)}. + * @param the type of objects in the pool + * @return the {@link TimerTask} that will periodically check the pools idle + * object count. + * @throws IllegalArgumentException + * when pool is null or when + * minIdle is negative or when period + * isn't valid for {@link Timer#schedule(TimerTask, long, long)} + */ + public static TimerTask checkMinIdle(final ObjectPool pool, + final int minIdle, final long period) + throws IllegalArgumentException { + if (pool == null) { + throw new IllegalArgumentException("keyedPool must not be null."); + } + if (minIdle < 0) { + throw new IllegalArgumentException("minIdle must be non-negative."); + } + final TimerTask task = new ObjectPoolMinIdleTimerTask(pool, minIdle); + getMinIdleTimer().schedule(task, 0L, period); + return task; + } + + /** + * Periodically check the idle object count for the key in the keyedPool. At + * most one idle object will be added per period. If there is an exception + * when calling {@link KeyedObjectPool#addObject(Object)} then no more + * checks for that key will be performed. + * + * @param keyedPool + * the keyedPool to check periodically. + * @param key + * the key to check the idle count of. + * @param minIdle + * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than + * this then add an idle object. + * @param period + * the frequency to check the number of idle objects in a + * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. + * @param the type of the pool key + * @param the type of pool entries + * @return the {@link TimerTask} that will periodically check the pools idle + * object count. + * @throws IllegalArgumentException + * when keyedPool, key is + * null or when minIdle is negative or + * when period isn't valid for + * {@link Timer#schedule(TimerTask, long, long)}. + */ + public static TimerTask checkMinIdle( + final KeyedObjectPool keyedPool, final K key, + final int minIdle, final long period) + throws IllegalArgumentException { + if (keyedPool == null) { + throw new IllegalArgumentException("keyedPool must not be null."); + } + if (key == null) { + throw new IllegalArgumentException("key must not be null."); + } + if (minIdle < 0) { + throw new IllegalArgumentException("minIdle must be non-negative."); + } + final TimerTask task = new KeyedObjectPoolMinIdleTimerTask( + keyedPool, key, minIdle); + getMinIdleTimer().schedule(task, 0L, period); + return task; + } + + /** + * Periodically check the idle object count for each key in the + * Collection keys in the keyedPool. At most one + * idle object will be added per period. + * + * @param keyedPool + * the keyedPool to check periodically. + * @param keys + * a collection of keys to check the idle object count. + * @param minIdle + * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than + * this then add an idle object. + * @param period + * the frequency to check the number of idle objects in a + * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. + * @param the type of the pool key + * @param the type of pool entries + * @return a {@link Map} of key and {@link TimerTask} pairs that will + * periodically check the pools idle object count. + * @throws IllegalArgumentException + * when keyedPool, keys, or any of the + * values in the collection is null or when + * minIdle is negative or when period + * isn't valid for {@link Timer#schedule(TimerTask, long, long)} + * . + * @see #checkMinIdle(KeyedObjectPool, Object, int, long) + */ + public static Map checkMinIdle( + final KeyedObjectPool keyedPool, final Collection keys, + final int minIdle, final long period) + throws IllegalArgumentException { + if (keys == null) { + throw new IllegalArgumentException("keys must not be null."); + } + final Map tasks = new HashMap(keys.size()); + final Iterator iter = keys.iterator(); + while (iter.hasNext()) { + final K key = iter.next(); + final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period); + tasks.put(key, task); + } + return tasks; + } + + /** + * Call addObject() on pool count + * number of times. + * + * @param pool + * the pool to prefill. + * @param count + * the number of idle objects to add. + * @param the type of objects in the pool + * @throws Exception + * when {@link ObjectPool#addObject()} fails. + * @throws IllegalArgumentException + * when pool is null. + */ + public static void prefill(final ObjectPool pool, final int count) + throws Exception, IllegalArgumentException { + if (pool == null) { + throw new IllegalArgumentException("pool must not be null."); + } + for (int i = 0; i < count; i++) { + pool.addObject(); + } + } + + /** + * Call addObject(Object) on keyedPool with + * key count number of times. + * + * @param keyedPool + * the keyedPool to prefill. + * @param key + * the key to add objects for. + * @param count + * the number of idle objects to add for key. + * @param the type of the pool key + * @param the type of pool entries + * @throws Exception + * when {@link KeyedObjectPool#addObject(Object)} fails. + * @throws IllegalArgumentException + * when keyedPool or key is + * null. + */ + public static void prefill(final KeyedObjectPool keyedPool, + final K key, final int count) throws Exception, + IllegalArgumentException { + if (keyedPool == null) { + throw new IllegalArgumentException("keyedPool must not be null."); + } + if (key == null) { + throw new IllegalArgumentException("key must not be null."); + } + for (int i = 0; i < count; i++) { + keyedPool.addObject(key); + } + } + + /** + * Call addObject(Object) on keyedPool with each + * key in keys for count number of times. This has + * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)} + * for each key in the keys collection. + * + * @param keyedPool + * the keyedPool to prefill. + * @param keys + * {@link Collection} of keys to add objects for. + * @param count + * the number of idle objects to add for each key. + * @param the type of the pool key + * @param the type of pool entries + * @throws Exception + * when {@link KeyedObjectPool#addObject(Object)} fails. + * @throws IllegalArgumentException + * when keyedPool, keys, or any value + * in keys is null. + * @see #prefill(KeyedObjectPool, Object, int) + */ + public static void prefill(final KeyedObjectPool keyedPool, + final Collection keys, final int count) throws Exception, + IllegalArgumentException { + if (keys == null) { + throw new IllegalArgumentException("keys must not be null."); + } + final Iterator iter = keys.iterator(); + while (iter.hasNext()) { + prefill(keyedPool, iter.next(), count); + } + } + + /** + * Returns a synchronized (thread-safe) ObjectPool backed by the specified + * ObjectPool. + *

+ * Note: This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. + *

+ * + * @param pool + * the ObjectPool to be "wrapped" in a synchronized ObjectPool. + * @param the type of objects in the pool + * @return a synchronized view of the specified ObjectPool. + */ + public static ObjectPool synchronizedPool(final ObjectPool pool) { + if (pool == null) { + throw new IllegalArgumentException("pool must not be null."); + } + /* + * assert !(pool instanceof GenericObjectPool) : + * "GenericObjectPool is already thread-safe"; assert !(pool instanceof + * SoftReferenceObjectPool) : + * "SoftReferenceObjectPool is already thread-safe"; assert !(pool + * instanceof StackObjectPool) : + * "StackObjectPool is already thread-safe"; assert + * !"org.apache.commons.pool.composite.CompositeObjectPool" + * .equals(pool.getClass().getName()) : + * "CompositeObjectPools are already thread-safe"; + */ + return new SynchronizedObjectPool(pool); + } + + /** + * Returns a synchronized (thread-safe) KeyedObjectPool backed by the + * specified KeyedObjectPool. + *

+ * Note: This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. + *

+ * + * @param keyedPool + * the KeyedObjectPool to be "wrapped" in a synchronized + * KeyedObjectPool. + * @param the type of the pool key + * @param the type of pool entries + * @return a synchronized view of the specified KeyedObjectPool. + */ + public static KeyedObjectPool synchronizedPool( + final KeyedObjectPool keyedPool) { + /* + * assert !(keyedPool instanceof GenericKeyedObjectPool) : + * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool + * instanceof StackKeyedObjectPool) : + * "StackKeyedObjectPool is already thread-safe"; assert + * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool" + * .equals(keyedPool.getClass().getName()) : + * "CompositeKeyedObjectPools are already thread-safe"; + */ + return new SynchronizedKeyedObjectPool(keyedPool); + } + + /** + * Returns a synchronized (thread-safe) PooledObjectFactory backed by the + * specified PooledObjectFactory. + * + * @param factory + * the PooledObjectFactory to be "wrapped" in a synchronized + * PooledObjectFactory. + * @param the type of objects in the pool + * @return a synchronized view of the specified PooledObjectFactory. + */ + public static PooledObjectFactory synchronizedPooledFactory( + final PooledObjectFactory factory) { + return new SynchronizedPooledObjectFactory(factory); + } + + /** + * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by + * the specified KeyedPoolableObjectFactory. + * + * @param keyedFactory + * the KeyedPooledObjectFactory to be "wrapped" in a + * synchronized KeyedPooledObjectFactory. + * @param the type of the pool key + * @param the type of pool entries + * @return a synchronized view of the specified KeyedPooledObjectFactory. + */ + public static KeyedPooledObjectFactory synchronizedKeyedPooledFactory( + final KeyedPooledObjectFactory keyedFactory) { + return new SynchronizedKeyedPooledObjectFactory(keyedFactory); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * + * @param pool + * the ObjectPool to be decorated so it shrinks its idle count + * when possible. + * @param the type of objects in the pool + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(ObjectPool, float) + */ + public static ObjectPool erodingPool(final ObjectPool pool) { + return erodingPool(pool, 1f); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + *

+ * The factor parameter provides a mechanism to tweak the rate at which the + * pool tries to shrink its size. Values between 0 and 1 cause the pool to + * try to shrink its size more often. Values greater than 1 cause the pool + * to less frequently try to shrink its size. + *

+ * + * @param pool + * the ObjectPool to be decorated so it shrinks its idle count + * when possible. + * @param factor + * a positive value to scale the rate at which the pool tries to + * reduce its size. If 0 < factor < 1 then the pool + * shrinks more aggressively. If 1 < factor then the pool + * shrinks less aggressively. + * @param the type of objects in the pool + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(ObjectPool) + */ + public static ObjectPool erodingPool(final ObjectPool pool, + final float factor) { + if (pool == null) { + throw new IllegalArgumentException("pool must not be null."); + } + if (factor <= 0f) { + throw new IllegalArgumentException("factor must be positive."); + } + return new ErodingObjectPool(pool, factor); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + * + * @param keyedPool + * the KeyedObjectPool to be decorated so it shrinks its idle + * count when possible. + * @param the type of the pool key + * @param the type of pool entries + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(KeyedObjectPool, float) + * @see #erodingPool(KeyedObjectPool, float, boolean) + */ + public static KeyedObjectPool erodingPool( + final KeyedObjectPool keyedPool) { + return erodingPool(keyedPool, 1f); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + *

+ * The factor parameter provides a mechanism to tweak the rate at which the + * pool tries to shrink its size. Values between 0 and 1 cause the pool to + * try to shrink its size more often. Values greater than 1 cause the pool + * to less frequently try to shrink its size. + *

+ * + * @param keyedPool + * the KeyedObjectPool to be decorated so it shrinks its idle + * count when possible. + * @param factor + * a positive value to scale the rate at which the pool tries to + * reduce its size. If 0 < factor < 1 then the pool + * shrinks more aggressively. If 1 < factor then the pool + * shrinks less aggressively. + * @param the type of the pool key + * @param the type of pool entries + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(KeyedObjectPool, float, boolean) + */ + public static KeyedObjectPool erodingPool( + final KeyedObjectPool keyedPool, final float factor) { + return erodingPool(keyedPool, factor, false); + } + + /** + * Returns a pool that adaptively decreases its size when idle objects are + * no longer needed. This is intended as an always thread-safe alternative + * to using an idle object evictor provided by many pool implementations. + * This is also an effective way to shrink FIFO ordered pools that + * experience load spikes. + *

+ * The factor parameter provides a mechanism to tweak the rate at which the + * pool tries to shrink its size. Values between 0 and 1 cause the pool to + * try to shrink its size more often. Values greater than 1 cause the pool + * to less frequently try to shrink its size. + *

+ *

+ * The perKey parameter determines if the pool shrinks on a whole pool basis + * or a per key basis. When perKey is false, the keys do not have an effect + * on the rate at which the pool tries to shrink its size. When perKey is + * true, each key is shrunk independently. + *

+ * + * @param keyedPool + * the KeyedObjectPool to be decorated so it shrinks its idle + * count when possible. + * @param factor + * a positive value to scale the rate at which the pool tries to + * reduce its size. If 0 < factor < 1 then the pool + * shrinks more aggressively. If 1 < factor then the pool + * shrinks less aggressively. + * @param perKey + * when true, each key is treated independently. + * @param the type of the pool key + * @param the type of pool entries + * @return a pool that adaptively decreases its size when idle objects are + * no longer needed. + * @see #erodingPool(KeyedObjectPool) + * @see #erodingPool(KeyedObjectPool, float) + */ + public static KeyedObjectPool erodingPool( + final KeyedObjectPool keyedPool, final float factor, + final boolean perKey) { + if (keyedPool == null) { + throw new IllegalArgumentException("keyedPool must not be null."); + } + if (factor <= 0f) { + throw new IllegalArgumentException("factor must be positive."); + } + if (perKey) { + return new ErodingPerKeyKeyedObjectPool(keyedPool, factor); + } + return new ErodingKeyedObjectPool(keyedPool, factor); + } + + /** + * Get the Timer for checking keyedPool's idle count. + * + * @return the {@link Timer} for checking keyedPool's idle count. + */ + private static Timer getMinIdleTimer() { + return TimerHolder.MIN_IDLE_TIMER; + } + + /** + * Timer task that adds objects to the pool until the number of idle + * instances reaches the configured minIdle. Note that this is not the same + * as the pool's minIdle setting. + * + * @param type of objects in the pool + */ + private static final class ObjectPoolMinIdleTimerTask extends TimerTask { + + /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ + private final int minIdle; + + /** Object pool */ + private final ObjectPool pool; + + /** + * Create a new ObjectPoolMinIdleTimerTask for the given pool with the + * given minIdle setting. + * + * @param pool + * object pool + * @param minIdle + * number of idle instances to maintain + * @throws IllegalArgumentException + * if the pool is null + */ + ObjectPoolMinIdleTimerTask(final ObjectPool pool, final int minIdle) + throws IllegalArgumentException { + if (pool == null) { + throw new IllegalArgumentException("pool must not be null."); + } + this.pool = pool; + this.minIdle = minIdle; + } + + /** + * {@inheritDoc} + */ + @Override + public void run() { + boolean success = false; + try { + if (pool.getNumIdle() < minIdle) { + pool.addObject(); + } + success = true; + + } catch (final Exception e) { + cancel(); + } finally { + // detect other types of Throwable and cancel this Timer + if (!success) { + cancel(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("ObjectPoolMinIdleTimerTask"); + sb.append("{minIdle=").append(minIdle); + sb.append(", pool=").append(pool); + sb.append('}'); + return sb.toString(); + } + } + + /** + * Timer task that adds objects to the pool until the number of idle + * instances for the given key reaches the configured minIdle. Note that + * this is not the same as the pool's minIdle setting. + * + * @param object pool key type + * @param object pool value type + */ + private static final class KeyedObjectPoolMinIdleTimerTask extends + TimerTask { + /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ + private final int minIdle; + + /** Key to ensure minIdle for */ + private final K key; + + /** Keyed object pool */ + private final KeyedObjectPool keyedPool; + + /** + * Create a new KeyedObjecPoolMinIdleTimerTask. + * + * @param keyedPool + * keyed object pool + * @param key + * key to ensure minimum number of idle instances + * @param minIdle + * minimum number of idle instances + * @throws IllegalArgumentException + * if the key is null + */ + KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool keyedPool, + final K key, final int minIdle) throws IllegalArgumentException { + if (keyedPool == null) { + throw new IllegalArgumentException( + "keyedPool must not be null."); + } + this.keyedPool = keyedPool; + this.key = key; + this.minIdle = minIdle; + } + + /** + * {@inheritDoc} + */ + @Override + public void run() { + boolean success = false; + try { + if (keyedPool.getNumIdle(key) < minIdle) { + keyedPool.addObject(key); + } + success = true; + + } catch (final Exception e) { + cancel(); + + } finally { + // detect other types of Throwable and cancel this Timer + if (!success) { + cancel(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("KeyedObjectPoolMinIdleTimerTask"); + sb.append("{minIdle=").append(minIdle); + sb.append(", key=").append(key); + sb.append(", keyedPool=").append(keyedPool); + sb.append('}'); + return sb.toString(); + } + } + + /** + * A synchronized (thread-safe) ObjectPool backed by the specified + * ObjectPool. + *

+ * Note: This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. + *

+ * + * @param type of objects in the pool + */ + private static final class SynchronizedObjectPool implements ObjectPool { + + /** + * Object whose monitor is used to synchronize methods on the wrapped + * pool. + */ + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + /** the underlying object pool */ + private final ObjectPool pool; + + /** + * Create a new SynchronizedObjectPool wrapping the given pool. + * + * @param pool + * the ObjectPool to be "wrapped" in a synchronized + * ObjectPool. + * @throws IllegalArgumentException + * if the pool is null + */ + SynchronizedObjectPool(final ObjectPool pool) + throws IllegalArgumentException { + if (pool == null) { + throw new IllegalArgumentException("pool must not be null."); + } + this.pool = pool; + } + + /** + * {@inheritDoc} + */ + @Override + public T borrowObject() throws Exception, NoSuchElementException, + IllegalStateException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + return pool.borrowObject(); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void returnObject(final T obj) { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.returnObject(obj); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void invalidateObject(final T obj) { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.invalidateObject(obj); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addObject() throws Exception, IllegalStateException, + UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.addObject(); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle() { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return pool.getNumIdle(); + } finally { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive() { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return pool.getNumActive(); + } finally { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.clear(); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void close() { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + pool.close(); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedObjectPool"); + sb.append("{pool=").append(pool); + sb.append('}'); + return sb.toString(); + } + } + + /** + * A synchronized (thread-safe) KeyedObjectPool backed by the specified + * KeyedObjectPool. + *

+ * Note: This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. Wrapping a pool that {@link #wait() waits} for poolable + * objects to be returned before allowing another one to be borrowed with + * another layer of synchronization will cause liveliness issues or a + * deadlock. + *

+ * + * @param object pool key type + * @param object pool value type + */ + private static final class SynchronizedKeyedObjectPool implements + KeyedObjectPool { + + /** + * Object whose monitor is used to synchronize methods on the wrapped + * pool. + */ + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + /** Underlying object pool */ + private final KeyedObjectPool keyedPool; + + /** + * Create a new SynchronizedKeyedObjectPool wrapping the given pool + * + * @param keyedPool + * KeyedObjectPool to wrap + * @throws IllegalArgumentException + * if keyedPool is null + */ + SynchronizedKeyedObjectPool(final KeyedObjectPool keyedPool) + throws IllegalArgumentException { + if (keyedPool == null) { + throw new IllegalArgumentException( + "keyedPool must not be null."); + } + this.keyedPool = keyedPool; + } + + /** + * {@inheritDoc} + */ + @Override + public V borrowObject(final K key) throws Exception, + NoSuchElementException, IllegalStateException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + return keyedPool.borrowObject(key); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void returnObject(final K key, final V obj) { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.returnObject(key, obj); + } catch (final Exception e) { + // swallowed + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void invalidateObject(final K key, final V obj) { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.invalidateObject(key, obj); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addObject(final K key) throws Exception, + IllegalStateException, UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.addObject(key); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle(final K key) { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return keyedPool.getNumIdle(key); + } finally { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive(final K key) { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return keyedPool.getNumActive(key); + } finally { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle() { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return keyedPool.getNumIdle(); + } finally { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive() { + final ReadLock readLock = readWriteLock.readLock(); + readLock.lock(); + try { + return keyedPool.getNumActive(); + } finally { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.clear(); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void clear(final K key) throws Exception, + UnsupportedOperationException { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.clear(key); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void close() { + final WriteLock writeLock = readWriteLock.writeLock(); + writeLock.lock(); + try { + keyedPool.close(); + } catch (final Exception e) { + // swallowed as of Pool 2 + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedKeyedObjectPool"); + sb.append("{keyedPool=").append(keyedPool); + sb.append('}'); + return sb.toString(); + } + } + + /** + * A fully synchronized PooledObjectFactory that wraps a + * PooledObjectFactory and synchronizes access to the wrapped factory + * methods. + *

+ * Note: This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. + *

+ * + * @param pooled object factory type + */ + private static final class SynchronizedPooledObjectFactory implements + PooledObjectFactory { + /** Synchronization lock */ + private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); + + /** Wrapped factory */ + private final PooledObjectFactory factory; + + /** + * Create a SynchronizedPoolableObjectFactory wrapping the given + * factory. + * + * @param factory + * underlying factory to wrap + * @throws IllegalArgumentException + * if the factory is null + */ + SynchronizedPooledObjectFactory(final PooledObjectFactory factory) + throws IllegalArgumentException { + if (factory == null) { + throw new IllegalArgumentException("factory must not be null."); + } + this.factory = factory; + } + + /** + * {@inheritDoc} + */ + @Override + public PooledObject makeObject() throws Exception { + writeLock.lock(); + try { + return factory.makeObject(); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void destroyObject(final PooledObject p) throws Exception { + writeLock.lock(); + try { + factory.destroyObject(p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean validateObject(final PooledObject p) { + writeLock.lock(); + try { + return factory.validateObject(p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void activateObject(final PooledObject p) throws Exception { + writeLock.lock(); + try { + factory.activateObject(p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void passivateObject(final PooledObject p) throws Exception { + writeLock.lock(); + try { + factory.passivateObject(p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedPoolableObjectFactory"); + sb.append("{factory=").append(factory); + sb.append('}'); + return sb.toString(); + } + } + + /** + * A fully synchronized KeyedPooledObjectFactory that wraps a + * KeyedPooledObjectFactory and synchronizes access to the wrapped factory + * methods. + *

+ * Note: This should not be used on pool implementations that already + * provide proper synchronization such as the pools provided in the Commons + * Pool library. + *

+ * + * @param pooled object factory key type + * @param pooled object factory key value + */ + private static final class SynchronizedKeyedPooledObjectFactory + implements KeyedPooledObjectFactory { + /** Synchronization lock */ + private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); + + /** Wrapped factory */ + private final KeyedPooledObjectFactory keyedFactory; + + /** + * Create a SynchronizedKeyedPoolableObjectFactory wrapping the given + * factory. + * + * @param keyedFactory + * underlying factory to wrap + * @throws IllegalArgumentException + * if the factory is null + */ + SynchronizedKeyedPooledObjectFactory( + final KeyedPooledObjectFactory keyedFactory) + throws IllegalArgumentException { + if (keyedFactory == null) { + throw new IllegalArgumentException( + "keyedFactory must not be null."); + } + this.keyedFactory = keyedFactory; + } + + /** + * {@inheritDoc} + */ + @Override + public PooledObject makeObject(final K key) throws Exception { + writeLock.lock(); + try { + return keyedFactory.makeObject(key); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void destroyObject(final K key, final PooledObject p) throws Exception { + writeLock.lock(); + try { + keyedFactory.destroyObject(key, p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean validateObject(final K key, final PooledObject p) { + writeLock.lock(); + try { + return keyedFactory.validateObject(key, p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void activateObject(final K key, final PooledObject p) throws Exception { + writeLock.lock(); + try { + keyedFactory.activateObject(key, p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void passivateObject(final K key, final PooledObject p) throws Exception { + writeLock.lock(); + try { + keyedFactory.passivateObject(key, p); + } finally { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("SynchronizedKeyedPoolableObjectFactory"); + sb.append("{keyedFactory=").append(keyedFactory); + sb.append('}'); + return sb.toString(); + } + } + + /** + * Encapsulate the logic for when the next poolable object should be + * discarded. Each time update is called, the next time to shrink is + * recomputed, based on the float factor, number of idle instances in the + * pool and high water mark. Float factor is assumed to be between 0 and 1. + * Values closer to 1 cause less frequent erosion events. Erosion event + * timing also depends on numIdle. When this value is relatively high (close + * to previously established high water mark), erosion occurs more + * frequently. + */ + private static final class ErodingFactor { + /** Determines frequency of "erosion" events */ + private final float factor; + + /** Time of next shrink event */ + private transient volatile long nextShrink; + + /** High water mark - largest numIdle encountered */ + private transient volatile int idleHighWaterMark; + + /** + * Create a new ErodingFactor with the given erosion factor. + * + * @param factor + * erosion factor + */ + public ErodingFactor(final float factor) { + this.factor = factor; + nextShrink = System.currentTimeMillis() + (long) (900000 * factor); // now + // + + // 15 + // min + // * + // factor + idleHighWaterMark = 1; + } + + /** + * Updates internal state using the supplied time and numIdle. + * + * @param now + * current time + * @param numIdle + * number of idle elements in the pool + */ + public void update(final long now, final int numIdle) { + final int idle = Math.max(0, numIdle); + idleHighWaterMark = Math.max(idle, idleHighWaterMark); + final float maxInterval = 15f; + final float minutes = maxInterval + + ((1f - maxInterval) / idleHighWaterMark) * idle; + nextShrink = now + (long) (minutes * 60000f * factor); + } + + /** + * Returns the time of the next erosion event. + * + * @return next shrink time + */ + public long getNextShrink() { + return nextShrink; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "ErodingFactor{" + "factor=" + factor + + ", idleHighWaterMark=" + idleHighWaterMark + '}'; + } + } + + /** + * Decorates an object pool, adding "eroding" behavior. Based on the + * configured {@link #factor erosion factor}, objects returning to the pool + * may be invalidated instead of being added to idle capacity. + * + * @param type of objects in the pool + */ + private static class ErodingObjectPool implements ObjectPool { + /** Underlying object pool */ + private final ObjectPool pool; + + /** Erosion factor */ + private final ErodingFactor factor; + + /** + * Create an ErodingObjectPool wrapping the given pool using the + * specified erosion factor. + * + * @param pool + * underlying pool + * @param factor + * erosion factor - determines the frequency of erosion + * events + * @see #factor + */ + public ErodingObjectPool(final ObjectPool pool, final float factor) { + this.pool = pool; + this.factor = new ErodingFactor(factor); + } + + /** + * {@inheritDoc} + */ + @Override + public T borrowObject() throws Exception, NoSuchElementException, + IllegalStateException { + return pool.borrowObject(); + } + + /** + * Returns obj to the pool, unless erosion is triggered, in which case + * obj is invalidated. Erosion is triggered when there are idle + * instances in the pool and more than the {@link #factor erosion + * factor}-determined time has elapsed since the last returnObject + * activation. + * + * @param obj + * object to return or invalidate + * @see #factor + */ + @Override + public void returnObject(final T obj) { + boolean discard = false; + final long now = System.currentTimeMillis(); + synchronized (pool) { + if (factor.getNextShrink() < now) { // XXX: Pool 3: move test + // out of sync block + final int numIdle = pool.getNumIdle(); + if (numIdle > 0) { + discard = true; + } + + factor.update(now, numIdle); + } + } + try { + if (discard) { + pool.invalidateObject(obj); + } else { + pool.returnObject(obj); + } + } catch (final Exception e) { + // swallowed + } + } + + /** + * {@inheritDoc} + */ + @Override + public void invalidateObject(final T obj) { + try { + pool.invalidateObject(obj); + } catch (final Exception e) { + // swallowed + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addObject() throws Exception, IllegalStateException, + UnsupportedOperationException { + pool.addObject(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle() { + return pool.getNumIdle(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive() { + return pool.getNumActive(); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + pool.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public void close() { + try { + pool.close(); + } catch (final Exception e) { + // swallowed + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "ErodingObjectPool{" + "factor=" + factor + ", pool=" + + pool + '}'; + } + } + + /** + * Decorates a keyed object pool, adding "eroding" behavior. Based on the + * configured erosion factor, objects returning to the pool + * may be invalidated instead of being added to idle capacity. + * + * @param object pool key type + * @param object pool value type + */ + private static class ErodingKeyedObjectPool implements + KeyedObjectPool { + /** Underlying pool */ + private final KeyedObjectPool keyedPool; + + /** Erosion factor */ + private final ErodingFactor erodingFactor; + + /** + * Create an ErodingObjectPool wrapping the given pool using the + * specified erosion factor. + * + * @param keyedPool + * underlying pool + * @param factor + * erosion factor - determines the frequency of erosion + * events + * @see #erodingFactor + */ + public ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, + final float factor) { + this(keyedPool, new ErodingFactor(factor)); + } + + /** + * Create an ErodingObjectPool wrapping the given pool using the + * specified erosion factor. + * + * @param keyedPool + * underlying pool - must not be null + * @param erodingFactor + * erosion factor - determines the frequency of erosion + * events + * @see #factor + */ + protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, + final ErodingFactor erodingFactor) { + if (keyedPool == null) { + throw new IllegalArgumentException( + "keyedPool must not be null."); + } + this.keyedPool = keyedPool; + this.erodingFactor = erodingFactor; + } + + /** + * {@inheritDoc} + */ + @Override + public V borrowObject(final K key) throws Exception, + NoSuchElementException, IllegalStateException { + return keyedPool.borrowObject(key); + } + + /** + * Returns obj to the pool, unless erosion is triggered, in which case + * obj is invalidated. Erosion is triggered when there are idle + * instances in the pool associated with the given key and more than the + * configured {@link #erodingFactor erosion factor} time has elapsed + * since the last returnObject activation. + * + * @param obj + * object to return or invalidate + * @param key + * key + * @see #erodingFactor + */ + @Override + public void returnObject(final K key, final V obj) throws Exception { + boolean discard = false; + final long now = System.currentTimeMillis(); + final ErodingFactor factor = getErodingFactor(key); + synchronized (keyedPool) { + if (factor.getNextShrink() < now) { + final int numIdle = getNumIdle(key); + if (numIdle > 0) { + discard = true; + } + + factor.update(now, numIdle); + } + } + try { + if (discard) { + keyedPool.invalidateObject(key, obj); + } else { + keyedPool.returnObject(key, obj); + } + } catch (final Exception e) { + // swallowed + } + } + + /** + * Returns the eroding factor for the given key + * + * @param key + * key + * @return eroding factor for the given keyed pool + */ + protected ErodingFactor getErodingFactor(final K key) { + return erodingFactor; + } + + /** + * {@inheritDoc} + */ + @Override + public void invalidateObject(final K key, final V obj) { + try { + keyedPool.invalidateObject(key, obj); + } catch (final Exception e) { + // swallowed + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addObject(final K key) throws Exception, + IllegalStateException, UnsupportedOperationException { + keyedPool.addObject(key); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle() { + return keyedPool.getNumIdle(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumIdle(final K key) { + return keyedPool.getNumIdle(key); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive() { + return keyedPool.getNumActive(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getNumActive(final K key) { + return keyedPool.getNumActive(key); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() throws Exception, UnsupportedOperationException { + keyedPool.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear(final K key) throws Exception, + UnsupportedOperationException { + keyedPool.clear(key); + } + + /** + * {@inheritDoc} + */ + @Override + public void close() { + try { + keyedPool.close(); + } catch (final Exception e) { + // swallowed + } + } + + /** + * Returns the underlying pool + * + * @return the keyed pool that this ErodingKeyedObjectPool wraps + */ + protected KeyedObjectPool getKeyedPool() { + return keyedPool; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "ErodingKeyedObjectPool{" + "factor=" + + erodingFactor + ", keyedPool=" + keyedPool + '}'; + } + } + + /** + * Extends ErodingKeyedObjectPool to allow erosion to take place on a + * per-key basis. Timing of erosion events is tracked separately for + * separate keyed pools. + * + * @param object pool key type + * @param object pool value type + */ + private static final class ErodingPerKeyKeyedObjectPool extends + ErodingKeyedObjectPool { + /** Erosion factor - same for all pools */ + private final float factor; + + /** Map of ErodingFactor instances keyed on pool keys */ + private final Map factors = Collections.synchronizedMap(new HashMap()); + + /** + * Create a new ErordingPerKeyKeyedObjectPool decorating the given keyed + * pool with the specified erosion factor. + * + * @param keyedPool + * underlying keyed pool + * @param factor + * erosion factor + */ + public ErodingPerKeyKeyedObjectPool( + final KeyedObjectPool keyedPool, final float factor) { + super(keyedPool, null); + this.factor = factor; + } + + /** + * {@inheritDoc} + */ + @Override + protected ErodingFactor getErodingFactor(final K key) { + ErodingFactor eFactor = factors.get(key); + // this may result in two ErodingFactors being created for a key + // since they are small and cheap this is okay. + if (eFactor == null) { + eFactor = new ErodingFactor(this.factor); + factors.put(key, eFactor); + } + return eFactor; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor + + ", keyedPool=" + getKeyedPool() + '}'; + } + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObject.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObject.java new file mode 100755 index 000000000..c61e10c33 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObject.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +import java.io.PrintWriter; +import java.util.Deque; + +/** + * Defines the wrapper that is used to track the additional information, such as + * state, for the pooled objects. + *

+ * Implementations of this class are required to be thread-safe. + * + * @param the type of object in the pool + * + * @since 2.0 + */ +public interface PooledObject extends Comparable> { + + /** + * Obtain the underlying object that is wrapped by this instance of + * {@link PooledObject}. + * + * @return The wrapped object + */ + T getObject(); + + /** + * Obtain the time (using the same basis as + * {@link System#currentTimeMillis()}) that this object was created. + * + * @return The creation time for the wrapped object + */ + long getCreateTime(); + + /** + * Obtain the time in milliseconds that this object last spent in the + * active state (it may still be active in which case subsequent calls will + * return an increased value). + * + * @return The time in milliseconds last spent in the active state + */ + long getActiveTimeMillis(); + + /** + * Obtain the time in milliseconds that this object last spend in the + * idle state (it may still be idle in which case subsequent calls will + * return an increased value). + * + * @return The time in milliseconds last spent in the idle state + */ + long getIdleTimeMillis(); + + /** + * Obtain the time the wrapped object was last borrowed. + * + * @return The time the object was last borrowed + */ + long getLastBorrowTime(); + + /** + * Obtain the time the wrapped object was last returned. + * + * @return The time the object was last returned + */ + long getLastReturnTime(); + + /** + * Return an estimate of the last time this object was used. If the class + * of the pooled object implements {@link TrackedUse}, what is returned is + * the maximum of {@link TrackedUse#getLastUsed()} and + * {@link #getLastBorrowTime()}; otherwise this method gives the same + * value as {@link #getLastBorrowTime()}. + * + * @return the last time this object was used + */ + long getLastUsedTime(); + + /** + * Orders instances based on idle time - i.e. the length of time since the + * instance was returned to the pool. Used by the GKOP idle object evictor. + *

+ * Note: This class has a natural ordering that is inconsistent with + * equals if distinct objects have the same identity hash code. + *

+ * {@inheritDoc} + */ + @Override + int compareTo(PooledObject other); + + @Override + boolean equals(Object obj); + + @Override + int hashCode(); + + /** + * Provides a String form of the wrapper for debug purposes. The format is + * not fixed and may change at any time. + *

+ * {@inheritDoc} + */ + @Override + String toString(); + + /** + * Attempt to place the pooled object in the + * {@link PooledObjectState#EVICTION} state. + * + * @return true if the object was placed in the + * {@link PooledObjectState#EVICTION} state otherwise + * false + */ + boolean startEvictionTest(); + + /** + * Called to inform the object that the eviction test has ended. + * + * @param idleQueue The queue of idle objects to which the object should be + * returned + * + * @return Currently not used + */ + boolean endEvictionTest(Deque> idleQueue); + + /** + * Allocates the object. + * + * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} + */ + boolean allocate(); + + /** + * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} + * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. + * + * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} + */ + boolean deallocate(); + + /** + * Sets the state to {@link PooledObjectState#INVALID INVALID} + */ + void invalidate(); + + /** + * Is abandoned object tracking being used? If this is true the + * implementation will need to record the stack trace of the last caller to + * borrow this object. + * + * @param logAbandoned The new configuration setting for abandoned + * object tracking + */ + void setLogAbandoned(boolean logAbandoned); + +// TODO: uncomment in 3.0 (API compatibility) +// /** +// * Configures the stack trace generation strategy based on whether or not fully +// * detailed stack traces are required. When set to false, abandoned logs may +// * only include caller class information rather than method names, line numbers, +// * and other normal metadata available in a full stack trace. +// * +// * @param requireFullStackTrace the new configuration setting for abandoned object +// * logging +// */ +// void setRequireFullStackTrace(boolean requireFullStackTrace); + + /** + * Record the current stack trace as the last time the object was used. + */ + void use(); + + /** + * Prints the stack trace of the code that borrowed this pooled object and + * the stack trace of the last code to use this object (if available) to + * the supplied writer. + * + * @param writer The destination for the debug output + */ + void printStackTrace(PrintWriter writer); + + /** + * Returns the state of this object. + * @return state + */ + PooledObjectState getState(); + + /** + * Marks the pooled object as abandoned. + */ + void markAbandoned(); + + /** + * Marks the object as returning to the pool. + */ + void markReturning(); + + // TODO: Uncomment this for version 3 (can't add it to 2.x as it will break + // API compatibility) + ///** + // * Get the number of times this object has been borrowed. + // */ + //long getBorrowedCount(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectFactory.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectFactory.java new file mode 100755 index 000000000..79b0201fe --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectFactory.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * An interface defining life-cycle methods for instances to be served by an + * {@link ObjectPool}. + *

+ * By contract, when an {@link ObjectPool} delegates to a + * {@link PooledObjectFactory}, + *

    + *
  1. + * {@link #makeObject} is called whenever a new instance is needed. + *
  2. + *
  3. + * {@link #activateObject} is invoked on every instance that has been + * {@link #passivateObject passivated} before it is + * {@link ObjectPool#borrowObject borrowed} from the pool. + *
  4. + *
  5. + * {@link #validateObject} may be invoked on {@link #activateObject activated} + * instances to make sure they can be {@link ObjectPool#borrowObject borrowed} + * from the pool. {@link #validateObject} may also be used to + * test an instance being {@link ObjectPool#returnObject returned} to the pool + * before it is {@link #passivateObject passivated}. It will only be invoked + * on an activated instance. + *
  6. + *
  7. + * {@link #passivateObject} is invoked on every instance when it is returned + * to the pool. + *
  8. + *
  9. + * {@link #destroyObject} is invoked on every instance when it is being + * "dropped" from the pool (whether due to the response from + * {@link #validateObject}, or for reasons specific to the pool + * implementation.) There is no guarantee that the instance being destroyed + * will be considered active, passive or in a generally consistent state. + *
  10. + *
+ * {@link PooledObjectFactory} must be thread-safe. The only promise + * an {@link ObjectPool} makes is that the same instance of an object will not + * be passed to more than one method of a PoolableObjectFactory + * at a time. + *

+ * While clients of a {@link KeyedObjectPool} borrow and return instances of + * the underlying value type {@code V}, the factory methods act on instances of + * {@link PooledObject PooledObject<V>}. These are the object wrappers that + * pools use to track and maintain state information about the objects that + * they manage. + * + * @param Type of element managed in this factory. + * + * @see ObjectPool + * + * @since 2.0 + */ +public interface PooledObjectFactory { + /** + * Create an instance that can be served by the pool and wrap it in a + * {@link PooledObject} to be managed by the pool. + * + * @return a {@code PooledObject} wrapping an instance that can be served by the pool + * + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. + */ + PooledObject makeObject() throws Exception; + + /** + * Destroys an instance no longer needed by the pool. + *

+ * It is important for implementations of this method to be aware that there + * is no guarantee about what state obj will be in and the + * implementation should be prepared to handle unexpected errors. + *

+ * Also, an implementation must take in to consideration that instances lost + * to the garbage collector may never be destroyed. + *

+ * + * @param p a {@code PooledObject} wrapping the instance to be destroyed + * + * @throws Exception should be avoided as it may be swallowed by + * the pool implementation. + * + * @see #validateObject + * @see ObjectPool#invalidateObject + */ + void destroyObject(PooledObject p) throws Exception; + + /** + * Ensures that the instance is safe to be returned by the pool. + * + * @param p a {@code PooledObject} wrapping the instance to be validated + * + * @return false if obj is not valid and should + * be dropped from the pool, true otherwise. + */ + boolean validateObject(PooledObject p); + + /** + * Reinitialize an instance to be returned by the pool. + * + * @param p a {@code PooledObject} wrapping the instance to be activated + * + * @throws Exception if there is a problem activating obj, + * this exception may be swallowed by the pool. + * + * @see #destroyObject + */ + void activateObject(PooledObject p) throws Exception; + + /** + * Uninitialize an instance to be returned to the idle object pool. + * + * @param p a {@code PooledObject} wrapping the instance to be passivated + * + * @throws Exception if there is a problem passivating obj, + * this exception may be swallowed by the pool. + * + * @see #destroyObject + */ + void passivateObject(PooledObject p) throws Exception; +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectState.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectState.java new file mode 100755 index 000000000..e86e60a51 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/PooledObjectState.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * Provides the possible states that a {@link PooledObject} may be in. + * + * @since 2.0 + */ +public enum PooledObjectState { + /** + * In the queue, not in use. + */ + IDLE, + + /** + * In use. + */ + ALLOCATED, + + /** + * In the queue, currently being tested for possible eviction. + */ + EVICTION, + + /** + * Not in the queue, currently being tested for possible eviction. An + * attempt to borrow the object was made while being tested which removed it + * from the queue. It should be returned to the head of the queue once + * eviction testing completes. + * TODO: Consider allocating object and ignoring the result of the eviction + * test. + */ + EVICTION_RETURN_TO_HEAD, + + /** + * In the queue, currently being validated. + */ + VALIDATION, + + /** + * Not in queue, currently being validated. The object was borrowed while + * being validated and since testOnBorrow was configured, it was removed + * from the queue and pre-allocated. It should be allocated once validation + * completes. + */ + VALIDATION_PREALLOCATED, + + /** + * Not in queue, currently being validated. An attempt to borrow the object + * was made while previously being tested for eviction which removed it from + * the queue. It should be returned to the head of the queue once validation + * completes. + */ + VALIDATION_RETURN_TO_HEAD, + + /** + * Failed maintenance (e.g. eviction test or validation) and will be / has + * been destroyed + */ + INVALID, + + /** + * Deemed abandoned, to be invalidated. + */ + ABANDONED, + + /** + * Returning to the pool. + */ + RETURNING +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/SwallowedExceptionListener.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/SwallowedExceptionListener.java new file mode 100755 index 000000000..b96886c5b --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/SwallowedExceptionListener.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * Pools that unavoidably swallow exceptions may be configured with an instance + * of this listener so the user may receive notification of when this happens. + * The listener should not throw an exception when called but pools calling + * listeners should protect themselves against exceptions anyway. + * + * @since 2.0 + */ +public interface SwallowedExceptionListener { + + /** + * This method is called every time the implementation unavoidably swallows + * an exception. + * + * @param e The exception that was swallowed + */ + void onSwallowException(Exception e); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/TrackedUse.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/TrackedUse.java new file mode 100755 index 000000000..67807e9e8 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/TrackedUse.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * This interface allows pooled objects to make information available about when + * and how they were used available to the object pool. The object pool may, but + * is not required, to use this information to make more informed decisions when + * determining the state of a pooled object - for instance whether or not the + * object has been abandoned. + * + * @since 2.0 + */ +public interface TrackedUse { + + /** + * Get the last time this object was used in ms. + * + * @return long time in ms + */ + long getLastUsed(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/UsageTracking.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/UsageTracking.java new file mode 100755 index 000000000..e5941a866 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/UsageTracking.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2; + +/** + * This interface may be implemented by an object pool to enable clients + * (primarily those clients that wrap pools to provide pools with extended + * features) to provide additional information to the pool relating to object + * using allowing more informed decisions and reporting to be made regarding + * abandoned objects. + * + * @param The type of object provided by the pool. + * + * @since 2.0 + */ +public interface UsageTracking { + + /** + * This method is called every time a pooled object is used to enable the pool to + * better track borrowed objects. + * + * @param pooledObject The object that is being used + */ + void use(T pooledObject); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/AbandonedConfig.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/AbandonedConfig.java new file mode 100755 index 000000000..cd9696e2a --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/AbandonedConfig.java @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.TrackedUse; +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +import java.io.PrintWriter; + +/** + * Configuration settings for abandoned object removal. + * + * @since 2.0 + */ +public class AbandonedConfig { + + /** + * Whether or not borrowObject performs abandoned object removal. + */ + private boolean removeAbandonedOnBorrow = false; + + /** + *

Flag to remove abandoned objects if they exceed the + * removeAbandonedTimeout when borrowObject is invoked.

+ * + *

The default value is false.

+ * + *

If set to true, abandoned objects are removed by borrowObject if + * there are fewer than 2 idle objects available in the pool and + * getNumActive() > getMaxTotal() - 3

+ * + * @return true if abandoned objects are to be removed by borrowObject + */ + public boolean getRemoveAbandonedOnBorrow() { + return this.removeAbandonedOnBorrow; + } + + /** + *

Flag to remove abandoned objects if they exceed the + * removeAbandonedTimeout when borrowObject is invoked.

+ * + * @param removeAbandonedOnBorrow true means abandoned objects will be + * removed by borrowObject + * @see #getRemoveAbandonedOnBorrow() + */ + public void setRemoveAbandonedOnBorrow(final boolean removeAbandonedOnBorrow) { + this.removeAbandonedOnBorrow = removeAbandonedOnBorrow; + } + + /** + * Whether or not pool maintenance (evictor) performs abandoned object + * removal. + */ + private boolean removeAbandonedOnMaintenance = false; + + /** + *

Flag to remove abandoned objects if they exceed the + * removeAbandonedTimeout when pool maintenance (the "evictor") + * runs.

+ * + *

The default value is false.

+ * + *

If set to true, abandoned objects are removed by the pool + * maintenance thread when it runs. This setting has no effect + * unless maintenance is enabled by setting + *{@link GenericObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis} + * to a positive number.

+ * + * @return true if abandoned objects are to be removed by the evictor + */ + public boolean getRemoveAbandonedOnMaintenance() { + return this.removeAbandonedOnMaintenance; + } + + /** + *

Flag to remove abandoned objects if they exceed the + * removeAbandonedTimeout when pool maintenance runs.

+ * + * @param removeAbandonedOnMaintenance true means abandoned objects will be + * removed by pool maintenance + * @see #getRemoveAbandonedOnMaintenance + */ + public void setRemoveAbandonedOnMaintenance(final boolean removeAbandonedOnMaintenance) { + this.removeAbandonedOnMaintenance = removeAbandonedOnMaintenance; + } + + /** + * Timeout in seconds before an abandoned object can be removed. + */ + private int removeAbandonedTimeout = 300; + + /** + *

Timeout in seconds before an abandoned object can be removed.

+ * + *

The time of most recent use of an object is the maximum (latest) of + * {@link TrackedUse#getLastUsed()} (if this class of the object implements + * TrackedUse) and the time when the object was borrowed from the pool.

+ * + *

The default value is 300 seconds.

+ * + * @return the abandoned object timeout in seconds + */ + public int getRemoveAbandonedTimeout() { + return this.removeAbandonedTimeout; + } + + /** + *

Sets the timeout in seconds before an abandoned object can be + * removed

+ * + *

Setting this property has no effect if + * {@link #getRemoveAbandonedOnBorrow() removeAbandonedOnBorrow} and + * {@link #getRemoveAbandonedOnMaintenance() removeAbandonedOnMaintenance} + * are both false.

+ * + * @param removeAbandonedTimeout new abandoned timeout in seconds + * @see #getRemoveAbandonedTimeout() + */ + public void setRemoveAbandonedTimeout(final int removeAbandonedTimeout) { + this.removeAbandonedTimeout = removeAbandonedTimeout; + } + + /** + * Determines whether or not to log stack traces for application code + * which abandoned an object. + */ + private boolean logAbandoned = false; + + /** + * Flag to log stack traces for application code which abandoned + * an object. + * + * Defaults to false. + * Logging of abandoned objects adds overhead for every object created + * because a stack trace has to be generated. + * + * @return boolean true if stack trace logging is turned on for abandoned + * objects + * + */ + public boolean getLogAbandoned() { + return this.logAbandoned; + } + + /** + * Sets the flag to log stack traces for application code which abandoned + * an object. + * + * @param logAbandoned true turns on abandoned stack trace logging + * @see #getLogAbandoned() + * + */ + public void setLogAbandoned(final boolean logAbandoned) { + this.logAbandoned = logAbandoned; + } + + /** + * Determines whether or not to log full stack traces when logAbandoned is true. + * If disabled, then a faster method for logging stack traces with only class data + * may be used if possible. + * + * @since 2.5 + */ + private boolean requireFullStackTrace = true; + + /** + * Indicates if full stack traces are required when {@link #getLogAbandoned() logAbandoned} + * is true. Defaults to true. Logging of abandoned objects requiring a full stack trace will + * generate an entire stack trace to generate for every object created. If this is disabled, + * a faster but less informative stack walking mechanism may be used if available. + * + * @return true if full stack traces are required for logging abandoned connections, or false + * if abbreviated stack traces are acceptable + * @see CallStack + * @since 2.5 + */ + public boolean getRequireFullStackTrace() { + return requireFullStackTrace; + } + + /** + * Sets the flag to require full stack traces for logging abandoned connections when enabled. + * + * @param requireFullStackTrace indicates whether or not full stack traces are required in + * abandoned connection logs + * @see CallStack + * @see #getRequireFullStackTrace() + * @since 2.5 + */ + public void setRequireFullStackTrace(boolean requireFullStackTrace) { + this.requireFullStackTrace = requireFullStackTrace; + } + + /** + * PrintWriter to use to log information on abandoned objects. + * Use of default system encoding is deliberate. + */ + private PrintWriter logWriter = new PrintWriter(System.out); + + /** + * Returns the log writer being used by this configuration to log + * information on abandoned objects. If not set, a PrintWriter based on + * System.out with the system default encoding is used. + * + * @return log writer in use + */ + public PrintWriter getLogWriter() { + return logWriter; + } + + /** + * Sets the log writer to be used by this configuration to log + * information on abandoned objects. + * + * @param logWriter The new log writer + */ + public void setLogWriter(final PrintWriter logWriter) { + this.logWriter = logWriter; + } + + /** + * If the pool implements {@link UsageTracking}, should the pool record a + * stack trace every time a method is called on a pooled object and retain + * the most recent stack trace to aid debugging of abandoned objects? + */ + private boolean useUsageTracking = false; + + /** + * If the pool implements {@link UsageTracking}, should the pool record a + * stack trace every time a method is called on a pooled object and retain + * the most recent stack trace to aid debugging of abandoned objects? + * + * @return true if usage tracking is enabled + */ + public boolean getUseUsageTracking() { + return useUsageTracking; + } + + /** + * If the pool implements {@link UsageTracking}, configure whether the pool + * should record a stack trace every time a method is called on a pooled + * object and retain the most recent stack trace to aid debugging of + * abandoned objects. + * + * @param useUsageTracking A value of true will enable + * the recording of a stack trace on every use + * of a pooled object + */ + public void setUseUsageTracking(final boolean useUsageTracking) { + this.useUsageTracking = useUsageTracking; + } + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("AbandonedConfig [removeAbandonedOnBorrow="); + builder.append(removeAbandonedOnBorrow); + builder.append(", removeAbandonedOnMaintenance="); + builder.append(removeAbandonedOnMaintenance); + builder.append(", removeAbandonedTimeout="); + builder.append(removeAbandonedTimeout); + builder.append(", logAbandoned="); + builder.append(logAbandoned); + builder.append(", logWriter="); + builder.append(logWriter); + builder.append(", useUsageTracking="); + builder.append(useUsageTracking); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseGenericObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseGenericObjectPool.java new file mode 100755 index 000000000..1f602a228 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseGenericObjectPool.java @@ -0,0 +1,1339 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.management.ManagementFactory; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Deque; +import java.util.Iterator; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; + +import com.fr.third.org.apache.commons.pool2.BaseObject; +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.SwallowedExceptionListener; + +/** + * Base class that provides common functionality for {@link GenericObjectPool} + * and {@link GenericKeyedObjectPool}. The primary reason this class exists is + * reduce code duplication between the two pool implementations. + * + * @param Type of element pooled in this pool. + * + * This class is intended to be thread-safe. + * + * @since 2.0 + */ +public abstract class BaseGenericObjectPool extends BaseObject { + + // Constants + /** + * The size of the caches used to store historical data for some attributes + * so that rolling means may be calculated. + */ + public static final int MEAN_TIMING_STATS_CACHE_SIZE = 100; + + // Configuration attributes + private volatile int maxTotal = + GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; + private volatile boolean blockWhenExhausted = + BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; + private volatile long maxWaitMillis = + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS; + private volatile boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO; + private final boolean fairness; + private volatile boolean testOnCreate = + BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; + private volatile boolean testOnBorrow = + BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; + private volatile boolean testOnReturn = + BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; + private volatile boolean testWhileIdle = + BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; + private volatile long timeBetweenEvictionRunsMillis = + BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + private volatile int numTestsPerEvictionRun = + BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; + private volatile long minEvictableIdleTimeMillis = + BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + private volatile long softMinEvictableIdleTimeMillis = + BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + private volatile EvictionPolicy evictionPolicy; + private volatile long evictorShutdownTimeoutMillis = + BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS; + + + // Internal (primarily state) attributes + final Object closeLock = new Object(); + volatile boolean closed = false; + final Object evictionLock = new Object(); + private Evictor evictor = null; // @GuardedBy("evictionLock") + EvictionIterator evictionIterator = null; // @GuardedBy("evictionLock") + /* + * Class loader for evictor thread to use since, in a JavaEE or similar + * environment, the context class loader for the evictor thread may not have + * visibility of the correct factory. See POOL-161. Uses a weak reference to + * avoid potential memory leaks if the Pool is discarded rather than closed. + */ + private final WeakReference factoryClassLoader; + + + // Monitoring (primarily JMX) attributes + private final ObjectName oname; + private final String creationStackTrace; + private final AtomicLong borrowedCount = new AtomicLong(0); + private final AtomicLong returnedCount = new AtomicLong(0); + final AtomicLong createdCount = new AtomicLong(0); + final AtomicLong destroyedCount = new AtomicLong(0); + final AtomicLong destroyedByEvictorCount = new AtomicLong(0); + final AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0); + private final StatsStore activeTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); + private final StatsStore idleTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); + private final StatsStore waitTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); + private final AtomicLong maxBorrowWaitTimeMillis = new AtomicLong(0L); + private volatile SwallowedExceptionListener swallowedExceptionListener = null; + + + /** + * Handles JMX registration (if required) and the initialization required for + * monitoring. + * + * @param config Pool configuration + * @param jmxNameBase The default base JMX name for the new pool unless + * overridden by the config + * @param jmxNamePrefix Prefix to be used for JMX name for the new pool + */ + public BaseGenericObjectPool(final BaseObjectPoolConfig config, + final String jmxNameBase, final String jmxNamePrefix) { + if (config.getJmxEnabled()) { + this.oname = jmxRegister(config, jmxNameBase, jmxNamePrefix); + } else { + this.oname = null; + } + + // Populate the creation stack trace + this.creationStackTrace = getStackTrace(new Exception()); + + // save the current TCCL (if any) to be used later by the evictor Thread + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + factoryClassLoader = null; + } else { + factoryClassLoader = new WeakReference(cl); + } + + fairness = config.getFairness(); + } + + + /** + * Returns the maximum number of objects that can be allocated by the pool + * (checked out to clients, or idle awaiting checkout) at a given time. When + * negative, there is no limit to the number of objects that can be + * managed by the pool at one time. + * + * @return the cap on the total number of object instances managed by the + * pool. + * + * @see #setMaxTotal + */ + public final int getMaxTotal() { + return maxTotal; + } + + /** + * Sets the cap on the number of objects that can be allocated by the pool + * (checked out to clients, or idle awaiting checkout) at a given time. Use + * a negative value for no limit. + * + * @param maxTotal The cap on the total number of object instances managed + * by the pool. Negative values mean that there is no limit + * to the number of objects allocated by the pool. + * + * @see #getMaxTotal + */ + public final void setMaxTotal(final int maxTotal) { + this.maxTotal = maxTotal; + } + + /** + * Returns whether to block when the borrowObject() method is + * invoked when the pool is exhausted (the maximum number of "active" + * objects has been reached). + * + * @return true if borrowObject() should block + * when the pool is exhausted + * + * @see #setBlockWhenExhausted + */ + public final boolean getBlockWhenExhausted() { + return blockWhenExhausted; + } + + /** + * Sets whether to block when the borrowObject() method is + * invoked when the pool is exhausted (the maximum number of "active" + * objects has been reached). + * + * @param blockWhenExhausted true if + * borrowObject() should block + * when the pool is exhausted + * + * @see #getBlockWhenExhausted + */ + public final void setBlockWhenExhausted(final boolean blockWhenExhausted) { + this.blockWhenExhausted = blockWhenExhausted; + } + + /** + * Returns the maximum amount of time (in milliseconds) the + * borrowObject() method should block before throwing an + * exception when the pool is exhausted and + * {@link #getBlockWhenExhausted} is true. When less than 0, the + * borrowObject() method may block indefinitely. + * + * @return the maximum number of milliseconds borrowObject() + * will block. + * + * @see #setMaxWaitMillis + * @see #setBlockWhenExhausted + */ + public final long getMaxWaitMillis() { + return maxWaitMillis; + } + + /** + * Sets the maximum amount of time (in milliseconds) the + * borrowObject() method should block before throwing an + * exception when the pool is exhausted and + * {@link #getBlockWhenExhausted} is true. When less than 0, the + * borrowObject() method may block indefinitely. + * + * @param maxWaitMillis the maximum number of milliseconds + * borrowObject() will block or negative + * for indefinitely. + * + * @see #getMaxWaitMillis + * @see #setBlockWhenExhausted + */ + public final void setMaxWaitMillis(final long maxWaitMillis) { + this.maxWaitMillis = maxWaitMillis; + } + + /** + * Returns whether the pool has LIFO (last in, first out) behaviour with + * respect to idle objects - always returning the most recently used object + * from the pool, or as a FIFO (first in, first out) queue, where the pool + * always returns the oldest object in the idle object pool. + * + * @return true if the pool is configured with LIFO behaviour + * or false if the pool is configured with FIFO + * behaviour + * + * @see #setLifo + */ + public final boolean getLifo() { + return lifo; + } + + /** + * Returns whether or not the pool serves threads waiting to borrow objects fairly. + * True means that waiting threads are served as if waiting in a FIFO queue. + * + * @return true if waiting threads are to be served + * by the pool in arrival order + */ + public final boolean getFairness() { + return fairness; + } + + /** + * Sets whether the pool has LIFO (last in, first out) behaviour with + * respect to idle objects - always returning the most recently used object + * from the pool, or as a FIFO (first in, first out) queue, where the pool + * always returns the oldest object in the idle object pool. + * + * @param lifo true if the pool is to be configured with LIFO + * behaviour or false if the pool is to be + * configured with FIFO behaviour + * + * @see #getLifo() + */ + public final void setLifo(final boolean lifo) { + this.lifo = lifo; + } + + /** + * Returns whether objects created for the pool will be validated before + * being returned from the borrowObject() method. Validation is + * performed by the validateObject() method of the factory + * associated with the pool. If the object fails to validate, then + * borrowObject() will fail. + * + * @return true if newly created objects are validated before + * being returned from the borrowObject() method + * + * @see #setTestOnCreate + * + * @since 2.2 + */ + public final boolean getTestOnCreate() { + return testOnCreate; + } + + /** + * Sets whether objects created for the pool will be validated before + * being returned from the borrowObject() method. Validation is + * performed by the validateObject() method of the factory + * associated with the pool. If the object fails to validate, then + * borrowObject() will fail. + * + * @param testOnCreate true if newly created objects should be + * validated before being returned from the + * borrowObject() method + * + * @see #getTestOnCreate + * + * @since 2.2 + */ + public final void setTestOnCreate(final boolean testOnCreate) { + this.testOnCreate = testOnCreate; + } + + /** + * Returns whether objects borrowed from the pool will be validated before + * being returned from the borrowObject() method. Validation is + * performed by the validateObject() method of the factory + * associated with the pool. If the object fails to validate, it will be + * removed from the pool and destroyed, and a new attempt will be made to + * borrow an object from the pool. + * + * @return true if objects are validated before being returned + * from the borrowObject() method + * + * @see #setTestOnBorrow + */ + public final boolean getTestOnBorrow() { + return testOnBorrow; + } + + /** + * Sets whether objects borrowed from the pool will be validated before + * being returned from the borrowObject() method. Validation is + * performed by the validateObject() method of the factory + * associated with the pool. If the object fails to validate, it will be + * removed from the pool and destroyed, and a new attempt will be made to + * borrow an object from the pool. + * + * @param testOnBorrow true if objects should be validated + * before being returned from the + * borrowObject() method + * + * @see #getTestOnBorrow + */ + public final void setTestOnBorrow(final boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + /** + * Returns whether objects borrowed from the pool will be validated when + * they are returned to the pool via the returnObject() method. + * Validation is performed by the validateObject() method of + * the factory associated with the pool. Returning objects that fail validation + * are destroyed rather then being returned the pool. + * + * @return true if objects are validated on return to + * the pool via the returnObject() method + * + * @see #setTestOnReturn + */ + public final boolean getTestOnReturn() { + return testOnReturn; + } + + /** + * Sets whether objects borrowed from the pool will be validated when + * they are returned to the pool via the returnObject() method. + * Validation is performed by the validateObject() method of + * the factory associated with the pool. Returning objects that fail validation + * are destroyed rather then being returned the pool. + * + * @param testOnReturn true if objects are validated on + * return to the pool via the + * returnObject() method + * + * @see #getTestOnReturn + */ + public final void setTestOnReturn(final boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + + /** + * Returns whether objects sitting idle in the pool will be validated by the + * idle object evictor (if any - see + * {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed + * by the validateObject() method of the factory associated + * with the pool. If the object fails to validate, it will be removed from + * the pool and destroyed. + * + * @return true if objects will be validated by the evictor + * + * @see #setTestWhileIdle + * @see #setTimeBetweenEvictionRunsMillis + */ + public final boolean getTestWhileIdle() { + return testWhileIdle; + } + + /** + * Returns whether objects sitting idle in the pool will be validated by the + * idle object evictor (if any - see + * {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed + * by the validateObject() method of the factory associated + * with the pool. If the object fails to validate, it will be removed from + * the pool and destroyed. Note that setting this property has no effect + * unless the idle object evictor is enabled by setting + * timeBetweenEvictionRunsMillis to a positive value. + * + * @param testWhileIdle + * true so objects will be validated by the evictor + * + * @see #getTestWhileIdle + * @see #setTimeBetweenEvictionRunsMillis + */ + public final void setTestWhileIdle(final boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + /** + * Returns the number of milliseconds to sleep between runs of the idle + * object evictor thread. When non-positive, no idle object evictor thread + * will be run. + * + * @return number of milliseconds to sleep between evictor runs + * + * @see #setTimeBetweenEvictionRunsMillis + */ + public final long getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + /** + * Sets the number of milliseconds to sleep between runs of the idle + * object evictor thread. When non-positive, no idle object evictor thread + * will be run. + * + * @param timeBetweenEvictionRunsMillis + * number of milliseconds to sleep between evictor runs + * + * @see #getTimeBetweenEvictionRunsMillis + */ + public final void setTimeBetweenEvictionRunsMillis( + final long timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + startEvictor(timeBetweenEvictionRunsMillis); + } + + /** + * Returns the maximum number of objects to examine during each run (if any) + * of the idle object evictor thread. When positive, the number of tests + * performed for a run will be the minimum of the configured value and the + * number of idle instances in the pool. When negative, the number of tests + * performed will be ceil({@link #getNumIdle}/ + * abs({@link #getNumTestsPerEvictionRun})) which means that when the + * value is -n roughly one nth of the idle objects will be + * tested per run. + * + * @return max number of objects to examine during each evictor run + * + * @see #setNumTestsPerEvictionRun + * @see #setTimeBetweenEvictionRunsMillis + */ + public final int getNumTestsPerEvictionRun() { + return numTestsPerEvictionRun; + } + + /** + * Sets the maximum number of objects to examine during each run (if any) + * of the idle object evictor thread. When positive, the number of tests + * performed for a run will be the minimum of the configured value and the + * number of idle instances in the pool. When negative, the number of tests + * performed will be ceil({@link #getNumIdle}/ + * abs({@link #getNumTestsPerEvictionRun})) which means that when the + * value is -n roughly one nth of the idle objects will be + * tested per run. + * + * @param numTestsPerEvictionRun + * max number of objects to examine during each evictor run + * + * @see #getNumTestsPerEvictionRun + * @see #setTimeBetweenEvictionRunsMillis + */ + public final void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) { + this.numTestsPerEvictionRun = numTestsPerEvictionRun; + } + + /** + * Returns the minimum amount of time an object may sit idle in the pool + * before it is eligible for eviction by the idle object evictor (if any - + * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, + * no objects will be evicted from the pool due to idle time alone. + * + * @return minimum amount of time an object may sit idle in the pool before + * it is eligible for eviction + * + * @see #setMinEvictableIdleTimeMillis + * @see #setTimeBetweenEvictionRunsMillis + */ + public final long getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + /** + * Sets the minimum amount of time an object may sit idle in the pool + * before it is eligible for eviction by the idle object evictor (if any - + * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, + * no objects will be evicted from the pool due to idle time alone. + * + * @param minEvictableIdleTimeMillis + * minimum amount of time an object may sit idle in the pool + * before it is eligible for eviction + * + * @see #getMinEvictableIdleTimeMillis + * @see #setTimeBetweenEvictionRunsMillis + */ + public final void setMinEvictableIdleTimeMillis( + final long minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + /** + * Returns the minimum amount of time an object may sit idle in the pool + * before it is eligible for eviction by the idle object evictor (if any - + * see {@link #setTimeBetweenEvictionRunsMillis(long)}), + * with the extra condition that at least minIdle object + * instances remain in the pool. This setting is overridden by + * {@link #getMinEvictableIdleTimeMillis} (that is, if + * {@link #getMinEvictableIdleTimeMillis} is positive, then + * {@link #getSoftMinEvictableIdleTimeMillis} is ignored). + * + * @return minimum amount of time an object may sit idle in the pool before + * it is eligible for eviction if minIdle instances are available + * + * @see #setSoftMinEvictableIdleTimeMillis + */ + public final long getSoftMinEvictableIdleTimeMillis() { + return softMinEvictableIdleTimeMillis; + } + + /** + * Sets the minimum amount of time an object may sit idle in the pool + * before it is eligible for eviction by the idle object evictor (if any - + * see {@link #setTimeBetweenEvictionRunsMillis(long)}), + * with the extra condition that at least minIdle object + * instances remain in the pool. This setting is overridden by + * {@link #getMinEvictableIdleTimeMillis} (that is, if + * {@link #getMinEvictableIdleTimeMillis} is positive, then + * {@link #getSoftMinEvictableIdleTimeMillis} is ignored). + * + * @param softMinEvictableIdleTimeMillis + * minimum amount of time an object may sit idle in the pool + * before it is eligible for eviction if minIdle instances are + * available + * + * @see #getSoftMinEvictableIdleTimeMillis + */ + public final void setSoftMinEvictableIdleTimeMillis( + final long softMinEvictableIdleTimeMillis) { + this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; + } + + /** + * Returns the name of the {@link EvictionPolicy} implementation that is + * used by this pool. + * + * @return The fully qualified class name of the {@link EvictionPolicy} + * + * @see #setEvictionPolicyClassName(String) + */ + public final String getEvictionPolicyClassName() { + return evictionPolicy.getClass().getName(); + } + + /** + * Sets the name of the {@link EvictionPolicy} implementation that is + * used by this pool. The Pool will attempt to load the class using the + * thread context class loader. If that fails, the Pool will attempt to load + * the class using the class loader that loaded this class. + * + * @param evictionPolicyClassName the fully qualified class name of the + * new eviction policy + * + * @see #getEvictionPolicyClassName() + */ + public final void setEvictionPolicyClassName( + final String evictionPolicyClassName) { + try { + Class clazz; + try { + clazz = Class.forName(evictionPolicyClassName, true, + Thread.currentThread().getContextClassLoader()); + } catch (final ClassNotFoundException e) { + clazz = Class.forName(evictionPolicyClassName); + } + final Object policy = clazz.getConstructor().newInstance(); + if (policy instanceof EvictionPolicy) { + @SuppressWarnings("unchecked") // safe, because we just checked the class + final + EvictionPolicy evicPolicy = (EvictionPolicy) policy; + this.evictionPolicy = evicPolicy; + } else { + throw new IllegalArgumentException("[" + evictionPolicyClassName + + "] does not implement EvictionPolicy"); + } + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException( + "Unable to create EvictionPolicy instance of type " + + evictionPolicyClassName, e); + } catch (final InstantiationException e) { + throw new IllegalArgumentException( + "Unable to create EvictionPolicy instance of type " + + evictionPolicyClassName, e); + } catch (final IllegalAccessException e) { + throw new IllegalArgumentException( + "Unable to create EvictionPolicy instance of type " + + evictionPolicyClassName, e); + } catch (final InvocationTargetException e) { + throw new IllegalArgumentException( + "Unable to create EvictionPolicy instance of type " + + evictionPolicyClassName, e); + } catch (final NoSuchMethodException e) { + throw new IllegalArgumentException( + "Unable to create EvictionPolicy instance of type " + + evictionPolicyClassName, e); + } + } + + /** + * Gets the timeout that will be used when waiting for the Evictor to + * shutdown if this pool is closed and it is the only pool still using the + * the value for the Evictor. + * + * @return The timeout in milliseconds that will be used while waiting for + * the Evictor to shut down. + */ + public final long getEvictorShutdownTimeoutMillis() { + return evictorShutdownTimeoutMillis; + } + + /** + * Sets the timeout that will be used when waiting for the Evictor to + * shutdown if this pool is closed and it is the only pool still using the + * the value for the Evictor. + * + * @param evictorShutdownTimeoutMillis the timeout in milliseconds that + * will be used while waiting for the + * Evictor to shut down. + */ + public final void setEvictorShutdownTimeoutMillis( + final long evictorShutdownTimeoutMillis) { + this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis; + } + + /** + * Closes the pool, destroys the remaining idle objects and, if registered + * in JMX, deregisters it. + */ + public abstract void close(); + + /** + * Has this pool instance been closed. + * @return true when this pool has been closed. + */ + public final boolean isClosed() { + return closed; + } + + /** + *

Perform numTests idle object eviction tests, evicting + * examined objects that meet the criteria for eviction. If + * testWhileIdle is true, examined objects are validated + * when visited (and removed if invalid); otherwise only objects that + * have been idle for more than minEvicableIdleTimeMillis + * are removed.

+ * + * @throws Exception when there is a problem evicting idle objects. + */ + public abstract void evict() throws Exception; + + /** + * Returns the {@link EvictionPolicy} defined for this pool. + * + * @return the eviction policy + * @since 2.4 + */ + protected EvictionPolicy getEvictionPolicy() { + return evictionPolicy; + } + + /** + * Verifies that the pool is open. + * @throws IllegalStateException if the pool is closed. + */ + final void assertOpen() throws IllegalStateException { + if (isClosed()) { + throw new IllegalStateException("Pool not open"); + } + } + + /** + *

Starts the evictor with the given delay. If there is an evictor + * running when this method is called, it is stopped and replaced with a + * new evictor with the specified delay.

+ * + *

This method needs to be final, since it is called from a constructor. + * See POOL-195.

+ * + * @param delay time in milliseconds before start and between eviction runs + */ + final void startEvictor(final long delay) { + synchronized (evictionLock) { + if (null != evictor) { + EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS); + evictor = null; + evictionIterator = null; + } + if (delay > 0) { + evictor = new Evictor(); + EvictionTimer.schedule(evictor, delay, delay); + } + } + } + + /** + * Tries to ensure that the configured minimum number of idle instances are + * available in the pool. + * @throws Exception if an error occurs creating idle instances + */ + abstract void ensureMinIdle() throws Exception; + + + // Monitoring (primarily JMX) related methods + + /** + * Provides the name under which the pool has been registered with the + * platform MBean server or null if the pool has not been + * registered. + * @return the JMX name + */ + public final ObjectName getJmxName() { + return oname; + } + + /** + * Provides the stack trace for the call that created this pool. JMX + * registration may trigger a memory leak so it is important that pools are + * deregistered when no longer used by calling the {@link #close()} method. + * This method is provided to assist with identifying code that creates but + * does not close it thereby creating a memory leak. + * @return pool creation stack trace + */ + public final String getCreationStackTrace() { + return creationStackTrace; + } + + /** + * The total number of objects successfully borrowed from this pool over the + * lifetime of the pool. + * @return the borrowed object count + */ + public final long getBorrowedCount() { + return borrowedCount.get(); + } + + /** + * The total number of objects returned to this pool over the lifetime of + * the pool. This excludes attempts to return the same object multiple + * times. + * @return the returned object count + */ + public final long getReturnedCount() { + return returnedCount.get(); + } + + /** + * The total number of objects created for this pool over the lifetime of + * the pool. + * @return the created object count + */ + public final long getCreatedCount() { + return createdCount.get(); + } + + /** + * The total number of objects destroyed by this pool over the lifetime of + * the pool. + * @return the destroyed object count + */ + public final long getDestroyedCount() { + return destroyedCount.get(); + } + + /** + * The total number of objects destroyed by the evictor associated with this + * pool over the lifetime of the pool. + * @return the evictor destroyed object count + */ + public final long getDestroyedByEvictorCount() { + return destroyedByEvictorCount.get(); + } + + /** + * The total number of objects destroyed by this pool as a result of failing + * validation during borrowObject() over the lifetime of the + * pool. + * @return validation destroyed object count + */ + public final long getDestroyedByBorrowValidationCount() { + return destroyedByBorrowValidationCount.get(); + } + + /** + * The mean time objects are active for based on the last {@link + * #MEAN_TIMING_STATS_CACHE_SIZE} objects returned to the pool. + * @return mean time an object has been checked out from the pool among + * recently returned objects + */ + public final long getMeanActiveTimeMillis() { + return activeTimes.getMean(); + } + + /** + * The mean time objects are idle for based on the last {@link + * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. + * @return mean time an object has been idle in the pool among recently + * borrowed objects + */ + public final long getMeanIdleTimeMillis() { + return idleTimes.getMean(); + } + + /** + * The mean time threads wait to borrow an object based on the last {@link + * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. + * @return mean time in milliseconds that a recently served thread has had + * to wait to borrow an object from the pool + */ + public final long getMeanBorrowWaitTimeMillis() { + return waitTimes.getMean(); + } + + /** + * The maximum time a thread has waited to borrow objects from the pool. + * @return maximum wait time in milliseconds since the pool was created + */ + public final long getMaxBorrowWaitTimeMillis() { + return maxBorrowWaitTimeMillis.get(); + } + + /** + * The number of instances currently idle in this pool. + * @return count of instances available for checkout from the pool + */ + public abstract int getNumIdle(); + + /** + * The listener used (if any) to receive notifications of exceptions + * unavoidably swallowed by the pool. + * + * @return The listener or null for no listener + */ + public final SwallowedExceptionListener getSwallowedExceptionListener() { + return swallowedExceptionListener; + } + + /** + * The listener used (if any) to receive notifications of exceptions + * unavoidably swallowed by the pool. + * + * @param swallowedExceptionListener The listener or null + * for no listener + */ + public final void setSwallowedExceptionListener( + final SwallowedExceptionListener swallowedExceptionListener) { + this.swallowedExceptionListener = swallowedExceptionListener; + } + + /** + * Swallows an exception and notifies the configured listener for swallowed + * exceptions queue. + * + * @param e exception to be swallowed + */ + final void swallowException(final Exception e) { + final SwallowedExceptionListener listener = getSwallowedExceptionListener(); + + if (listener == null) { + return; + } + + try { + listener.onSwallowException(e); + } catch (final OutOfMemoryError oome) { + throw oome; + } catch (final VirtualMachineError vme) { + throw vme; + } catch (final Throwable t) { + // Ignore. Enjoy the irony. + } + } + + /** + * Updates statistics after an object is borrowed from the pool. + * @param p object borrowed from the pool + * @param waitTime time (in milliseconds) that the borrowing thread had to wait + */ + final void updateStatsBorrow(final PooledObject p, final long waitTime) { + borrowedCount.incrementAndGet(); + idleTimes.add(p.getIdleTimeMillis()); + waitTimes.add(waitTime); + + // lock-free optimistic-locking maximum + long currentMax; + do { + currentMax = maxBorrowWaitTimeMillis.get(); + if (currentMax >= waitTime) { + break; + } + } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMax, waitTime)); + } + + /** + * Updates statistics after an object is returned to the pool. + * @param activeTime the amount of time (in milliseconds) that the returning + * object was checked out + */ + final void updateStatsReturn(final long activeTime) { + returnedCount.incrementAndGet(); + activeTimes.add(activeTime); + } + + /** + * Unregisters this pool's MBean. + */ + final void jmxUnregister() { + if (oname != null) { + try { + ManagementFactory.getPlatformMBeanServer().unregisterMBean( + oname); + } catch (final MBeanRegistrationException e) { + swallowException(e); + } catch (final InstanceNotFoundException e) { + swallowException(e); + } + } + } + + /** + * Registers the pool with the platform MBean server. + * The registered name will be + * jmxNameBase + jmxNamePrefix + i where i is the least + * integer greater than or equal to 1 such that the name is not already + * registered. Swallows MBeanRegistrationException, NotCompliantMBeanException + * returning null. + * + * @param config Pool configuration + * @param jmxNameBase default base JMX name for this pool + * @param jmxNamePrefix name prefix + * @return registered ObjectName, null if registration fails + */ + private ObjectName jmxRegister(final BaseObjectPoolConfig config, + final String jmxNameBase, String jmxNamePrefix) { + ObjectName objectName = null; + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + int i = 1; + boolean registered = false; + String base = config.getJmxNameBase(); + if (base == null) { + base = jmxNameBase; + } + while (!registered) { + try { + ObjectName objName; + // Skip the numeric suffix for the first pool in case there is + // only one so the names are cleaner. + if (i == 1) { + objName = new ObjectName(base + jmxNamePrefix); + } else { + objName = new ObjectName(base + jmxNamePrefix + i); + } + mbs.registerMBean(this, objName); + objectName = objName; + registered = true; + } catch (final MalformedObjectNameException e) { + if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals( + jmxNamePrefix) && jmxNameBase.equals(base)) { + // Shouldn't happen. Skip registration if it does. + registered = true; + } else { + // Must be an invalid name. Use the defaults instead. + jmxNamePrefix = + BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX; + base = jmxNameBase; + } + } catch (final InstanceAlreadyExistsException e) { + // Increment the index and try again + i++; + } catch (final MBeanRegistrationException e) { + // Shouldn't happen. Skip registration if it does. + registered = true; + } catch (final NotCompliantMBeanException e) { + // Shouldn't happen. Skip registration if it does. + registered = true; + } + } + return objectName; + } + + /** + * Gets the stack trace of an exception as a string. + * @param e exception to trace + * @return exception stack trace as a string + */ + private String getStackTrace(final Exception e) { + // Need the exception in string form to prevent the retention of + // references to classes in the stack trace that could trigger a memory + // leak in a container environment. + final Writer w = new StringWriter(); + final PrintWriter pw = new PrintWriter(w); + e.printStackTrace(pw); + return w.toString(); + } + + // Inner classes + + /** + * The idle object evictor {@link TimerTask}. + * + * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis + */ + class Evictor extends TimerTask { + /** + * Run pool maintenance. Evict objects qualifying for eviction and then + * ensure that the minimum number of idle instances are available. + * Since the Timer that invokes Evictors is shared for all Pools but + * pools may exist in different class loaders, the Evictor ensures that + * any actions taken are under the class loader of the factory + * associated with the pool. + */ + @Override + public void run() { + final ClassLoader savedClassLoader = + Thread.currentThread().getContextClassLoader(); + try { + if (factoryClassLoader != null) { + // Set the class loader for the factory + final ClassLoader cl = factoryClassLoader.get(); + if (cl == null) { + // The pool has been dereferenced and the class loader + // GC'd. Cancel this timer so the pool can be GC'd as + // well. + cancel(); + return; + } + Thread.currentThread().setContextClassLoader(cl); + } + + // Evict from the pool + try { + evict(); + } catch(final Exception e) { + swallowException(e); + } catch(final OutOfMemoryError oome) { + // Log problem but give evictor thread a chance to continue + // in case error is recoverable + oome.printStackTrace(System.err); + } + // Re-create idle instances. + try { + ensureMinIdle(); + } catch (final Exception e) { + swallowException(e); + } + } finally { + // Restore the previous CCL + Thread.currentThread().setContextClassLoader(savedClassLoader); + } + } + } + + /** + * Maintains a cache of values for a single metric and reports + * statistics on the cached values. + */ + private class StatsStore { + + private final AtomicLong values[]; + private final int size; + private int index; + + /** + * Create a StatsStore with the given cache size. + * + * @param size number of values to maintain in the cache. + */ + public StatsStore(final int size) { + this.size = size; + values = new AtomicLong[size]; + for (int i = 0; i < size; i++) { + values[i] = new AtomicLong(-1); + } + } + + /** + * Adds a value to the cache. If the cache is full, one of the + * existing values is replaced by the new value. + * + * @param value new value to add to the cache. + */ + public synchronized void add(final long value) { + values[index].set(value); + index++; + if (index == size) { + index = 0; + } + } + + /** + * Returns the mean of the cached values. + * + * @return the mean of the cache, truncated to long + */ + public long getMean() { + double result = 0; + int counter = 0; + for (int i = 0; i < size; i++) { + final long value = values[i].get(); + if (value != -1) { + counter++; + result = result * ((counter - 1) / (double) counter) + + value/(double) counter; + } + } + return (long) result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("StatsStore [values="); + builder.append(Arrays.toString(values)); + builder.append(", size="); + builder.append(size); + builder.append(", index="); + builder.append(index); + builder.append("]"); + return builder.toString(); + } + } + + /** + * The idle object eviction iterator. Holds a reference to the idle objects. + */ + class EvictionIterator implements Iterator> { + + private final Deque> idleObjects; + private final Iterator> idleObjectIterator; + + /** + * Create an EvictionIterator for the provided idle instance deque. + * @param idleObjects underlying deque + */ + EvictionIterator(final Deque> idleObjects) { + this.idleObjects = idleObjects; + + if (getLifo()) { + idleObjectIterator = idleObjects.descendingIterator(); + } else { + idleObjectIterator = idleObjects.iterator(); + } + } + + /** + * Returns the idle object deque referenced by this iterator. + * @return the idle object deque + */ + public Deque> getIdleObjects() { + return idleObjects; + } + + /** {@inheritDoc} */ + @Override + public boolean hasNext() { + return idleObjectIterator.hasNext(); + } + + /** {@inheritDoc} */ + @Override + public PooledObject next() { + return idleObjectIterator.next(); + } + + /** {@inheritDoc} */ + @Override + public void remove() { + idleObjectIterator.remove(); + } + + } + + /** + * Wrapper for objects under management by the pool. + * + * GenericObjectPool and GenericKeyedObjectPool maintain references to all + * objects under management using maps keyed on the objects. This wrapper + * class ensures that objects can work as hash keys. + * + * @param type of objects in the pool + */ + static class IdentityWrapper { + /** Wrapped object */ + private final T instance; + + /** + * Create a wrapper for an instance. + * + * @param instance object to wrap + */ + public IdentityWrapper(final T instance) { + this.instance = instance; + } + + @Override + public int hashCode() { + return System.identityHashCode(instance); + } + + @Override + @SuppressWarnings("rawtypes") + public boolean equals(final Object other) { + return other instanceof IdentityWrapper && + ((IdentityWrapper) other).instance == instance; + } + + /** + * @return the wrapped object + */ + public T getObject() { + return instance; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("IdentityWrapper [instance="); + builder.append(instance); + builder.append("]"); + return builder.toString(); + } + } + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + builder.append("maxTotal="); + builder.append(maxTotal); + builder.append(", blockWhenExhausted="); + builder.append(blockWhenExhausted); + builder.append(", maxWaitMillis="); + builder.append(maxWaitMillis); + builder.append(", lifo="); + builder.append(lifo); + builder.append(", fairness="); + builder.append(fairness); + builder.append(", testOnCreate="); + builder.append(testOnCreate); + builder.append(", testOnBorrow="); + builder.append(testOnBorrow); + builder.append(", testOnReturn="); + builder.append(testOnReturn); + builder.append(", testWhileIdle="); + builder.append(testWhileIdle); + builder.append(", timeBetweenEvictionRunsMillis="); + builder.append(timeBetweenEvictionRunsMillis); + builder.append(", numTestsPerEvictionRun="); + builder.append(numTestsPerEvictionRun); + builder.append(", minEvictableIdleTimeMillis="); + builder.append(minEvictableIdleTimeMillis); + builder.append(", softMinEvictableIdleTimeMillis="); + builder.append(softMinEvictableIdleTimeMillis); + builder.append(", evictionPolicy="); + builder.append(evictionPolicy); + builder.append(", closeLock="); + builder.append(closeLock); + builder.append(", closed="); + builder.append(closed); + builder.append(", evictionLock="); + builder.append(evictionLock); + builder.append(", evictor="); + builder.append(evictor); + builder.append(", evictionIterator="); + builder.append(evictionIterator); + builder.append(", factoryClassLoader="); + builder.append(factoryClassLoader); + builder.append(", oname="); + builder.append(oname); + builder.append(", creationStackTrace="); + builder.append(creationStackTrace); + builder.append(", borrowedCount="); + builder.append(borrowedCount); + builder.append(", returnedCount="); + builder.append(returnedCount); + builder.append(", createdCount="); + builder.append(createdCount); + builder.append(", destroyedCount="); + builder.append(destroyedCount); + builder.append(", destroyedByEvictorCount="); + builder.append(destroyedByEvictorCount); + builder.append(", destroyedByBorrowValidationCount="); + builder.append(destroyedByBorrowValidationCount); + builder.append(", activeTimes="); + builder.append(activeTimes); + builder.append(", idleTimes="); + builder.append(idleTimes); + builder.append(", waitTimes="); + builder.append(waitTimes); + builder.append(", maxBorrowWaitTimeMillis="); + builder.append(maxBorrowWaitTimeMillis); + builder.append(", swallowedExceptionListener="); + builder.append(swallowedExceptionListener); + } + +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java new file mode 100755 index 000000000..cfedfa15a --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java @@ -0,0 +1,718 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.BaseObject; + +/** + * Provides the implementation for the common attributes shared by the + * sub-classes. New instances of this class will be created using the defaults + * defined by the public constants. + *

+ * This class is not thread-safe. + * + * @since 2.0 + */ +public abstract class BaseObjectPoolConfig extends BaseObject implements Cloneable { + + /** + * The default value for the {@code lifo} configuration attribute. + * @see GenericObjectPool#getLifo() + * @see GenericKeyedObjectPool#getLifo() + */ + public static final boolean DEFAULT_LIFO = true; + + /** + * The default value for the {@code fairness} configuration attribute. + * @see GenericObjectPool#getFairness() + * @see GenericKeyedObjectPool#getFairness() + */ + public static final boolean DEFAULT_FAIRNESS = false; + + /** + * The default value for the {@code maxWait} configuration attribute. + * @see GenericObjectPool#getMaxWaitMillis() + * @see GenericKeyedObjectPool#getMaxWaitMillis() + */ + public static final long DEFAULT_MAX_WAIT_MILLIS = -1L; + + /** + * The default value for the {@code minEvictableIdleTimeMillis} + * configuration attribute. + * @see GenericObjectPool#getMinEvictableIdleTimeMillis() + * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() + */ + public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = + 1000L * 60L * 30L; + + /** + * The default value for the {@code softMinEvictableIdleTimeMillis} + * configuration attribute. + * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis() + * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis() + */ + public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1; + + /** + * The default value for {@code evictorShutdownTimeoutMillis} configuration + * attribute. + * @see GenericObjectPool#getEvictorShutdownTimeoutMillis() + * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis() + */ + public static final long DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS = + 10L * 1000L; + + /** + * The default value for the {@code numTestsPerEvictionRun} configuration + * attribute. + * @see GenericObjectPool#getNumTestsPerEvictionRun() + * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() + */ + public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3; + + /** + * The default value for the {@code testOnCreate} configuration attribute. + * @see GenericObjectPool#getTestOnCreate() + * @see GenericKeyedObjectPool#getTestOnCreate() + * + * @since 2.2 + */ + public static final boolean DEFAULT_TEST_ON_CREATE = false; + + /** + * The default value for the {@code testOnBorrow} configuration attribute. + * @see GenericObjectPool#getTestOnBorrow() + * @see GenericKeyedObjectPool#getTestOnBorrow() + */ + public static final boolean DEFAULT_TEST_ON_BORROW = false; + + /** + * The default value for the {@code testOnReturn} configuration attribute. + * @see GenericObjectPool#getTestOnReturn() + * @see GenericKeyedObjectPool#getTestOnReturn() + */ + public static final boolean DEFAULT_TEST_ON_RETURN = false; + + /** + * The default value for the {@code testWhileIdle} configuration attribute. + * @see GenericObjectPool#getTestWhileIdle() + * @see GenericKeyedObjectPool#getTestWhileIdle() + */ + public static final boolean DEFAULT_TEST_WHILE_IDLE = false; + + /** + * The default value for the {@code timeBetweenEvictionRunsMillis} + * configuration attribute. + * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() + * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() + */ + public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L; + + /** + * The default value for the {@code blockWhenExhausted} configuration + * attribute. + * @see GenericObjectPool#getBlockWhenExhausted() + * @see GenericKeyedObjectPool#getBlockWhenExhausted() + */ + public static final boolean DEFAULT_BLOCK_WHEN_EXHAUSTED = true; + + /** + * The default value for enabling JMX for pools created with a configuration + * instance. + */ + public static final boolean DEFAULT_JMX_ENABLE = true; + + /** + * The default value for the prefix used to name JMX enabled pools created + * with a configuration instance. + * @see GenericObjectPool#getJmxName() + * @see GenericKeyedObjectPool#getJmxName() + */ + public static final String DEFAULT_JMX_NAME_PREFIX = "pool"; + + /** + * The default value for the base name to use to name JMX enabled pools + * created with a configuration instance. The default is null + * which means the pool will provide the base name to use. + * @see GenericObjectPool#getJmxName() + * @see GenericKeyedObjectPool#getJmxName() + */ + public static final String DEFAULT_JMX_NAME_BASE = null; + + /** + * The default value for the {@code evictionPolicyClassName} configuration + * attribute. + * @see GenericObjectPool#getEvictionPolicyClassName() + * @see GenericKeyedObjectPool#getEvictionPolicyClassName() + */ + public static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = + "org.apache.commons.pool2.impl.DefaultEvictionPolicy"; + + + private boolean lifo = DEFAULT_LIFO; + + private boolean fairness = DEFAULT_FAIRNESS; + + private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS; + + private long minEvictableIdleTimeMillis = + DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + + private long evictorShutdownTimeoutMillis = + DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS; + + private long softMinEvictableIdleTimeMillis = + DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + + private int numTestsPerEvictionRun = + DEFAULT_NUM_TESTS_PER_EVICTION_RUN; + + private String evictionPolicyClassName = DEFAULT_EVICTION_POLICY_CLASS_NAME; + + private boolean testOnCreate = DEFAULT_TEST_ON_CREATE; + + private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW; + + private boolean testOnReturn = DEFAULT_TEST_ON_RETURN; + + private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE; + + private long timeBetweenEvictionRunsMillis = + DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + + private boolean blockWhenExhausted = DEFAULT_BLOCK_WHEN_EXHAUSTED; + + private boolean jmxEnabled = DEFAULT_JMX_ENABLE; + + // TODO Consider changing this to a single property for 3.x + private String jmxNamePrefix = DEFAULT_JMX_NAME_PREFIX; + + private String jmxNameBase = DEFAULT_JMX_NAME_BASE; + + + /** + * Get the value for the {@code lifo} configuration attribute for pools + * created with this configuration instance. + * + * @return The current setting of {@code lifo} for this configuration + * instance + * + * @see GenericObjectPool#getLifo() + * @see GenericKeyedObjectPool#getLifo() + */ + public boolean getLifo() { + return lifo; + } + + /** + * Get the value for the {@code fairness} configuration attribute for pools + * created with this configuration instance. + * + * @return The current setting of {@code fairness} for this configuration + * instance + * + * @see GenericObjectPool#getFairness() + * @see GenericKeyedObjectPool#getFairness() + */ + public boolean getFairness() { + return fairness; + } + + /** + * Set the value for the {@code lifo} configuration attribute for pools + * created with this configuration instance. + * + * @param lifo The new setting of {@code lifo} + * for this configuration instance + * + * @see GenericObjectPool#getLifo() + * @see GenericKeyedObjectPool#getLifo() + */ + public void setLifo(final boolean lifo) { + this.lifo = lifo; + } + + /** + * Set the value for the {@code fairness} configuration attribute for pools + * created with this configuration instance. + * + * @param fairness The new setting of {@code fairness} + * for this configuration instance + * + * @see GenericObjectPool#getFairness() + * @see GenericKeyedObjectPool#getFairness() + */ + public void setFairness(final boolean fairness) { + this.fairness = fairness; + } + + /** + * Get the value for the {@code maxWait} configuration attribute for pools + * created with this configuration instance. + * + * @return The current setting of {@code maxWait} for this + * configuration instance + * + * @see GenericObjectPool#getMaxWaitMillis() + * @see GenericKeyedObjectPool#getMaxWaitMillis() + */ + public long getMaxWaitMillis() { + return maxWaitMillis; + } + + /** + * Set the value for the {@code maxWait} configuration attribute for pools + * created with this configuration instance. + * + * @param maxWaitMillis The new setting of {@code maxWaitMillis} + * for this configuration instance + * + * @see GenericObjectPool#getMaxWaitMillis() + * @see GenericKeyedObjectPool#getMaxWaitMillis() + */ + public void setMaxWaitMillis(final long maxWaitMillis) { + this.maxWaitMillis = maxWaitMillis; + } + + /** + * Get the value for the {@code minEvictableIdleTimeMillis} configuration + * attribute for pools created with this configuration instance. + * + * @return The current setting of {@code minEvictableIdleTimeMillis} for + * this configuration instance + * + * @see GenericObjectPool#getMinEvictableIdleTimeMillis() + * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() + */ + public long getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + /** + * Set the value for the {@code minEvictableIdleTimeMillis} configuration + * attribute for pools created with this configuration instance. + * + * @param minEvictableIdleTimeMillis The new setting of + * {@code minEvictableIdleTimeMillis} for this configuration instance + * + * @see GenericObjectPool#getMinEvictableIdleTimeMillis() + * @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() + */ + public void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + /** + * Get the value for the {@code softMinEvictableIdleTimeMillis} + * configuration attribute for pools created with this configuration + * instance. + * + * @return The current setting of {@code softMinEvictableIdleTimeMillis} + * for this configuration instance + * + * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis() + * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis() + */ + public long getSoftMinEvictableIdleTimeMillis() { + return softMinEvictableIdleTimeMillis; + } + + /** + * Set the value for the {@code softMinEvictableIdleTimeMillis} + * configuration attribute for pools created with this configuration + * instance. + * + * @param softMinEvictableIdleTimeMillis The new setting of + * {@code softMinEvictableIdleTimeMillis} for this configuration + * instance + * + * @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis() + * @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis() + */ + public void setSoftMinEvictableIdleTimeMillis( + final long softMinEvictableIdleTimeMillis) { + this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; + } + + /** + * Get the value for the {@code numTestsPerEvictionRun} configuration + * attribute for pools created with this configuration instance. + * + * @return The current setting of {@code numTestsPerEvictionRun} for this + * configuration instance + * + * @see GenericObjectPool#getNumTestsPerEvictionRun() + * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() + */ + public int getNumTestsPerEvictionRun() { + return numTestsPerEvictionRun; + } + + /** + * Set the value for the {@code numTestsPerEvictionRun} configuration + * attribute for pools created with this configuration instance. + * + * @param numTestsPerEvictionRun The new setting of + * {@code numTestsPerEvictionRun} for this configuration instance + * + * @see GenericObjectPool#getNumTestsPerEvictionRun() + * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() + */ + public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) { + this.numTestsPerEvictionRun = numTestsPerEvictionRun; + } + + /** + * Get the value for the {@code evictorShutdownTimeoutMillis} configuration + * attribute for pools created with this configuration instance. + * + * @return The current setting of {@code evictorShutdownTimeoutMillis} for + * this configuration instance + * + * @see GenericObjectPool#getEvictorShutdownTimeoutMillis() + * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis() + */ + public long getEvictorShutdownTimeoutMillis() { + return evictorShutdownTimeoutMillis; + } + + /** + * Set the value for the {@code evictorShutdownTimeoutMillis} configuration + * attribute for pools created with this configuration instance. + * + * @param evictorShutdownTimeoutMillis The new setting of + * {@code evictorShutdownTimeoutMillis} for this configuration + * instance + * + * @see GenericObjectPool#getEvictorShutdownTimeoutMillis() + * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis() + */ + public void setEvictorShutdownTimeoutMillis( + final long evictorShutdownTimeoutMillis) { + this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis; + } + + /** + * Get the value for the {@code testOnCreate} configuration attribute for + * pools created with this configuration instance. + * + * @return The current setting of {@code testOnCreate} for this + * configuration instance + * + * @see GenericObjectPool#getTestOnCreate() + * @see GenericKeyedObjectPool#getTestOnCreate() + * + * @since 2.2 + */ + public boolean getTestOnCreate() { + return testOnCreate; + } + + /** + * Set the value for the {@code testOnCreate} configuration attribute for + * pools created with this configuration instance. + * + * @param testOnCreate The new setting of {@code testOnCreate} + * for this configuration instance + * + * @see GenericObjectPool#getTestOnCreate() + * @see GenericKeyedObjectPool#getTestOnCreate() + * + * @since 2.2 + */ + public void setTestOnCreate(final boolean testOnCreate) { + this.testOnCreate = testOnCreate; + } + + /** + * Get the value for the {@code testOnBorrow} configuration attribute for + * pools created with this configuration instance. + * + * @return The current setting of {@code testOnBorrow} for this + * configuration instance + * + * @see GenericObjectPool#getTestOnBorrow() + * @see GenericKeyedObjectPool#getTestOnBorrow() + */ + public boolean getTestOnBorrow() { + return testOnBorrow; + } + + /** + * Set the value for the {@code testOnBorrow} configuration attribute for + * pools created with this configuration instance. + * + * @param testOnBorrow The new setting of {@code testOnBorrow} + * for this configuration instance + * + * @see GenericObjectPool#getTestOnBorrow() + * @see GenericKeyedObjectPool#getTestOnBorrow() + */ + public void setTestOnBorrow(final boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + /** + * Get the value for the {@code testOnReturn} configuration attribute for + * pools created with this configuration instance. + * + * @return The current setting of {@code testOnReturn} for this + * configuration instance + * + * @see GenericObjectPool#getTestOnReturn() + * @see GenericKeyedObjectPool#getTestOnReturn() + */ + public boolean getTestOnReturn() { + return testOnReturn; + } + + /** + * Set the value for the {@code testOnReturn} configuration attribute for + * pools created with this configuration instance. + * + * @param testOnReturn The new setting of {@code testOnReturn} + * for this configuration instance + * + * @see GenericObjectPool#getTestOnReturn() + * @see GenericKeyedObjectPool#getTestOnReturn() + */ + public void setTestOnReturn(final boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + + /** + * Get the value for the {@code testWhileIdle} configuration attribute for + * pools created with this configuration instance. + * + * @return The current setting of {@code testWhileIdle} for this + * configuration instance + * + * @see GenericObjectPool#getTestWhileIdle() + * @see GenericKeyedObjectPool#getTestWhileIdle() + */ + public boolean getTestWhileIdle() { + return testWhileIdle; + } + + /** + * Set the value for the {@code testWhileIdle} configuration attribute for + * pools created with this configuration instance. + * + * @param testWhileIdle The new setting of {@code testWhileIdle} + * for this configuration instance + * + * @see GenericObjectPool#getTestWhileIdle() + * @see GenericKeyedObjectPool#getTestWhileIdle() + */ + public void setTestWhileIdle(final boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + /** + * Get the value for the {@code timeBetweenEvictionRunsMillis} configuration + * attribute for pools created with this configuration instance. + * + * @return The current setting of {@code timeBetweenEvictionRunsMillis} for + * this configuration instance + * + * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() + * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() + */ + public long getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + /** + * Set the value for the {@code timeBetweenEvictionRunsMillis} configuration + * attribute for pools created with this configuration instance. + * + * @param timeBetweenEvictionRunsMillis The new setting of + * {@code timeBetweenEvictionRunsMillis} for this configuration + * instance + * + * @see GenericObjectPool#getTimeBetweenEvictionRunsMillis() + * @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() + */ + public void setTimeBetweenEvictionRunsMillis( + final long timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + /** + * Get the value for the {@code evictionPolicyClassName} configuration + * attribute for pools created with this configuration instance. + * + * @return The current setting of {@code evictionPolicyClassName} for this + * configuration instance + * + * @see GenericObjectPool#getEvictionPolicyClassName() + * @see GenericKeyedObjectPool#getEvictionPolicyClassName() + */ + public String getEvictionPolicyClassName() { + return evictionPolicyClassName; + } + + /** + * Set the value for the {@code evictionPolicyClassName} configuration + * attribute for pools created with this configuration instance. + * + * @param evictionPolicyClassName The new setting of + * {@code evictionPolicyClassName} for this configuration instance + * + * @see GenericObjectPool#getEvictionPolicyClassName() + * @see GenericKeyedObjectPool#getEvictionPolicyClassName() + */ + public void setEvictionPolicyClassName(final String evictionPolicyClassName) { + this.evictionPolicyClassName = evictionPolicyClassName; + } + + /** + * Get the value for the {@code blockWhenExhausted} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code blockWhenExhausted} for this + * configuration instance + * + * @see GenericObjectPool#getBlockWhenExhausted() + * @see GenericKeyedObjectPool#getBlockWhenExhausted() + */ + public boolean getBlockWhenExhausted() { + return blockWhenExhausted; + } + + /** + * Set the value for the {@code blockWhenExhausted} configuration attribute + * for pools created with this configuration instance. + * + * @param blockWhenExhausted The new setting of {@code blockWhenExhausted} + * for this configuration instance + * + * @see GenericObjectPool#getBlockWhenExhausted() + * @see GenericKeyedObjectPool#getBlockWhenExhausted() + */ + public void setBlockWhenExhausted(final boolean blockWhenExhausted) { + this.blockWhenExhausted = blockWhenExhausted; + } + + /** + * Gets the value of the flag that determines if JMX will be enabled for + * pools created with this configuration instance. + * + * @return The current setting of {@code jmxEnabled} for this configuration + * instance + */ + public boolean getJmxEnabled() { + return jmxEnabled; + } + + /** + * Sets the value of the flag that determines if JMX will be enabled for + * pools created with this configuration instance. + * + * @param jmxEnabled The new setting of {@code jmxEnabled} + * for this configuration instance + */ + public void setJmxEnabled(final boolean jmxEnabled) { + this.jmxEnabled = jmxEnabled; + } + + /** + * Gets the value of the JMX name base that will be used as part of the + * name assigned to JMX enabled pools created with this configuration + * instance. A value of null means that the pool will define + * the JMX name base. + * + * @return The current setting of {@code jmxNameBase} for this + * configuration instance + */ + public String getJmxNameBase() { + return jmxNameBase; + } + + /** + * Sets the value of the JMX name base that will be used as part of the + * name assigned to JMX enabled pools created with this configuration + * instance. A value of null means that the pool will define + * the JMX name base. + * + * @param jmxNameBase The new setting of {@code jmxNameBase} + * for this configuration instance + */ + public void setJmxNameBase(final String jmxNameBase) { + this.jmxNameBase = jmxNameBase; + } + + /** + * Gets the value of the JMX name prefix that will be used as part of the + * name assigned to JMX enabled pools created with this configuration + * instance. + * + * @return The current setting of {@code jmxNamePrefix} for this + * configuration instance + */ + public String getJmxNamePrefix() { + return jmxNamePrefix; + } + + /** + * Sets the value of the JMX name prefix that will be used as part of the + * name assigned to JMX enabled pools created with this configuration + * instance. + * + * @param jmxNamePrefix The new setting of {@code jmxNamePrefix} + * for this configuration instance + */ + public void setJmxNamePrefix(final String jmxNamePrefix) { + this.jmxNamePrefix = jmxNamePrefix; + } + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + builder.append("lifo="); + builder.append(lifo); + builder.append(", fairness="); + builder.append(fairness); + builder.append(", maxWaitMillis="); + builder.append(maxWaitMillis); + builder.append(", minEvictableIdleTimeMillis="); + builder.append(minEvictableIdleTimeMillis); + builder.append(", softMinEvictableIdleTimeMillis="); + builder.append(softMinEvictableIdleTimeMillis); + builder.append(", numTestsPerEvictionRun="); + builder.append(numTestsPerEvictionRun); + builder.append(", evictionPolicyClassName="); + builder.append(evictionPolicyClassName); + builder.append(", testOnCreate="); + builder.append(testOnCreate); + builder.append(", testOnBorrow="); + builder.append(testOnBorrow); + builder.append(", testOnReturn="); + builder.append(testOnReturn); + builder.append(", testWhileIdle="); + builder.append(testWhileIdle); + builder.append(", timeBetweenEvictionRunsMillis="); + builder.append(timeBetweenEvictionRunsMillis); + builder.append(", blockWhenExhausted="); + builder.append(blockWhenExhausted); + builder.append(", jmxEnabled="); + builder.append(jmxEnabled); + builder.append(", jmxNamePrefix="); + builder.append(jmxNamePrefix); + builder.append(", jmxNameBase="); + builder.append(jmxNameBase); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStack.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStack.java new file mode 100755 index 000000000..7b757cf63 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStack.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +import java.io.PrintWriter; + +/** + * Strategy for obtaining and printing the current call stack. This is primarily useful for + * {@linkplain UsageTracking usage tracking} so that different JVMs and configurations can use more efficient strategies + * for obtaining the current call stack depending on metadata needs. + * + * @see CallStackUtils + * @since 2.4.3 + */ +public interface CallStack { + + /** + * Prints the current stack trace if available to a PrintWriter. The format is undefined and is primarily useful + * for debugging issues with {@link PooledObject} usage in user code. + * + * @param writer a PrintWriter to write the current stack trace to if available + * @return true if a stack trace was available to print or false if nothing was printed + */ + boolean printStackTrace(final PrintWriter writer); + + /** + * Takes a snapshot of the current call stack. Subsequent calls to {@link #printStackTrace(PrintWriter)} will print + * out that stack trace until it is {@linkplain #clear() cleared}. + */ + void fillInStackTrace(); + + /** + * Clears the current stack trace snapshot. Subsequent calls to {@link #printStackTrace(PrintWriter)} will be + * no-ops until another call to {@link #fillInStackTrace()}. + */ + void clear(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStackUtils.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStackUtils.java new file mode 100755 index 000000000..2ab2152ba --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/CallStackUtils.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.security.AccessControlException; + +/** + * Utility methods for {@link CallStack}. + * + * @since 2.4.3 + */ +public final class CallStackUtils { + + private static final boolean CAN_CREATE_SECURITY_MANAGER; + + static { + CAN_CREATE_SECURITY_MANAGER = canCreateSecurityManager(); + } + + /** + * @return {@code true} if it is able to create a security manager in the current environment, {@code false} + * otherwise. + */ + private static boolean canCreateSecurityManager() { + final SecurityManager manager = System.getSecurityManager(); + if (manager == null) { + return true; + } + try { + manager.checkPermission(new RuntimePermission("createSecurityManager")); + return true; + } catch (final AccessControlException ignored) { + return false; + } + } + + /** + * Constructs a new {@link CallStack} using the fastest allowed strategy. + * + * @param messageFormat message (or format) to print first in stack traces + * @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp; otherwise, + * print message format literally + * @return a new CallStack + * @deprecated use {@link #newCallStack(String, boolean, boolean)} + */ + @Deprecated + public static CallStack newCallStack(final String messageFormat, final boolean useTimestamp) { + return newCallStack(messageFormat, useTimestamp, false); + } + + /** + * Constructs a new {@link CallStack} using the fasted allowed strategy. + * + * @param messageFormat message (or format) to print first in stack traces + * @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp; + * otherwise, print message format literally + * @param requireFullStackTrace if true, forces the use of a stack walking mechanism that includes full stack trace + * information; otherwise, uses a faster implementation if possible + * @return a new CallStack + * @since 2.5 + */ + public static CallStack newCallStack(final String messageFormat, + final boolean useTimestamp, + final boolean requireFullStackTrace) { + return CAN_CREATE_SECURITY_MANAGER && !requireFullStackTrace + ? new SecurityManagerCallStack(messageFormat, useTimestamp) + : new ThrowableCallStack(messageFormat, useTimestamp); + } + + /** + * Hidden constructor. + */ + private CallStackUtils() { + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java new file mode 100755 index 000000000..d5e2c333b --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.PooledObject; + +/** + * Provides the default implementation of {@link EvictionPolicy} used by the + * pools. Objects will be evicted if the following conditions are met: + *

    + *
  • the object has been idle longer than + * {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} / + * {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()}
  • + *
  • there are more than {@link GenericObjectPool#getMinIdle()} / + * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} idle objects in + * the pool and the object has been idle for longer than + * {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} / + * {@link GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()} + *
+ * This class is immutable and thread-safe. + * + * @param the type of objects in the pool + * + * @since 2.0 + */ +public class DefaultEvictionPolicy implements EvictionPolicy { + + @Override + public boolean evict(final EvictionConfig config, final PooledObject underTest, + final int idleCount) { + + if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() && + config.getMinIdle() < idleCount) || + config.getIdleEvictTime() < underTest.getIdleTimeMillis()) { + return true; + } + return false; + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObject.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObject.java new file mode 100755 index 000000000..70f37578f --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObject.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.PooledObjectState; +import com.fr.third.org.apache.commons.pool2.TrackedUse; + +import java.io.PrintWriter; +import java.util.Deque; + +/** + * This wrapper is used to track the additional information, such as state, for + * the pooled objects. + *

+ * This class is intended to be thread-safe. + * + * @param the type of object in the pool + * + * @since 2.0 + */ +public class DefaultPooledObject implements PooledObject { + + private final T object; + private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid + private final long createTime = System.currentTimeMillis(); + private volatile long lastBorrowTime = createTime; + private volatile long lastUseTime = createTime; + private volatile long lastReturnTime = createTime; + private volatile boolean logAbandoned = false; + private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE; + private volatile CallStack usedBy = NoOpCallStack.INSTANCE; + private volatile long borrowedCount = 0; + + /** + * Create a new instance that wraps the provided object so that the pool can + * track the state of the pooled object. + * + * @param object The object to wrap + */ + public DefaultPooledObject(final T object) { + this.object = object; + } + + @Override + public T getObject() { + return object; + } + + @Override + public long getCreateTime() { + return createTime; + } + + @Override + public long getActiveTimeMillis() { + // Take copies to avoid threading issues + final long rTime = lastReturnTime; + final long bTime = lastBorrowTime; + + if (rTime > bTime) { + return rTime - bTime; + } + return System.currentTimeMillis() - bTime; + } + + @Override + public long getIdleTimeMillis() { + final long elapsed = System.currentTimeMillis() - lastReturnTime; + // elapsed may be negative if: + // - another thread updates lastReturnTime during the calculation window + // - System.currentTimeMillis() is not monotonic (e.g. system time is set back) + return elapsed >= 0 ? elapsed : 0; + } + + @Override + public long getLastBorrowTime() { + return lastBorrowTime; + } + + @Override + public long getLastReturnTime() { + return lastReturnTime; + } + + /** + * Get the number of times this object has been borrowed. + * @return The number of times this object has been borrowed. + * @since 2.1 + */ + public long getBorrowedCount() { + return borrowedCount; + } + + /** + * Return an estimate of the last time this object was used. If the class + * of the pooled object implements {@link TrackedUse}, what is returned is + * the maximum of {@link TrackedUse#getLastUsed()} and + * {@link #getLastBorrowTime()}; otherwise this method gives the same + * value as {@link #getLastBorrowTime()}. + * + * @return the last time this object was used + */ + @Override + public long getLastUsedTime() { + if (object instanceof TrackedUse) { + return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime); + } + return lastUseTime; + } + + @Override + public int compareTo(final PooledObject other) { + final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime(); + if (lastActiveDiff == 0) { + // Make sure the natural ordering is broadly consistent with equals + // although this will break down if distinct objects have the same + // identity hash code. + // see java.lang.Comparable Javadocs + return System.identityHashCode(this) - System.identityHashCode(other); + } + // handle int overflow + return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE); + } + + @Override + public String toString() { + final StringBuilder result = new StringBuilder(); + result.append("Object: "); + result.append(object.toString()); + result.append(", State: "); + synchronized (this) { + result.append(state.toString()); + } + return result.toString(); + // TODO add other attributes + } + + @Override + public synchronized boolean startEvictionTest() { + if (state == PooledObjectState.IDLE) { + state = PooledObjectState.EVICTION; + return true; + } + + return false; + } + + @Override + public synchronized boolean endEvictionTest( + final Deque> idleQueue) { + if (state == PooledObjectState.EVICTION) { + state = PooledObjectState.IDLE; + return true; + } else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) { + state = PooledObjectState.IDLE; + if (!idleQueue.offerFirst(this)) { + // TODO - Should never happen + } + } + + return false; + } + + /** + * Allocates the object. + * + * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} + */ + @Override + public synchronized boolean allocate() { + if (state == PooledObjectState.IDLE) { + state = PooledObjectState.ALLOCATED; + lastBorrowTime = System.currentTimeMillis(); + lastUseTime = lastBorrowTime; + borrowedCount++; + if (logAbandoned) { + borrowedBy.fillInStackTrace(); + } + return true; + } else if (state == PooledObjectState.EVICTION) { + // TODO Allocate anyway and ignore eviction test + state = PooledObjectState.EVICTION_RETURN_TO_HEAD; + return false; + } + // TODO if validating and testOnBorrow == true then pre-allocate for + // performance + return false; + } + + /** + * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE} + * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}. + * + * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED} + */ + @Override + public synchronized boolean deallocate() { + if (state == PooledObjectState.ALLOCATED || + state == PooledObjectState.RETURNING) { + state = PooledObjectState.IDLE; + lastReturnTime = System.currentTimeMillis(); + borrowedBy.clear(); + return true; + } + + return false; + } + + /** + * Sets the state to {@link PooledObjectState#INVALID INVALID} + */ + @Override + public synchronized void invalidate() { + state = PooledObjectState.INVALID; + } + + @Override + public void use() { + lastUseTime = System.currentTimeMillis(); + usedBy.fillInStackTrace(); + } + + @Override + public void printStackTrace(final PrintWriter writer) { + boolean written = borrowedBy.printStackTrace(writer); + written |= usedBy.printStackTrace(writer); + if (written) { + writer.flush(); + } + } + + /** + * Returns the state of this object. + * @return state + */ + @Override + public synchronized PooledObjectState getState() { + return state; + } + + /** + * Marks the pooled object as abandoned. + */ + @Override + public synchronized void markAbandoned() { + state = PooledObjectState.ABANDONED; + } + + /** + * Marks the object as returning to the pool. + */ + @Override + public synchronized void markReturning() { + state = PooledObjectState.RETURNING; + } + + @Override + public void setLogAbandoned(final boolean logAbandoned) { + this.logAbandoned = logAbandoned; + } + + /** + * Configures the stack trace generation strategy based on whether or not fully + * detailed stack traces are required. When set to false, abandoned logs may + * only include caller class information rather than method names, line numbers, + * and other normal metadata available in a full stack trace. + * + * @param requireFullStackTrace the new configuration setting for abandoned object + * logging + * @since 2.5 + */ + // TODO: uncomment below in 3.0 + // @Override + public void setRequireFullStackTrace(final boolean requireFullStackTrace) { + borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + + "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", + true, requireFullStackTrace); + usedBy = CallStackUtils.newCallStack("The last code to use this object was:", + false, requireFullStackTrace); + } + +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfo.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfo.java new file mode 100755 index 000000000..29825b33c --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfo.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; + +import com.fr.third.org.apache.commons.pool2.PooledObject; + +/** + * Implementation of object that is used to provide information on pooled + * objects via JMX. + * + * @since 2.0 + */ +public class DefaultPooledObjectInfo implements DefaultPooledObjectInfoMBean { + + private final PooledObject pooledObject; + + /** + * Create a new instance for the given pooled object. + * + * @param pooledObject The pooled object that this instance will represent + */ + public DefaultPooledObjectInfo(final PooledObject pooledObject) { + this.pooledObject = pooledObject; + } + + @Override + public long getCreateTime() { + return pooledObject.getCreateTime(); + } + + @Override + public String getCreateTimeFormatted() { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + return sdf.format(Long.valueOf(pooledObject.getCreateTime())); + } + + @Override + public long getLastBorrowTime() { + return pooledObject.getLastBorrowTime(); + } + + @Override + public String getLastBorrowTimeFormatted() { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + return sdf.format(Long.valueOf(pooledObject.getLastBorrowTime())); + } + + @Override + public String getLastBorrowTrace() { + final StringWriter sw = new StringWriter(); + pooledObject.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + + @Override + public long getLastReturnTime() { + return pooledObject.getLastReturnTime(); + } + + @Override + public String getLastReturnTimeFormatted() { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + return sdf.format(Long.valueOf(pooledObject.getLastReturnTime())); + } + + @Override + public String getPooledObjectType() { + return pooledObject.getObject().getClass().getName(); + } + + @Override + public String getPooledObjectToString() { + return pooledObject.getObject().toString(); + } + + @Override + public long getBorrowedCount() { + // TODO Simplify this once getBorrowedCount has been added to PooledObject + if (pooledObject instanceof DefaultPooledObject) { + return ((DefaultPooledObject) pooledObject).getBorrowedCount(); + } + return -1; + } + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("DefaultPooledObjectInfo [pooledObject="); + builder.append(pooledObject); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfoMBean.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfoMBean.java new file mode 100755 index 000000000..d5e48dbee --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/DefaultPooledObjectInfoMBean.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +/** + * The interface that defines the information about pooled objects that will be + * exposed via JMX. + * + * NOTE: This interface exists only to define those attributes and methods that + * will be made available via JMX. It must not be implemented by clients + * as it is subject to change between major, minor and patch version + * releases of commons pool. Clients that implement this interface may + * not, therefore, be able to upgrade to a new minor or patch release + * without requiring code changes. + * + * @since 2.0 + */ +public interface DefaultPooledObjectInfoMBean { + /** + * Obtain the time (using the same basis as + * {@link System#currentTimeMillis()}) that pooled object was created. + * + * @return The creation time for the pooled object + */ + long getCreateTime(); + + /** + * Obtain the time that pooled object was created. + * + * @return The creation time for the pooled object formatted as + * yyyy-MM-dd HH:mm:ss Z + */ + String getCreateTimeFormatted(); + + /** + * Obtain the time (using the same basis as + * {@link System#currentTimeMillis()}) the polled object was last borrowed. + * + * @return The time the pooled object was last borrowed + */ + long getLastBorrowTime(); + + /** + * Obtain the time that pooled object was last borrowed. + * + * @return The last borrowed time for the pooled object formated as + * yyyy-MM-dd HH:mm:ss Z + */ + String getLastBorrowTimeFormatted(); + + /** + * Obtain the stack trace recorded when the pooled object was last borrowed. + * + * @return The stack trace showing which code last borrowed the pooled + * object + */ + String getLastBorrowTrace(); + + + /** + * Obtain the time (using the same basis as + * {@link System#currentTimeMillis()})the wrapped object was last returned. + * + * @return The time the object was last returned + */ + long getLastReturnTime(); + + /** + * Obtain the time that pooled object was last returned. + * + * @return The last returned time for the pooled object formated as + * yyyy-MM-dd HH:mm:ss Z + */ + String getLastReturnTimeFormatted(); + + /** + * Obtain the name of the class of the pooled object. + * + * @return The pooled object's class name + * + * @see Class#getName() + */ + String getPooledObjectType(); + + /** + * Provides a String form of the wrapper for debug purposes. The format is + * not fixed and may change at any time. + * + * @return A string representation of the pooled object + * + * @see Object#toString() + */ + String getPooledObjectToString(); + + /** + * Get the number of times this object has been borrowed. + * @return The number of times this object has been borrowed. + * @since 2.1 + */ + long getBorrowedCount(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionConfig.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionConfig.java new file mode 100755 index 000000000..94d436c05 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionConfig.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +/** + * This class is used by pool implementations to pass configuration information + * to {@link EvictionPolicy} instances. The {@link EvictionPolicy} may also have + * its own specific configuration attributes. + *

+ * This class is immutable and thread-safe. + * + * @since 2.0 + */ +public class EvictionConfig { + + private final long idleEvictTime; + private final long idleSoftEvictTime; + private final int minIdle; + + /** + * Create a new eviction configuration with the specified parameters. + * Instances are immutable. + * + * @param poolIdleEvictTime Expected to be provided by + * {@link BaseGenericObjectPool#getMinEvictableIdleTimeMillis()} + * @param poolIdleSoftEvictTime Expected to be provided by + * {@link BaseGenericObjectPool#getSoftMinEvictableIdleTimeMillis()} + * @param minIdle Expected to be provided by + * {@link GenericObjectPool#getMinIdle()} or + * {@link GenericKeyedObjectPool#getMinIdlePerKey()} + */ + public EvictionConfig(final long poolIdleEvictTime, final long poolIdleSoftEvictTime, + final int minIdle) { + if (poolIdleEvictTime > 0) { + idleEvictTime = poolIdleEvictTime; + } else { + idleEvictTime = Long.MAX_VALUE; + } + if (poolIdleSoftEvictTime > 0) { + idleSoftEvictTime = poolIdleSoftEvictTime; + } else { + idleSoftEvictTime = Long.MAX_VALUE; + } + this.minIdle = minIdle; + } + + /** + * Obtain the {@code idleEvictTime} for this eviction configuration + * instance. + *

+ * How the evictor behaves based on this value will be determined by the + * configured {@link EvictionPolicy}. + * + * @return The {@code idleEvictTime} in milliseconds + */ + public long getIdleEvictTime() { + return idleEvictTime; + } + + /** + * Obtain the {@code idleSoftEvictTime} for this eviction configuration + * instance. + *

+ * How the evictor behaves based on this value will be determined by the + * configured {@link EvictionPolicy}. + * + * @return The (@code idleSoftEvictTime} in milliseconds + */ + public long getIdleSoftEvictTime() { + return idleSoftEvictTime; + } + + /** + * Obtain the {@code minIdle} for this eviction configuration instance. + *

+ * How the evictor behaves based on this value will be determined by the + * configured {@link EvictionPolicy}. + * + * @return The {@code minIdle} + */ + public int getMinIdle() { + return minIdle; + } + + /** + * @since 2.4 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("EvictionConfig [idleEvictTime="); + builder.append(idleEvictTime); + builder.append(", idleSoftEvictTime="); + builder.append(idleSoftEvictTime); + builder.append(", minIdle="); + builder.append(minIdle); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionPolicy.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionPolicy.java new file mode 100755 index 000000000..4914f62f6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionPolicy.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.PooledObject; + +/** + * To provide a custom eviction policy (i.e. something other than {@link + * DefaultEvictionPolicy} for a pool, users must provide an implementation of + * this interface that provides the required eviction policy. + * + * @param the type of objects in the pool + * + * @since 2.0 + */ +public interface EvictionPolicy { + + /** + * This method is called to test if an idle object in the pool should be + * evicted or not. + * + * @param config The pool configuration settings related to eviction + * @param underTest The pooled object being tested for eviction + * @param idleCount The current number of idle objects in the pool including + * the object under test + * @return true if the object should be evicted, otherwise + * false + */ + boolean evict(EvictionConfig config, PooledObject underTest, + int idleCount); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionTimer.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionTimer.java new file mode 100755 index 000000000..c0ff60206 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/EvictionTimer.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.TimerTask; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +/** + * Provides a shared idle object eviction timer for all pools. This class is + * currently implemented using {@link ScheduledThreadPoolExecutor}. This + * implementation may change in any future release. This class keeps track of + * how many pools are using it. If no pools are using the timer, it is cancelled. + * This prevents a thread being left running which, in application server + * environments, can lead to memory leads and/or prevent applications from + * shutting down or reloading cleanly. + *

+ * This class has package scope to prevent its inclusion in the pool public API. + * The class declaration below should *not* be changed to public. + *

+ * This class is intended to be thread-safe. + * + * @since 2.0 + */ +class EvictionTimer { + + /** Executor instance */ + private static ScheduledThreadPoolExecutor executor; //@GuardedBy("EvictionTimer.class") + + /** Static usage count tracker */ + private static int usageCount; //@GuardedBy("EvictionTimer.class") + + /** Prevent instantiation */ + private EvictionTimer() { + // Hide the default constructor + } + + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("EvictionTimer []"); + return builder.toString(); + } + + + /** + * Add the specified eviction task to the timer. Tasks that are added with a + * call to this method *must* call {@link #cancel(TimerTask)} to cancel the + * task to prevent memory and/or thread leaks in application server + * environments. + * @param task Task to be scheduled + * @param delay Delay in milliseconds before task is executed + * @param period Time in milliseconds between executions + */ + static synchronized void schedule(final Runnable task, final long delay, final long period) { + if (null == executor) { + executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory()); + } + usageCount++; + executor.scheduleWithFixedDelay(task, delay, period, TimeUnit.MILLISECONDS); + } + + /** + * Remove the specified eviction task from the timer. + * + * @param task Task to be cancelled + * @param timeout If the associated executor is no longer required, how + * long should this thread wait for the executor to + * terminate? + * @param unit The units for the specified timeout + */ + static synchronized void cancel(final TimerTask task, final long timeout, final TimeUnit unit) { + task.cancel(); + usageCount--; + if (usageCount == 0) { + executor.shutdown(); + try { + executor.awaitTermination(timeout, unit); + } catch (final InterruptedException e) { + // Swallow + // Significant API changes would be required to propagate this + } + executor.setCorePoolSize(0); + executor = null; + } + } + + /** + * Thread factory that creates a thread, with the context classloader from this class. + */ + private static class EvictorThreadFactory implements ThreadFactory { + + @Override + public Thread newThread(final Runnable r) { + final Thread t = new Thread(null, r, "commons-pool-evictor-thread"); + + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + t.setContextClassLoader(EvictorThreadFactory.class.getClassLoader()); + return null; + } + }); + + return t; + } + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java new file mode 100755 index 000000000..06c292a74 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java @@ -0,0 +1,1625 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import com.fr.third.org.apache.commons.pool2.KeyedObjectPool; +import com.fr.third.org.apache.commons.pool2.KeyedPooledObjectFactory; +import com.fr.third.org.apache.commons.pool2.PoolUtils; +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.PooledObjectState; +import com.fr.third.org.apache.commons.pool2.SwallowedExceptionListener; + +/** + * A configurable KeyedObjectPool implementation. + *

+ * When coupled with the appropriate {@link KeyedPooledObjectFactory}, + * GenericKeyedObjectPool provides robust pooling functionality for + * keyed objects. A GenericKeyedObjectPool can be viewed as a map + * of sub-pools, keyed on the (unique) key values provided to the + * {@link #preparePool preparePool}, {@link #addObject addObject} or + * {@link #borrowObject borrowObject} methods. Each time a new key value is + * provided to one of these methods, a sub-new pool is created under the given + * key to be managed by the containing GenericKeyedObjectPool. + *

+ * Note that the current implementation uses a ConcurrentHashMap which uses + * equals() to compare keys. + * This means that distinct instance keys must be distinguishable using equals. + *

+ * Optionally, one may configure the pool to examine and possibly evict objects + * as they sit idle in the pool and to ensure that a minimum number of idle + * objects is maintained for each key. This is performed by an "idle object + * eviction" thread, which runs asynchronously. Caution should be used when + * configuring this optional feature. Eviction runs contend with client threads + * for access to objects in the pool, so if they run too frequently performance + * issues may result. + *

+ * Implementation note: To prevent possible deadlocks, care has been taken to + * ensure that no call to a factory method will occur within a synchronization + * block. See POOL-125 and DBCP-44 for more information. + *

+ * This class is intended to be thread-safe. + * + * @see GenericObjectPool + * + * @param The type of keys maintained by this pool. + * @param Type of element pooled in this pool. + * + * @since 2.0 + */ +public class GenericKeyedObjectPool extends BaseGenericObjectPool + implements KeyedObjectPool, GenericKeyedObjectPoolMXBean { + + /** + * Create a new GenericKeyedObjectPool using defaults from + * {@link GenericKeyedObjectPoolConfig}. + * @param factory the factory to be used to create entries + */ + public GenericKeyedObjectPool(final KeyedPooledObjectFactory factory) { + this(factory, new GenericKeyedObjectPoolConfig()); + } + + /** + * Create a new GenericKeyedObjectPool using a specific + * configuration. + * + * @param factory the factory to be used to create entries + * @param config The configuration to use for this pool instance. The + * configuration is used by value. Subsequent changes to + * the configuration object will not be reflected in the + * pool. + */ + public GenericKeyedObjectPool(final KeyedPooledObjectFactory factory, + final GenericKeyedObjectPoolConfig config) { + + super(config, ONAME_BASE, config.getJmxNamePrefix()); + + if (factory == null) { + jmxUnregister(); // tidy up + throw new IllegalArgumentException("factory may not be null"); + } + this.factory = factory; + this.fairness = config.getFairness(); + + setConfig(config); + + startEvictor(getTimeBetweenEvictionRunsMillis()); + } + + /** + * Returns the limit on the number of object instances allocated by the pool + * (checked out or idle), per key. When the limit is reached, the sub-pool + * is said to be exhausted. A negative value indicates no limit. + * + * @return the limit on the number of active instances per key + * + * @see #setMaxTotalPerKey + */ + @Override + public int getMaxTotalPerKey() { + return maxTotalPerKey; + } + + /** + * Sets the limit on the number of object instances allocated by the pool + * (checked out or idle), per key. When the limit is reached, the sub-pool + * is said to be exhausted. A negative value indicates no limit. + * + * @param maxTotalPerKey the limit on the number of active instances per key + * + * @see #getMaxTotalPerKey + */ + public void setMaxTotalPerKey(final int maxTotalPerKey) { + this.maxTotalPerKey = maxTotalPerKey; + } + + + /** + * Returns the cap on the number of "idle" instances per key in the pool. + * If maxIdlePerKey is set too low on heavily loaded systems it is possible + * you will see objects being destroyed and almost immediately new objects + * being created. This is a result of the active threads momentarily + * returning objects faster than they are requesting them, causing the + * number of idle objects to rise above maxIdlePerKey. The best value for + * maxIdlePerKey for heavily loaded system will vary but the default is a + * good starting point. + * + * @return the maximum number of "idle" instances that can be held in a + * given keyed sub-pool or a negative value if there is no limit + * + * @see #setMaxIdlePerKey + */ + @Override + public int getMaxIdlePerKey() { + return maxIdlePerKey; + } + + /** + * Sets the cap on the number of "idle" instances per key in the pool. + * If maxIdlePerKey is set too low on heavily loaded systems it is possible + * you will see objects being destroyed and almost immediately new objects + * being created. This is a result of the active threads momentarily + * returning objects faster than they are requesting them, causing the + * number of idle objects to rise above maxIdlePerKey. The best value for + * maxIdlePerKey for heavily loaded system will vary but the default is a + * good starting point. + * + * @param maxIdlePerKey the maximum number of "idle" instances that can be + * held in a given keyed sub-pool. Use a negative value + * for no limit + * + * @see #getMaxIdlePerKey + */ + public void setMaxIdlePerKey(final int maxIdlePerKey) { + this.maxIdlePerKey = maxIdlePerKey; + } + + /** + * Sets the target for the minimum number of idle objects to maintain in + * each of the keyed sub-pools. This setting only has an effect if it is + * positive and {@link #getTimeBetweenEvictionRunsMillis()} is greater than + * zero. If this is the case, an attempt is made to ensure that each + * sub-pool has the required minimum number of instances during idle object + * eviction runs. + *

+ * If the configured value of minIdlePerKey is greater than the configured + * value for maxIdlePerKey then the value of maxIdlePerKey will be used + * instead. + * + * @param minIdlePerKey The minimum size of the each keyed pool + * + * @see #getMinIdlePerKey + * @see #getMaxIdlePerKey() + * @see #setTimeBetweenEvictionRunsMillis + */ + public void setMinIdlePerKey(final int minIdlePerKey) { + this.minIdlePerKey = minIdlePerKey; + } + + /** + * Returns the target for the minimum number of idle objects to maintain in + * each of the keyed sub-pools. This setting only has an effect if it is + * positive and {@link #getTimeBetweenEvictionRunsMillis()} is greater than + * zero. If this is the case, an attempt is made to ensure that each + * sub-pool has the required minimum number of instances during idle object + * eviction runs. + *

+ * If the configured value of minIdlePerKey is greater than the configured + * value for maxIdlePerKey then the value of maxIdlePerKey will be used + * instead. + * + * @return minimum size of the each keyed pool + * + * @see #setTimeBetweenEvictionRunsMillis + */ + @Override + public int getMinIdlePerKey() { + final int maxIdlePerKeySave = getMaxIdlePerKey(); + if (this.minIdlePerKey > maxIdlePerKeySave) { + return maxIdlePerKeySave; + } + return minIdlePerKey; + } + + /** + * Sets the configuration. + * + * @param conf the new configuration to use. This is used by value. + * + * @see GenericKeyedObjectPoolConfig + */ + public void setConfig(final GenericKeyedObjectPoolConfig conf) { + setLifo(conf.getLifo()); + setMaxIdlePerKey(conf.getMaxIdlePerKey()); + setMaxTotalPerKey(conf.getMaxTotalPerKey()); + setMaxTotal(conf.getMaxTotal()); + setMinIdlePerKey(conf.getMinIdlePerKey()); + setMaxWaitMillis(conf.getMaxWaitMillis()); + setBlockWhenExhausted(conf.getBlockWhenExhausted()); + setTestOnCreate(conf.getTestOnCreate()); + setTestOnBorrow(conf.getTestOnBorrow()); + setTestOnReturn(conf.getTestOnReturn()); + setTestWhileIdle(conf.getTestWhileIdle()); + setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun()); + setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis()); + setSoftMinEvictableIdleTimeMillis( + conf.getSoftMinEvictableIdleTimeMillis()); + setTimeBetweenEvictionRunsMillis( + conf.getTimeBetweenEvictionRunsMillis()); + setEvictionPolicyClassName(conf.getEvictionPolicyClassName()); + setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis()); + } + + /** + * Obtain a reference to the factory used to create, destroy and validate + * the objects used by this pool. + * + * @return the factory + */ + public KeyedPooledObjectFactory getFactory() { + return factory; + } + + /** + * Equivalent to {@link #borrowObject(Object, long) borrowObject}(key, + * {@link #getMaxWaitMillis()}). + *

+ * {@inheritDoc} + */ + @Override + public T borrowObject(final K key) throws Exception { + return borrowObject(key, getMaxWaitMillis()); + } + + /** + * Borrows an object from the sub-pool associated with the given key using + * the specified waiting time which only applies if + * {@link #getBlockWhenExhausted()} is true. + *

+ * If there is one or more idle instances available in the sub-pool + * associated with the given key, then an idle instance will be selected + * based on the value of {@link #getLifo()}, activated and returned. If + * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to + * true and validation fails, the instance is destroyed and the + * next available instance is examined. This continues until either a valid + * instance is returned or there are no more idle instances available. + *

+ * If there are no idle instances available in the sub-pool associated with + * the given key, behavior depends on the {@link #getMaxTotalPerKey() + * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable) + * {@link #getBlockWhenExhausted()} and the value passed in to the + * borrowMaxWaitMillis parameter. If the number of instances checked + * out from the sub-pool under the given key is less than + * maxTotalPerKey and the total number of instances in + * circulation (under all keys) is less than maxTotal, a new + * instance is created, activated and (if applicable) validated and returned + * to the caller. If validation fails, a NoSuchElementException + * will be thrown. + *

+ * If the associated sub-pool is exhausted (no available idle instances and + * no capacity to create new ones), this method will either block + * ({@link #getBlockWhenExhausted()} is true) or throw a + * NoSuchElementException + * ({@link #getBlockWhenExhausted()} is false). + * The length of time that this method will block when + * {@link #getBlockWhenExhausted()} is true is determined by the value + * passed in to the borrowMaxWait parameter. + *

+ * When maxTotal is set to a positive value and this method is + * invoked when at the limit with no idle instances available under the requested + * key, an attempt is made to create room by clearing the oldest 15% of the + * elements from the keyed sub-pools. + *

+ * When the pool is exhausted, multiple calling threads may be + * simultaneously blocked waiting for instances to become available. A + * "fairness" algorithm has been implemented to ensure that threads receive + * available instances in request arrival order. + * + * @param key pool key + * @param borrowMaxWaitMillis The time to wait in milliseconds for an object + * to become available + * + * @return object instance from the keyed pool + * + * @throws NoSuchElementException if a keyed object instance cannot be + * returned because the pool is exhausted. + * + * @throws Exception if a keyed object instance cannot be returned due to an + * error + */ + public T borrowObject(final K key, final long borrowMaxWaitMillis) throws Exception { + assertOpen(); + + PooledObject p = null; + + // Get local copy of current config so it is consistent for entire + // method execution + final boolean blockWhenExhausted = getBlockWhenExhausted(); + + boolean create; + final long waitTime = System.currentTimeMillis(); + final ObjectDeque objectDeque = register(key); + + try { + while (p == null) { + create = false; + p = objectDeque.getIdleObjects().pollFirst(); + if (p == null) { + p = create(key); + if (p != null) { + create = true; + } + } + if (blockWhenExhausted) { + if (p == null) { + if (borrowMaxWaitMillis < 0) { + p = objectDeque.getIdleObjects().takeFirst(); + } else { + p = objectDeque.getIdleObjects().pollFirst( + borrowMaxWaitMillis, TimeUnit.MILLISECONDS); + } + } + if (p == null) { + throw new NoSuchElementException( + "Timeout waiting for idle object"); + } + } else { + if (p == null) { + throw new NoSuchElementException("Pool exhausted"); + } + } + if (!p.allocate()) { + p = null; + } + + if (p != null) { + try { + factory.activateObject(key, p); + } catch (final Exception e) { + try { + destroy(key, p, true); + } catch (final Exception e1) { + // Ignore - activation failure is more important + } + p = null; + if (create) { + final NoSuchElementException nsee = new NoSuchElementException( + "Unable to activate object"); + nsee.initCause(e); + throw nsee; + } + } + if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) { + boolean validate = false; + Throwable validationThrowable = null; + try { + validate = factory.validateObject(key, p); + } catch (final Throwable t) { + PoolUtils.checkRethrow(t); + validationThrowable = t; + } + if (!validate) { + try { + destroy(key, p, true); + destroyedByBorrowValidationCount.incrementAndGet(); + } catch (final Exception e) { + // Ignore - validation failure is more important + } + p = null; + if (create) { + final NoSuchElementException nsee = new NoSuchElementException( + "Unable to validate object"); + nsee.initCause(validationThrowable); + throw nsee; + } + } + } + } + } + } finally { + deregister(key); + } + + updateStatsBorrow(p, System.currentTimeMillis() - waitTime); + + return p.getObject(); + } + + + /** + * Returns an object to a keyed sub-pool. + *

+ * If {@link #getMaxIdlePerKey() maxIdle} is set to a positive value and the + * number of idle instances under the given key has reached this value, the + * returning instance is destroyed. + *

+ * If {@link #getTestOnReturn() testOnReturn} == true, the returning + * instance is validated before being returned to the idle instance sub-pool + * under the given key. In this case, if validation fails, the instance is + * destroyed. + *

+ * Exceptions encountered destroying objects for any reason are swallowed + * but notified via a {@link SwallowedExceptionListener}. + * + * @param key pool key + * @param obj instance to return to the keyed pool + * + * @throws IllegalStateException if an object is returned to the pool that + * was not borrowed from it or if an object is + * returned to the pool multiple times + */ + @Override + public void returnObject(final K key, final T obj) { + + final ObjectDeque objectDeque = poolMap.get(key); + + final PooledObject p = objectDeque.getAllObjects().get(new IdentityWrapper(obj)); + + if (p == null) { + throw new IllegalStateException( + "Returned object not currently part of this pool"); + } + + synchronized(p) { + final PooledObjectState state = p.getState(); + if (state != PooledObjectState.ALLOCATED) { + throw new IllegalStateException( + "Object has already been returned to this pool or is invalid"); + } + p.markReturning(); // Keep from being marked abandoned (once GKOP does this) + } + + final long activeTime = p.getActiveTimeMillis(); + + try { + if (getTestOnReturn()) { + if (!factory.validateObject(key, p)) { + try { + destroy(key, p, true); + } catch (final Exception e) { + swallowException(e); + } + if (objectDeque.idleObjects.hasTakeWaiters()) { + try { + addObject(key); + } catch (final Exception e) { + swallowException(e); + } + } + return; + } + } + + try { + factory.passivateObject(key, p); + } catch (final Exception e1) { + swallowException(e1); + try { + destroy(key, p, true); + } catch (final Exception e) { + swallowException(e); + } + if (objectDeque.idleObjects.hasTakeWaiters()) { + try { + addObject(key); + } catch (final Exception e) { + swallowException(e); + } + } + return; + } + + if (!p.deallocate()) { + throw new IllegalStateException( + "Object has already been returned to this pool"); + } + + final int maxIdle = getMaxIdlePerKey(); + final LinkedBlockingDeque> idleObjects = + objectDeque.getIdleObjects(); + + if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) { + try { + destroy(key, p, true); + } catch (final Exception e) { + swallowException(e); + } + } else { + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(key); + } + } + } finally { + if (hasBorrowWaiters()) { + reuseCapacity(); + } + updateStatsReturn(activeTime); + } + } + + + /** + * {@inheritDoc} + *

+ * Activation of this method decrements the active count associated with + * the given keyed pool and attempts to destroy obj. + * + * @param key pool key + * @param obj instance to invalidate + * + * @throws Exception if an exception occurs destroying the + * object + * @throws IllegalStateException if obj does not belong to the pool + * under the given key + */ + @Override + public void invalidateObject(final K key, final T obj) throws Exception { + + final ObjectDeque objectDeque = poolMap.get(key); + + final PooledObject p = objectDeque.getAllObjects().get(new IdentityWrapper(obj)); + if (p == null) { + throw new IllegalStateException( + "Object not currently part of this pool"); + } + synchronized (p) { + if (p.getState() != PooledObjectState.INVALID) { + destroy(key, p, true); + } + } + if (objectDeque.idleObjects.hasTakeWaiters()) { + addObject(key); + } + } + + + /** + * Clears any objects sitting idle in the pool by removing them from the + * idle instance sub-pools and then invoking the configured + * PoolableObjectFactory's + * {@link KeyedPooledObjectFactory#destroyObject(Object, PooledObject)} + * method on each idle instance. + *

+ * Implementation notes: + *

    + *
  • This method does not destroy or effect in any way instances that are + * checked out when it is invoked.
  • + *
  • Invoking this method does not prevent objects being returned to the + * idle instance pool, even during its execution. Additional instances may + * be returned while removed items are being destroyed.
  • + *
  • Exceptions encountered destroying idle instances are swallowed + * but notified via a {@link SwallowedExceptionListener}.
  • + *
+ */ + @Override + public void clear() { + final Iterator iter = poolMap.keySet().iterator(); + + while (iter.hasNext()) { + clear(iter.next()); + } + } + + + /** + * Clears the specified sub-pool, removing all pooled instances + * corresponding to the given key. Exceptions encountered + * destroying idle instances are swallowed but notified via a + * {@link SwallowedExceptionListener}. + * + * @param key the key to clear + */ + @Override + public void clear(final K key) { + + final ObjectDeque objectDeque = register(key); + + try { + final LinkedBlockingDeque> idleObjects = + objectDeque.getIdleObjects(); + + PooledObject p = idleObjects.poll(); + + while (p != null) { + try { + destroy(key, p, true); + } catch (final Exception e) { + swallowException(e); + } + p = idleObjects.poll(); + } + } finally { + deregister(key); + } + } + + + @Override + public int getNumActive() { + return numTotal.get() - getNumIdle(); + } + + + @Override + public int getNumIdle() { + final Iterator> iter = poolMap.values().iterator(); + int result = 0; + + while (iter.hasNext()) { + result += iter.next().getIdleObjects().size(); + } + + return result; + } + + + @Override + public int getNumActive(final K key) { + final ObjectDeque objectDeque = poolMap.get(key); + if (objectDeque != null) { + return objectDeque.getAllObjects().size() - + objectDeque.getIdleObjects().size(); + } + return 0; + } + + + @Override + public int getNumIdle(final K key) { + final ObjectDeque objectDeque = poolMap.get(key); + return objectDeque != null ? objectDeque.getIdleObjects().size() : 0; + } + + + /** + * Closes the keyed object pool. Once the pool is closed, + * {@link #borrowObject(Object)} will fail with IllegalStateException, but + * {@link #returnObject(Object, Object)} and + * {@link #invalidateObject(Object, Object)} will continue to work, with + * returned objects destroyed on return. + *

+ * Destroys idle instances in the pool by invoking {@link #clear()}. + */ + @Override + public void close() { + if (isClosed()) { + return; + } + + synchronized (closeLock) { + if (isClosed()) { + return; + } + + // Stop the evictor before the pool is closed since evict() calls + // assertOpen() + startEvictor(-1L); + + closed = true; + // This clear removes any idle objects + clear(); + + jmxUnregister(); + + // Release any threads that were waiting for an object + final Iterator> iter = poolMap.values().iterator(); + while (iter.hasNext()) { + iter.next().getIdleObjects().interuptTakeWaiters(); + } + // This clear cleans up the keys now any waiting threads have been + // interrupted + clear(); + } + } + + + /** + * Clears oldest 15% of objects in pool. The method sorts the objects into + * a TreeMap and then iterates the first 15% for removal. + */ + public void clearOldest() { + + // build sorted map of idle objects + final Map, K> map = new TreeMap, K>(); + + for (final Entry> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque deque = entry.getValue(); + // Protect against possible NPE if key has been removed in another + // thread. Not worth locking the keys while this loop completes. + if (deque != null) { + final LinkedBlockingDeque> idleObjects = + deque.getIdleObjects(); + for (final PooledObject p : idleObjects) { + // each item into the map using the PooledObject object as the + // key. It then gets sorted based on the idle time + map.put(p, k); + } + } + } + + // Now iterate created map and kill the first 15% plus one to account + // for zero + int itemsToRemove = ((int) (map.size() * 0.15)) + 1; + final Iterator, K>> iter = + map.entrySet().iterator(); + + while (iter.hasNext() && itemsToRemove > 0) { + final Entry, K> entry = iter.next(); + // kind of backwards on naming. In the map, each key is the + // PooledObject because it has the ordering with the timestamp + // value. Each value that the key references is the key of the + // list it belongs to. + final K key = entry.getValue(); + final PooledObject p = entry.getKey(); + // Assume the destruction succeeds + boolean destroyed = true; + try { + destroyed = destroy(key, p, false); + } catch (final Exception e) { + swallowException(e); + } + if (destroyed) { + itemsToRemove--; + } + } + } + + /** + * Attempt to create one new instance to serve from the most heavily + * loaded pool that can add a new instance. + * + * This method exists to ensure liveness in the pool when threads are + * parked waiting and capacity to create instances under the requested keys + * subsequently becomes available. + * + * This method is not guaranteed to create an instance and its selection + * of the most loaded pool that can create an instance may not always be + * correct, since it does not lock the pool and instances may be created, + * borrowed, returned or destroyed by other threads while it is executing. + */ + private void reuseCapacity() { + final int maxTotalPerKeySave = getMaxTotalPerKey(); + + // Find the most loaded pool that could take a new instance + int maxQueueLength = 0; + LinkedBlockingDeque> mostLoaded = null; + K loadedKey = null; + for (final Entry> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque deque = entry.getValue(); + if (deque != null) { + final LinkedBlockingDeque> pool = deque.getIdleObjects(); + final int queueLength = pool.getTakeQueueLength(); + if (getNumActive(k) < maxTotalPerKeySave && queueLength > maxQueueLength) { + maxQueueLength = queueLength; + mostLoaded = pool; + loadedKey = k; + } + } + } + + // Attempt to add an instance to the most loaded pool + if (mostLoaded != null) { + register(loadedKey); + try { + final PooledObject p = create(loadedKey); + if (p != null) { + addIdleObject(loadedKey, p); + } + } catch (final Exception e) { + swallowException(e); + } finally { + deregister(loadedKey); + } + } + } + + /** + * Checks to see if there are any threads currently waiting to borrow + * objects but are blocked waiting for more objects to become available. + * + * @return {@code true} if there is at least one thread waiting otherwise + * {@code false} + */ + private boolean hasBorrowWaiters() { + for (final Entry> entry : poolMap.entrySet()) { + final ObjectDeque deque = entry.getValue(); + if (deque != null) { + final LinkedBlockingDeque> pool = + deque.getIdleObjects(); + if(pool.hasTakeWaiters()) { + return true; + } + } + } + return false; + } + + + /** + * {@inheritDoc} + *

+ * Successive activations of this method examine objects in keyed sub-pools + * in sequence, cycling through the keys and examining objects in + * oldest-to-youngest order within the keyed sub-pools. + */ + @Override + public void evict() throws Exception { + assertOpen(); + + if (getNumIdle() == 0) { + return; + } + + PooledObject underTest = null; + final EvictionPolicy evictionPolicy = getEvictionPolicy(); + + synchronized (evictionLock) { + final EvictionConfig evictionConfig = new EvictionConfig( + getMinEvictableIdleTimeMillis(), + getSoftMinEvictableIdleTimeMillis(), + getMinIdlePerKey()); + + final boolean testWhileIdle = getTestWhileIdle(); + + for (int i = 0, m = getNumTests(); i < m; i++) { + if(evictionIterator == null || !evictionIterator.hasNext()) { + if (evictionKeyIterator == null || + !evictionKeyIterator.hasNext()) { + final List keyCopy = new ArrayList(); + final Lock readLock = keyLock.readLock(); + readLock.lock(); + try { + keyCopy.addAll(poolKeyList); + } finally { + readLock.unlock(); + } + evictionKeyIterator = keyCopy.iterator(); + } + while (evictionKeyIterator.hasNext()) { + evictionKey = evictionKeyIterator.next(); + final ObjectDeque objectDeque = poolMap.get(evictionKey); + if (objectDeque == null) { + continue; + } + + final Deque> idleObjects = objectDeque.getIdleObjects(); + evictionIterator = new EvictionIterator(idleObjects); + if (evictionIterator.hasNext()) { + break; + } + evictionIterator = null; + } + } + if (evictionIterator == null) { + // Pools exhausted + return; + } + final Deque> idleObjects; + try { + underTest = evictionIterator.next(); + idleObjects = evictionIterator.getIdleObjects(); + } catch (final NoSuchElementException nsee) { + // Object was borrowed in another thread + // Don't count this as an eviction test so reduce i; + i--; + evictionIterator = null; + continue; + } + + if (!underTest.startEvictionTest()) { + // Object was borrowed in another thread + // Don't count this as an eviction test so reduce i; + i--; + continue; + } + + // User provided eviction policy could throw all sorts of + // crazy exceptions. Protect against such an exception + // killing the eviction thread. + boolean evict; + try { + evict = evictionPolicy.evict(evictionConfig, underTest, + poolMap.get(evictionKey).getIdleObjects().size()); + } catch (final Throwable t) { + // Slightly convoluted as SwallowedExceptionListener + // uses Exception rather than Throwable + PoolUtils.checkRethrow(t); + swallowException(new Exception(t)); + // Don't evict on error conditions + evict = false; + } + + if (evict) { + destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); + } else { + if (testWhileIdle) { + boolean active = false; + try { + factory.activateObject(evictionKey, underTest); + active = true; + } catch (final Exception e) { + destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); + } + if (active) { + if (!factory.validateObject(evictionKey, underTest)) { + destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); + } else { + try { + factory.passivateObject(evictionKey, underTest); + } catch (final Exception e) { + destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); + } + } + } + } + if (!underTest.endEvictionTest(idleObjects)) { + // TODO - May need to add code here once additional + // states are used + } + } + } + } + } + + /** + * Create a new pooled object. + * + * @param key Key associated with new pooled object + * + * @return The new, wrapped pooled object + * + * @throws Exception If the objection creation fails + */ + private PooledObject create(final K key) throws Exception { + int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key + if (maxTotalPerKeySave < 0) { + maxTotalPerKeySave = Integer.MAX_VALUE; + } + final int maxTotal = getMaxTotal(); // All keys + + final ObjectDeque objectDeque = poolMap.get(key); + + // Check against the overall limit + boolean loop = true; + + while (loop) { + final int newNumTotal = numTotal.incrementAndGet(); + if (maxTotal > -1 && newNumTotal > maxTotal) { + numTotal.decrementAndGet(); + if (getNumIdle() == 0) { + return null; + } + clearOldest(); + } else { + loop = false; + } + } + + // Flag that indicates if create should: + // - TRUE: call the factory to create an object + // - FALSE: return null + // - null: loop and re-test the condition that determines whether to + // call the factory + Boolean create = null; + while (create == null) { + synchronized (objectDeque.makeObjectCountLock) { + final long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); + // Check against the per key limit + if (newCreateCount > maxTotalPerKeySave) { + // The key is currently at capacity or in the process of + // making enough new objects to take it to capacity. + objectDeque.getCreateCount().decrementAndGet(); + if (objectDeque.makeObjectCount == 0) { + // There are no makeObject() calls in progress for this + // key so the key is at capacity. Do not attempt to + // create a new object. Return and wait for an object to + // be returned. + create = Boolean.FALSE; + } else { + // There are makeObject() calls in progress that might + // bring the pool to capacity. Those calls might also + // fail so wait until they complete and then re-test if + // the pool is at capacity or not. + objectDeque.makeObjectCountLock.wait(); + } + } else { + // The pool is not at capacity. Create a new object. + objectDeque.makeObjectCount++; + create = Boolean.TRUE; + } + } + } + + if (!create.booleanValue()) { + numTotal.decrementAndGet(); + return null; + } + + PooledObject p = null; + try { + p = factory.makeObject(key); + } catch (final Exception e) { + numTotal.decrementAndGet(); + objectDeque.getCreateCount().decrementAndGet(); + throw e; + } finally { + synchronized (objectDeque.makeObjectCountLock) { + objectDeque.makeObjectCount--; + objectDeque.makeObjectCountLock.notifyAll(); + } + } + + createdCount.incrementAndGet(); + objectDeque.getAllObjects().put(new IdentityWrapper(p.getObject()), p); + return p; + } + + /** + * Destroy the wrapped, pooled object. + * + * @param key The key associated with the object to destroy. + * @param toDestroy The wrapped object to be destroyed + * @param always Should the object be destroyed even if it is not currently + * in the set of idle objects for the given key + * @return {@code true} if the object was destroyed, otherwise {@code false} + * @throws Exception If the object destruction failed + */ + private boolean destroy(final K key, final PooledObject toDestroy, final boolean always) + throws Exception { + + final ObjectDeque objectDeque = register(key); + + try { + final boolean isIdle = objectDeque.getIdleObjects().remove(toDestroy); + + if (isIdle || always) { + objectDeque.getAllObjects().remove(new IdentityWrapper(toDestroy.getObject())); + toDestroy.invalidate(); + + try { + factory.destroyObject(key, toDestroy); + } finally { + objectDeque.getCreateCount().decrementAndGet(); + destroyedCount.incrementAndGet(); + numTotal.decrementAndGet(); + } + return true; + } + return false; + } finally { + deregister(key); + } + } + + + /** + * Register the use of a key by an object. + *

+ * register() and deregister() must always be used as a pair. + * + * @param k The key to register + * + * @return The objects currently associated with the given key. If this + * method returns without throwing an exception then it will never + * return null. + */ + private ObjectDeque register(final K k) { + Lock lock = keyLock.readLock(); + ObjectDeque objectDeque = null; + try { + lock.lock(); + objectDeque = poolMap.get(k); + if (objectDeque == null) { + // Upgrade to write lock + lock.unlock(); + lock = keyLock.writeLock(); + lock.lock(); + objectDeque = poolMap.get(k); + if (objectDeque == null) { + objectDeque = new ObjectDeque(fairness); + objectDeque.getNumInterested().incrementAndGet(); + // NOTE: Keys must always be added to both poolMap and + // poolKeyList at the same time while protected by + // keyLock.writeLock() + poolMap.put(k, objectDeque); + poolKeyList.add(k); + } else { + objectDeque.getNumInterested().incrementAndGet(); + } + } else { + objectDeque.getNumInterested().incrementAndGet(); + } + } finally { + lock.unlock(); + } + return objectDeque; + } + + /** + * De-register the use of a key by an object. + *

+ * register() and deregister() must always be used as a pair. + * + * @param k The key to de-register + */ + private void deregister(final K k) { + ObjectDeque objectDeque; + + objectDeque = poolMap.get(k); + final long numInterested = objectDeque.getNumInterested().decrementAndGet(); + if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) { + // Potential to remove key + final Lock writeLock = keyLock.writeLock(); + writeLock.lock(); + try { + if (objectDeque.getCreateCount().get() == 0 && + objectDeque.getNumInterested().get() == 0) { + // NOTE: Keys must always be removed from both poolMap and + // poolKeyList at the same time while protected by + // keyLock.writeLock() + poolMap.remove(k); + poolKeyList.remove(k); + } + } finally { + writeLock.unlock(); + } + } + } + + @Override + void ensureMinIdle() throws Exception { + final int minIdlePerKeySave = getMinIdlePerKey(); + if (minIdlePerKeySave < 1) { + return; + } + + for (final K k : poolMap.keySet()) { + ensureMinIdle(k); + } + } + + /** + * Ensure that the configured number of minimum idle objects is available in + * the pool for the given key. + * + * @param key The key to check for idle objects + * + * @throws Exception If a new object is required and cannot be created + */ + private void ensureMinIdle(final K key) throws Exception { + // Calculate current pool objects + ObjectDeque objectDeque = poolMap.get(key); + + // objectDeque == null is OK here. It is handled correctly by both + // methods called below. + + // this method isn't synchronized so the + // calculateDeficit is done at the beginning + // as a loop limit and a second time inside the loop + // to stop when another thread already returned the + // needed objects + final int deficit = calculateDeficit(objectDeque); + + for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) { + addObject(key); + // If objectDeque was null, it won't be any more. Obtain a reference + // to it so the deficit can be correctly calculated. It needs to + // take account of objects created in other threads. + if (objectDeque == null) { + objectDeque = poolMap.get(key); + } + } + } + + /** + * Create an object using the {@link KeyedPooledObjectFactory#makeObject + * factory}, passivate it, and then place it in the idle object pool. + * addObject is useful for "pre-loading" a pool with idle + * objects. + * + * @param key the key a new instance should be added to + * + * @throws Exception when {@link KeyedPooledObjectFactory#makeObject} + * fails. + */ + @Override + public void addObject(final K key) throws Exception { + assertOpen(); + register(key); + try { + final PooledObject p = create(key); + addIdleObject(key, p); + } finally { + deregister(key); + } + } + + /** + * Add an object to the set of idle objects for a given key. + * + * @param key The key to associate with the idle object + * @param p The wrapped object to add. + * + * @throws Exception If the associated factory fails to passivate the object + */ + private void addIdleObject(final K key, final PooledObject p) throws Exception { + + if (p != null) { + factory.passivateObject(key, p); + final LinkedBlockingDeque> idleObjects = + poolMap.get(key).getIdleObjects(); + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + } + } + + /** + * Registers a key for pool control and ensures that + * {@link #getMinIdlePerKey()} idle instances are created. + * + * @param key - The key to register for pool control. + * + * @throws Exception If the associated factory throws an exception + */ + public void preparePool(final K key) throws Exception { + final int minIdlePerKeySave = getMinIdlePerKey(); + if (minIdlePerKeySave < 1) { + return; + } + ensureMinIdle(key); + } + + /** + * Calculate the number of objects to test in a run of the idle object + * evictor. + * + * @return The number of objects to test for validity + */ + private int getNumTests() { + final int totalIdle = getNumIdle(); + final int numTests = getNumTestsPerEvictionRun(); + if (numTests >= 0) { + return Math.min(numTests, totalIdle); + } + return(int)(Math.ceil(totalIdle/Math.abs((double)numTests))); + } + + /** + * Calculate the number of objects that need to be created to attempt to + * maintain the minimum number of idle objects while not exceeded the limits + * on the maximum number of objects either per key or totally. + * + * @param objectDeque The set of objects to check + * + * @return The number of new objects to create + */ + private int calculateDeficit(final ObjectDeque objectDeque) { + + if (objectDeque == null) { + return getMinIdlePerKey(); + } + + // Used more than once so keep a local copy so the value is consistent + final int maxTotal = getMaxTotal(); + final int maxTotalPerKeySave = getMaxTotalPerKey(); + + int objectDefecit = 0; + + // Calculate no of objects needed to be created, in order to have + // the number of pooled objects < maxTotalPerKey(); + objectDefecit = getMinIdlePerKey() - objectDeque.getIdleObjects().size(); + if (maxTotalPerKeySave > 0) { + final int growLimit = Math.max(0, + maxTotalPerKeySave - objectDeque.getIdleObjects().size()); + objectDefecit = Math.min(objectDefecit, growLimit); + } + + // Take the maxTotal limit into account + if (maxTotal > 0) { + final int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle()); + objectDefecit = Math.min(objectDefecit, growLimit); + } + + return objectDefecit; + } + + + //--- JMX support ---------------------------------------------------------- + + @Override + public Map getNumActivePerKey() { + final HashMap result = new HashMap(); + + final Iterator>> iter = poolMap.entrySet().iterator(); + while (iter.hasNext()) { + final Entry> entry = iter.next(); + if (entry != null) { + final K key = entry.getKey(); + final ObjectDeque objectDequeue = entry.getValue(); + if (key != null && objectDequeue != null) { + result.put(key.toString(), Integer.valueOf( + objectDequeue.getAllObjects().size() - + objectDequeue.getIdleObjects().size())); + } + } + } + return result; + } + + /** + * Return an estimate of the number of threads currently blocked waiting for + * an object from the pool. This is intended for monitoring only, not for + * synchronization control. + * + * @return The estimate of the number of threads currently blocked waiting + * for an object from the pool + */ + @Override + public int getNumWaiters() { + int result = 0; + + if (getBlockWhenExhausted()) { + final Iterator> iter = poolMap.values().iterator(); + + while (iter.hasNext()) { + // Assume no overflow + result += iter.next().getIdleObjects().getTakeQueueLength(); + } + } + + return result; + } + + /** + * Return an estimate of the number of threads currently blocked waiting for + * an object from the pool for each key. This is intended for + * monitoring only, not for synchronization control. + * + * @return The estimate of the number of threads currently blocked waiting + * for an object from the pool for each key + */ + @Override + public Map getNumWaitersByKey() { + final Map result = new HashMap(); + + for (final Entry> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque deque = entry.getValue(); + if (deque != null) { + if (getBlockWhenExhausted()) { + result.put(k.toString(), Integer.valueOf( + deque.getIdleObjects().getTakeQueueLength())); + } else { + result.put(k.toString(), Integer.valueOf(0)); + } + } + } + return result; + } + + /** + * Provides information on all the objects in the pool, both idle (waiting + * to be borrowed) and active (currently borrowed). + *

+ * Note: This is named listAllObjects so it is presented as an operation via + * JMX. That means it won't be invoked unless the explicitly requested + * whereas all attributes will be automatically requested when viewing the + * attributes for an object in a tool like JConsole. + * + * @return Information grouped by key on all the objects in the pool + */ + @Override + public Map> listAllObjects() { + final Map> result = + new HashMap>(); + + for (final Entry> entry : poolMap.entrySet()) { + final K k = entry.getKey(); + final ObjectDeque deque = entry.getValue(); + if (deque != null) { + final List list = + new ArrayList(); + result.put(k.toString(), list); + for (final PooledObject p : deque.getAllObjects().values()) { + list.add(new DefaultPooledObjectInfo(p)); + } + } + } + return result; + } + + + //--- inner classes ---------------------------------------------- + + /** + * Maintains information on the per key queue for a given key. + * + * @param type of objects in the pool + */ + private class ObjectDeque { + + private final LinkedBlockingDeque> idleObjects; + + /* + * Number of instances created - number destroyed. + * Invariant: createCount <= maxTotalPerKey + */ + private final AtomicInteger createCount = new AtomicInteger(0); + + private long makeObjectCount = 0; + private final Object makeObjectCountLock = new Object(); + + /* + * The map is keyed on pooled instances, wrapped to ensure that + * they work properly as keys. + */ + private final Map, PooledObject> allObjects = + new ConcurrentHashMap, PooledObject>(); + + /* + * Number of threads with registered interest in this key. + * register(K) increments this counter and deRegister(K) decrements it. + * Invariant: empty keyed pool will not be dropped unless numInterested + * is 0. + */ + private final AtomicLong numInterested = new AtomicLong(0); + + /** + * Create a new ObjecDeque with the given fairness policy. + * @param fairness true means client threads waiting to borrow / return instances + * will be served as if waiting in a FIFO queue. + */ + public ObjectDeque(final boolean fairness) { + idleObjects = new LinkedBlockingDeque>(fairness); + } + + /** + * Obtain the idle objects for the current key. + * + * @return The idle objects + */ + public LinkedBlockingDeque> getIdleObjects() { + return idleObjects; + } + + /** + * Obtain the count of the number of objects created for the current + * key. + * + * @return The number of objects created for this key + */ + public AtomicInteger getCreateCount() { + return createCount; + } + + /** + * Obtain the number of threads with an interest registered in this key. + * + * @return The number of threads with a registered interest in this key + */ + public AtomicLong getNumInterested() { + return numInterested; + } + + /** + * Obtain all the objects for the current key. + * + * @return All the objects + */ + public Map, PooledObject> getAllObjects() { + return allObjects; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("ObjectDeque [idleObjects="); + builder.append(idleObjects); + builder.append(", createCount="); + builder.append(createCount); + builder.append(", allObjects="); + builder.append(allObjects); + builder.append(", numInterested="); + builder.append(numInterested); + builder.append("]"); + return builder.toString(); + } + + } + + //--- configuration attributes --------------------------------------------- + private volatile int maxIdlePerKey = + GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY; + private volatile int minIdlePerKey = + GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; + private volatile int maxTotalPerKey = + GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; + private final KeyedPooledObjectFactory factory; + private final boolean fairness; + + + //--- internal attributes -------------------------------------------------- + + /* + * My hash of sub-pools (ObjectQueue). The list of keys must be kept + * in step with {@link #poolKeyList} using {@link #keyLock} to ensure any + * changes to the list of current keys is made in a thread-safe manner. + */ + private final Map> poolMap = + new ConcurrentHashMap>(); // @GuardedBy("keyLock") for write access (and some read access) + /* + * List of pool keys - used to control eviction order. The list of keys + * must be kept in step with {@link #poolMap} using {@link #keyLock} + * to ensure any changes to the list of current keys is made in a + * thread-safe manner. + */ + private final List poolKeyList = new ArrayList(); // @GuardedBy("keyLock") + private final ReadWriteLock keyLock = new ReentrantReadWriteLock(true); + /* + * The combined count of the currently active objects for all keys and those + * in the process of being created. Under load, it may exceed + * {@link #maxTotal} but there will never be more than {@link #maxTotal} + * created at any one time. + */ + private final AtomicInteger numTotal = new AtomicInteger(0); + private Iterator evictionKeyIterator = null; // @GuardedBy("evictionLock") + private K evictionKey = null; // @GuardedBy("evictionLock") + + // JMX specific attributes + private static final String ONAME_BASE = + "org.apache.commons.pool2:type=GenericKeyedObjectPool,name="; + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + super.toStringAppendFields(builder); + builder.append(", maxIdlePerKey="); + builder.append(maxIdlePerKey); + builder.append(", minIdlePerKey="); + builder.append(minIdlePerKey); + builder.append(", maxTotalPerKey="); + builder.append(maxTotalPerKey); + builder.append(", factory="); + builder.append(factory); + builder.append(", fairness="); + builder.append(fairness); + builder.append(", poolMap="); + builder.append(poolMap); + builder.append(", poolKeyList="); + builder.append(poolKeyList); + builder.append(", keyLock="); + builder.append(keyLock); + builder.append(", numTotal="); + builder.append(numTotal); + builder.append(", evictionKeyIterator="); + builder.append(evictionKeyIterator); + builder.append(", evictionKey="); + builder.append(evictionKey); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolConfig.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolConfig.java new file mode 100755 index 000000000..5eb04bfc6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolConfig.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +/** + * A simple "struct" encapsulating the configuration for a + * {@link GenericKeyedObjectPool}. + * + *

+ * This class is not thread-safe; it is only intended to be used to provide + * attributes used when creating a pool. + * + * @since 2.0 + */ +public class GenericKeyedObjectPoolConfig extends BaseObjectPoolConfig { + + /** + * The default value for the {@code maxTotalPerKey} configuration attribute. + * @see GenericKeyedObjectPool#getMaxTotalPerKey() + */ + public static final int DEFAULT_MAX_TOTAL_PER_KEY = 8; + + /** + * The default value for the {@code maxTotal} configuration attribute. + * @see GenericKeyedObjectPool#getMaxTotal() + */ + public static final int DEFAULT_MAX_TOTAL = -1; + + /** + * The default value for the {@code minIdlePerKey} configuration attribute. + * @see GenericKeyedObjectPool#getMinIdlePerKey() + */ + public static final int DEFAULT_MIN_IDLE_PER_KEY = 0; + + /** + * The default value for the {@code maxIdlePerKey} configuration attribute. + * @see GenericKeyedObjectPool#getMaxIdlePerKey() + */ + public static final int DEFAULT_MAX_IDLE_PER_KEY = 8; + + + private int minIdlePerKey = DEFAULT_MIN_IDLE_PER_KEY; + + private int maxIdlePerKey = DEFAULT_MAX_IDLE_PER_KEY; + + private int maxTotalPerKey = DEFAULT_MAX_TOTAL_PER_KEY; + + private int maxTotal = DEFAULT_MAX_TOTAL; + + /** + * Create a new configuration with default settings. + */ + public GenericKeyedObjectPoolConfig() { + } + + /** + * Get the value for the {@code maxTotal} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code maxTotal} for this + * configuration instance + * + * @see GenericKeyedObjectPool#getMaxTotal() + */ + public int getMaxTotal() { + return maxTotal; + } + + /** + * Set the value for the {@code maxTotal} configuration attribute for + * pools created with this configuration instance. + * + * @param maxTotal The new setting of {@code maxTotal} + * for this configuration instance + * + * @see GenericKeyedObjectPool#setMaxTotal(int) + */ + public void setMaxTotal(final int maxTotal) { + this.maxTotal = maxTotal; + } + + /** + * Get the value for the {@code maxTotalPerKey} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code maxTotalPerKey} for this + * configuration instance + * + * @see GenericKeyedObjectPool#getMaxTotalPerKey() + */ + public int getMaxTotalPerKey() { + return maxTotalPerKey; + } + + /** + * Set the value for the {@code maxTotalPerKey} configuration attribute for + * pools created with this configuration instance. + * + * @param maxTotalPerKey The new setting of {@code maxTotalPerKey} + * for this configuration instance + * + * @see GenericKeyedObjectPool#setMaxTotalPerKey(int) + */ + public void setMaxTotalPerKey(final int maxTotalPerKey) { + this.maxTotalPerKey = maxTotalPerKey; + } + + /** + * Get the value for the {@code minIdlePerKey} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code minIdlePerKey} for this + * configuration instance + * + * @see GenericKeyedObjectPool#getMinIdlePerKey() + */ + public int getMinIdlePerKey() { + return minIdlePerKey; + } + + /** + * Set the value for the {@code minIdlePerKey} configuration attribute for + * pools created with this configuration instance. + * + * @param minIdlePerKey The new setting of {@code minIdlePerKey} + * for this configuration instance + * + * @see GenericKeyedObjectPool#setMinIdlePerKey(int) + */ + public void setMinIdlePerKey(final int minIdlePerKey) { + this.minIdlePerKey = minIdlePerKey; + } + + /** + * Get the value for the {@code maxIdlePerKey} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code maxIdlePerKey} for this + * configuration instance + * + * @see GenericKeyedObjectPool#getMaxIdlePerKey() + */ + public int getMaxIdlePerKey() { + return maxIdlePerKey; + } + + /** + * Set the value for the {@code maxIdlePerKey} configuration attribute for + * pools created with this configuration instance. + * + * @param maxIdlePerKey The new setting of {@code maxIdlePerKey} + * for this configuration instance + * + * @see GenericKeyedObjectPool#setMaxIdlePerKey(int) + */ + public void setMaxIdlePerKey(final int maxIdlePerKey) { + this.maxIdlePerKey = maxIdlePerKey; + } + + @Override + public GenericKeyedObjectPoolConfig clone() { + try { + return (GenericKeyedObjectPoolConfig) super.clone(); + } catch (final CloneNotSupportedException e) { + throw new AssertionError(); // Can't happen + } + } + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + super.toStringAppendFields(builder); + builder.append(", minIdlePerKey="); + builder.append(minIdlePerKey); + builder.append(", maxIdlePerKey="); + builder.append(maxIdlePerKey); + builder.append(", maxTotalPerKey="); + builder.append(maxTotalPerKey); + builder.append(", maxTotal="); + builder.append(maxTotal); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMXBean.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMXBean.java new file mode 100755 index 000000000..e0aed91a9 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMXBean.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.util.List; +import java.util.Map; + +/** + * Defines the methods that will be made available via JMX. + * + * NOTE: This interface exists only to define those attributes and methods that + * will be made available via JMX. It must not be implemented by clients + * as it is subject to change between major, minor and patch version + * releases of commons pool. Clients that implement this interface may + * not, therefore, be able to upgrade to a new minor or patch release + * without requiring code changes. + * + * @param The type of keys maintained by the pool. + * + * @since 2.0 + */ +public interface GenericKeyedObjectPoolMXBean { + // Expose getters for configuration settings + /** + * See {@link GenericKeyedObjectPool#getBlockWhenExhausted()} + * @return See {@link GenericKeyedObjectPool#getBlockWhenExhausted()} + */ + boolean getBlockWhenExhausted(); + /** + * See {@link GenericKeyedObjectPool#getFairness()} + * @return See {@link GenericKeyedObjectPool#getFairness()} + */ + boolean getFairness(); + /** + * See {@link GenericKeyedObjectPool#getLifo()} + * @return See {@link GenericKeyedObjectPool#getLifo()} + */ + boolean getLifo(); + /** + * See {@link GenericKeyedObjectPool#getMaxIdlePerKey()} + * @return See {@link GenericKeyedObjectPool#getMaxIdlePerKey()} + */ + int getMaxIdlePerKey(); + /** + * See {@link GenericKeyedObjectPool#getMaxTotal()} + * @return See {@link GenericKeyedObjectPool#getMaxTotal()} + */ + int getMaxTotal(); + /** + * See {@link GenericKeyedObjectPool#getMaxTotalPerKey()} + * @return See {@link GenericKeyedObjectPool#getMaxTotalPerKey()} + */ + int getMaxTotalPerKey(); + /** + * See {@link GenericKeyedObjectPool#getMaxWaitMillis()} + * @return See {@link GenericKeyedObjectPool#getMaxWaitMillis()} + */ + long getMaxWaitMillis(); + /** + * See {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()} + * @return See {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()} + */ + long getMinEvictableIdleTimeMillis(); + /** + * See {@link GenericKeyedObjectPool#getMinIdlePerKey()} + * @return See {@link GenericKeyedObjectPool#getMinIdlePerKey()} + */ + int getMinIdlePerKey(); + /** + * See {@link GenericKeyedObjectPool#getNumActive()} + * @return See {@link GenericKeyedObjectPool#getNumActive()} + */ + int getNumActive(); + /** + * See {@link GenericKeyedObjectPool#getNumIdle()} + * @return See {@link GenericKeyedObjectPool#getNumIdle()} + */ + int getNumIdle(); + /** + * See {@link GenericKeyedObjectPool#getNumTestsPerEvictionRun()} + * @return See {@link GenericKeyedObjectPool#getNumTestsPerEvictionRun()} + */ + int getNumTestsPerEvictionRun(); + /** + * See {@link GenericKeyedObjectPool#getTestOnCreate()} + * @return See {@link GenericKeyedObjectPool#getTestOnCreate()} + * @since 2.2 + */ + boolean getTestOnCreate(); + /** + * See {@link GenericKeyedObjectPool#getTestOnBorrow()} + * @return See {@link GenericKeyedObjectPool#getTestOnBorrow()} + */ + boolean getTestOnBorrow(); + /** + * See {@link GenericKeyedObjectPool#getTestOnReturn()} + * @return See {@link GenericKeyedObjectPool#getTestOnReturn()} + */ + boolean getTestOnReturn(); + /** + * See {@link GenericKeyedObjectPool#getTestWhileIdle()} + * @return See {@link GenericKeyedObjectPool#getTestWhileIdle()} + */ + boolean getTestWhileIdle(); + /** + * See {@link GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()} + * @return See {@link GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()} + */ + long getTimeBetweenEvictionRunsMillis(); + /** + * See {@link GenericKeyedObjectPool#isClosed()} + * @return See {@link GenericKeyedObjectPool#isClosed()} + */ + boolean isClosed(); + // Expose getters for monitoring attributes + /** + * See {@link GenericKeyedObjectPool#getNumActivePerKey()} + * @return See {@link GenericKeyedObjectPool#getNumActivePerKey()} + */ + Map getNumActivePerKey(); + /** + * See {@link GenericKeyedObjectPool#getBorrowedCount()} + * @return See {@link GenericKeyedObjectPool#getBorrowedCount()} + */ + long getBorrowedCount(); + /** + * See {@link GenericKeyedObjectPool#getReturnedCount()} + * @return See {@link GenericKeyedObjectPool#getReturnedCount()} + */ + long getReturnedCount(); + /** + * See {@link GenericKeyedObjectPool#getCreatedCount()} + * @return See {@link GenericKeyedObjectPool#getCreatedCount()} + */ + long getCreatedCount(); + /** + * See {@link GenericKeyedObjectPool#getDestroyedCount()} + * @return See {@link GenericKeyedObjectPool#getDestroyedCount()} + */ + long getDestroyedCount(); + /** + * See {@link GenericKeyedObjectPool#getDestroyedByEvictorCount()} + * @return See {@link GenericKeyedObjectPool#getDestroyedByEvictorCount()} + */ + long getDestroyedByEvictorCount(); + /** + * See {@link GenericKeyedObjectPool#getDestroyedByBorrowValidationCount()} + * @return See {@link GenericKeyedObjectPool#getDestroyedByBorrowValidationCount()} + */ + long getDestroyedByBorrowValidationCount(); + /** + * See {@link GenericKeyedObjectPool#getMeanActiveTimeMillis()} + * @return See {@link GenericKeyedObjectPool#getMeanActiveTimeMillis()} + */ + long getMeanActiveTimeMillis(); + /** + * See {@link GenericKeyedObjectPool#getMeanIdleTimeMillis()} + * @return See {@link GenericKeyedObjectPool#getMeanIdleTimeMillis()} + */ + long getMeanIdleTimeMillis(); + /** + * See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} + * @return See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} + */ + long getMeanBorrowWaitTimeMillis(); + /** + * See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} + * @return See {@link GenericKeyedObjectPool#getMaxBorrowWaitTimeMillis()} + */ + long getMaxBorrowWaitTimeMillis(); + /** + * See {@link GenericKeyedObjectPool#getCreationStackTrace()} + * @return See {@link GenericKeyedObjectPool#getCreationStackTrace()} + */ + String getCreationStackTrace(); + /** + * See {@link GenericKeyedObjectPool#getNumWaiters()} + * @return See {@link GenericKeyedObjectPool#getNumWaiters()} + */ + int getNumWaiters(); + /** + * See {@link GenericKeyedObjectPool#getNumWaitersByKey()} + * @return See {@link GenericKeyedObjectPool#getNumWaitersByKey()} + */ + Map getNumWaitersByKey(); + /** + * See {@link GenericKeyedObjectPool#listAllObjects()} + * @return See {@link GenericKeyedObjectPool#listAllObjects()} + */ + Map> listAllObjects(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPool.java new file mode 100755 index 000000000..e5242947c --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPool.java @@ -0,0 +1,1203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import com.fr.third.org.apache.commons.pool2.ObjectPool; +import com.fr.third.org.apache.commons.pool2.PoolUtils; +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; +import com.fr.third.org.apache.commons.pool2.PooledObjectState; +import com.fr.third.org.apache.commons.pool2.SwallowedExceptionListener; +import com.fr.third.org.apache.commons.pool2.TrackedUse; +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A configurable {@link ObjectPool} implementation. + *

+ * When coupled with the appropriate {@link PooledObjectFactory}, + * GenericObjectPool provides robust pooling functionality for + * arbitrary objects.

+ *

+ * Optionally, one may configure the pool to examine and possibly evict objects + * as they sit idle in the pool and to ensure that a minimum number of idle + * objects are available. This is performed by an "idle object eviction" thread, + * which runs asynchronously. Caution should be used when configuring this + * optional feature. Eviction runs contend with client threads for access to + * objects in the pool, so if they run too frequently performance issues may + * result.

+ *

+ * The pool can also be configured to detect and remove "abandoned" objects, + * i.e. objects that have been checked out of the pool but neither used nor + * returned before the configured + * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}. + * Abandoned object removal can be configured to happen when + * borrowObject is invoked and the pool is close to starvation, or + * it can be executed by the idle object evictor, or both. If pooled objects + * implement the {@link TrackedUse} interface, their last use will be queried + * using the getLastUsed method on that interface; otherwise + * abandonment is determined by how long an object has been checked out from + * the pool.

+ *

+ * Implementation note: To prevent possible deadlocks, care has been taken to + * ensure that no call to a factory method will occur within a synchronization + * block. See POOL-125 and DBCP-44 for more information.

+ *

+ * This class is intended to be thread-safe.

+ * + * @see GenericKeyedObjectPool + * + * @param Type of element pooled in this pool. + * + * @since 2.0 + */ +public class GenericObjectPool extends BaseGenericObjectPool + implements ObjectPool, GenericObjectPoolMXBean, UsageTracking { + + /** + * Create a new GenericObjectPool using defaults from + * {@link GenericObjectPoolConfig}. + * + * @param factory The object factory to be used to create object instances + * used by this pool + */ + public GenericObjectPool(final PooledObjectFactory factory) { + this(factory, new GenericObjectPoolConfig()); + } + + /** + * Create a new GenericObjectPool using a specific + * configuration. + * + * @param factory The object factory to be used to create object instances + * used by this pool + * @param config The configuration to use for this pool instance. The + * configuration is used by value. Subsequent changes to + * the configuration object will not be reflected in the + * pool. + */ + public GenericObjectPool(final PooledObjectFactory factory, + final GenericObjectPoolConfig config) { + + super(config, ONAME_BASE, config.getJmxNamePrefix()); + + if (factory == null) { + jmxUnregister(); // tidy up + throw new IllegalArgumentException("factory may not be null"); + } + this.factory = factory; + + idleObjects = new LinkedBlockingDeque>(config.getFairness()); + + setConfig(config); + + startEvictor(getTimeBetweenEvictionRunsMillis()); + } + + /** + * Create a new GenericObjectPool that tracks and destroys + * objects that are checked out, but never returned to the pool. + * + * @param factory The object factory to be used to create object instances + * used by this pool + * @param config The base pool configuration to use for this pool instance. + * The configuration is used by value. Subsequent changes to + * the configuration object will not be reflected in the + * pool. + * @param abandonedConfig Configuration for abandoned object identification + * and removal. The configuration is used by value. + */ + public GenericObjectPool(final PooledObjectFactory factory, + final GenericObjectPoolConfig config, final AbandonedConfig abandonedConfig) { + this(factory, config); + setAbandonedConfig(abandonedConfig); + } + + /** + * Returns the cap on the number of "idle" instances in the pool. If maxIdle + * is set too low on heavily loaded systems it is possible you will see + * objects being destroyed and almost immediately new objects being created. + * This is a result of the active threads momentarily returning objects + * faster than they are requesting them, causing the number of idle + * objects to rise above maxIdle. The best value for maxIdle for heavily + * loaded system will vary but the default is a good starting point. + * + * @return the maximum number of "idle" instances that can be held in the + * pool or a negative value if there is no limit + * + * @see #setMaxIdle + */ + @Override + public int getMaxIdle() { + return maxIdle; + } + + /** + * Returns the cap on the number of "idle" instances in the pool. If maxIdle + * is set too low on heavily loaded systems it is possible you will see + * objects being destroyed and almost immediately new objects being created. + * This is a result of the active threads momentarily returning objects + * faster than they are requesting them, causing the number of idle + * objects to rise above maxIdle. The best value for maxIdle for heavily + * loaded system will vary but the default is a good starting point. + * + * @param maxIdle + * The cap on the number of "idle" instances in the pool. Use a + * negative value to indicate an unlimited number of idle + * instances + * + * @see #getMaxIdle + */ + public void setMaxIdle(final int maxIdle) { + this.maxIdle = maxIdle; + } + + /** + * Sets the target for the minimum number of idle objects to maintain in + * the pool. This setting only has an effect if it is positive and + * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this + * is the case, an attempt is made to ensure that the pool has the required + * minimum number of instances during idle object eviction runs. + *

+ * If the configured value of minIdle is greater than the configured value + * for maxIdle then the value of maxIdle will be used instead. + * + * @param minIdle + * The minimum number of objects. + * + * @see #getMinIdle() + * @see #getMaxIdle() + * @see #getTimeBetweenEvictionRunsMillis() + */ + public void setMinIdle(final int minIdle) { + this.minIdle = minIdle; + } + + /** + * Returns the target for the minimum number of idle objects to maintain in + * the pool. This setting only has an effect if it is positive and + * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this + * is the case, an attempt is made to ensure that the pool has the required + * minimum number of instances during idle object eviction runs. + *

+ * If the configured value of minIdle is greater than the configured value + * for maxIdle then the value of maxIdle will be used instead. + * + * @return The minimum number of objects. + * + * @see #setMinIdle(int) + * @see #setMaxIdle(int) + * @see #setTimeBetweenEvictionRunsMillis(long) + */ + @Override + public int getMinIdle() { + final int maxIdleSave = getMaxIdle(); + if (this.minIdle > maxIdleSave) { + return maxIdleSave; + } + return minIdle; + } + + /** + * Whether or not abandoned object removal is configured for this pool. + * + * @return true if this pool is configured to detect and remove + * abandoned objects + */ + @Override + public boolean isAbandonedConfig() { + return abandonedConfig != null; + } + + /** + * Will this pool identify and log any abandoned objects? + * + * @return {@code true} if abandoned object removal is configured for this + * pool and removal events are to be logged otherwise {@code false} + * + * @see AbandonedConfig#getLogAbandoned() + */ + @Override + public boolean getLogAbandoned() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null && ac.getLogAbandoned(); + } + + /** + * Will a check be made for abandoned objects when an object is borrowed + * from this pool? + * + * @return {@code true} if abandoned object removal is configured to be + * activated by borrowObject otherwise {@code false} + * + * @see AbandonedConfig#getRemoveAbandonedOnBorrow() + */ + @Override + public boolean getRemoveAbandonedOnBorrow() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null && ac.getRemoveAbandonedOnBorrow(); + } + + /** + * Will a check be made for abandoned objects when the evictor runs? + * + * @return {@code true} if abandoned object removal is configured to be + * activated when the evictor runs otherwise {@code false} + * + * @see AbandonedConfig#getRemoveAbandonedOnMaintenance() + */ + @Override + public boolean getRemoveAbandonedOnMaintenance() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null && ac.getRemoveAbandonedOnMaintenance(); + } + + /** + * Obtain the timeout before which an object will be considered to be + * abandoned by this pool. + * + * @return The abandoned object timeout in seconds if abandoned object + * removal is configured for this pool; Integer.MAX_VALUE otherwise. + * + * @see AbandonedConfig#getRemoveAbandonedTimeout() + */ + @Override + public int getRemoveAbandonedTimeout() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE; + } + + + /** + * Sets the base pool configuration. + * + * @param conf the new configuration to use. This is used by value. + * + * @see GenericObjectPoolConfig + */ + public void setConfig(final GenericObjectPoolConfig conf) { + setLifo(conf.getLifo()); + setMaxIdle(conf.getMaxIdle()); + setMinIdle(conf.getMinIdle()); + setMaxTotal(conf.getMaxTotal()); + setMaxWaitMillis(conf.getMaxWaitMillis()); + setBlockWhenExhausted(conf.getBlockWhenExhausted()); + setTestOnCreate(conf.getTestOnCreate()); + setTestOnBorrow(conf.getTestOnBorrow()); + setTestOnReturn(conf.getTestOnReturn()); + setTestWhileIdle(conf.getTestWhileIdle()); + setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun()); + setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis()); + setTimeBetweenEvictionRunsMillis( + conf.getTimeBetweenEvictionRunsMillis()); + setSoftMinEvictableIdleTimeMillis( + conf.getSoftMinEvictableIdleTimeMillis()); + setEvictionPolicyClassName(conf.getEvictionPolicyClassName()); + setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis()); + } + + /** + * Sets the abandoned object removal configuration. + * + * @param abandonedConfig the new configuration to use. This is used by value. + * + * @see AbandonedConfig + */ + public void setAbandonedConfig(final AbandonedConfig abandonedConfig) { + if (abandonedConfig == null) { + this.abandonedConfig = null; + } else { + this.abandonedConfig = new AbandonedConfig(); + this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned()); + this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter()); + this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow()); + this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance()); + this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeout()); + this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking()); + this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace()); + } + } + + /** + * Obtain a reference to the factory used to create, destroy and validate + * the objects used by this pool. + * + * @return the factory + */ + public PooledObjectFactory getFactory() { + return factory; + } + + /** + * Equivalent to {@link #borrowObject(long) + * borrowObject}({@link #getMaxWaitMillis()}). + *

+ * {@inheritDoc} + */ + @Override + public T borrowObject() throws Exception { + return borrowObject(getMaxWaitMillis()); + } + + /** + * Borrow an object from the pool using the specific waiting time which only + * applies if {@link #getBlockWhenExhausted()} is true. + *

+ * If there is one or more idle instance available in the pool, then an + * idle instance will be selected based on the value of {@link #getLifo()}, + * activated and returned. If activation fails, or {@link #getTestOnBorrow() + * testOnBorrow} is set to true and validation fails, the + * instance is destroyed and the next available instance is examined. This + * continues until either a valid instance is returned or there are no more + * idle instances available. + *

+ * If there are no idle instances available in the pool, behavior depends on + * the {@link #getMaxTotal() maxTotal}, (if applicable) + * {@link #getBlockWhenExhausted()} and the value passed in to the + * borrowMaxWaitMillis parameter. If the number of instances + * checked out from the pool is less than maxTotal, a new + * instance is created, activated and (if applicable) validated and returned + * to the caller. If validation fails, a NoSuchElementException + * is thrown. + *

+ * If the pool is exhausted (no available idle instances and no capacity to + * create new ones), this method will either block (if + * {@link #getBlockWhenExhausted()} is true) or throw a + * NoSuchElementException (if + * {@link #getBlockWhenExhausted()} is false). The length of time that this + * method will block when {@link #getBlockWhenExhausted()} is true is + * determined by the value passed in to the borrowMaxWaitMillis + * parameter. + *

+ * When the pool is exhausted, multiple calling threads may be + * simultaneously blocked waiting for instances to become available. A + * "fairness" algorithm has been implemented to ensure that threads receive + * available instances in request arrival order. + * + * @param borrowMaxWaitMillis The time to wait in milliseconds for an object + * to become available + * + * @return object instance from the pool + * + * @throws NoSuchElementException if an instance cannot be returned + * + * @throws Exception if an object instance cannot be returned due to an + * error + */ + public T borrowObject(final long borrowMaxWaitMillis) throws Exception { + assertOpen(); + + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getRemoveAbandonedOnBorrow() && + (getNumIdle() < 2) && + (getNumActive() > getMaxTotal() - 3) ) { + removeAbandoned(ac); + } + + PooledObject p = null; + + // Get local copy of current config so it is consistent for entire + // method execution + final boolean blockWhenExhausted = getBlockWhenExhausted(); + + boolean create; + final long waitTime = System.currentTimeMillis(); + + while (p == null) { + create = false; + p = idleObjects.pollFirst(); + if (p == null) { + p = create(); + if (p != null) { + create = true; + } + } + if (blockWhenExhausted) { + if (p == null) { + if (borrowMaxWaitMillis < 0) { + p = idleObjects.takeFirst(); + } else { + p = idleObjects.pollFirst(borrowMaxWaitMillis, + TimeUnit.MILLISECONDS); + } + } + if (p == null) { + throw new NoSuchElementException( + "Timeout waiting for idle object"); + } + } else { + if (p == null) { + throw new NoSuchElementException("Pool exhausted"); + } + } + if (!p.allocate()) { + p = null; + } + + if (p != null) { + try { + factory.activateObject(p); + } catch (final Exception e) { + try { + destroy(p); + } catch (final Exception e1) { + // Ignore - activation failure is more important + } + p = null; + if (create) { + final NoSuchElementException nsee = new NoSuchElementException( + "Unable to activate object"); + nsee.initCause(e); + throw nsee; + } + } + if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) { + boolean validate = false; + Throwable validationThrowable = null; + try { + validate = factory.validateObject(p); + } catch (final Throwable t) { + PoolUtils.checkRethrow(t); + validationThrowable = t; + } + if (!validate) { + try { + destroy(p); + destroyedByBorrowValidationCount.incrementAndGet(); + } catch (final Exception e) { + // Ignore - validation failure is more important + } + p = null; + if (create) { + final NoSuchElementException nsee = new NoSuchElementException( + "Unable to validate object"); + nsee.initCause(validationThrowable); + throw nsee; + } + } + } + } + } + + updateStatsBorrow(p, System.currentTimeMillis() - waitTime); + + return p.getObject(); + } + + /** + * {@inheritDoc} + *

+ * If {@link #getMaxIdle() maxIdle} is set to a positive value and the + * number of idle instances has reached this value, the returning instance + * is destroyed. + *

+ * If {@link #getTestOnReturn() testOnReturn} == true, the returning + * instance is validated before being returned to the idle instance pool. In + * this case, if validation fails, the instance is destroyed. + *

+ * Exceptions encountered destroying objects for any reason are swallowed + * but notified via a {@link SwallowedExceptionListener}. + */ + @Override + public void returnObject(final T obj) { + final PooledObject p = allObjects.get(new IdentityWrapper(obj)); + + if (p == null) { + if (!isAbandonedConfig()) { + throw new IllegalStateException( + "Returned object not currently part of this pool"); + } + return; // Object was abandoned and removed + } + + synchronized(p) { + final PooledObjectState state = p.getState(); + if (state != PooledObjectState.ALLOCATED) { + throw new IllegalStateException( + "Object has already been returned to this pool or is invalid"); + } + p.markReturning(); // Keep from being marked abandoned + } + + final long activeTime = p.getActiveTimeMillis(); + + if (getTestOnReturn()) { + if (!factory.validateObject(p)) { + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } + updateStatsReturn(activeTime); + return; + } + } + + try { + factory.passivateObject(p); + } catch (final Exception e1) { + swallowException(e1); + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } + updateStatsReturn(activeTime); + return; + } + + if (!p.deallocate()) { + throw new IllegalStateException( + "Object has already been returned to this pool or is invalid"); + } + + final int maxIdleSave = getMaxIdle(); + if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + } else { + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(); + } + } + updateStatsReturn(activeTime); + } + + /** + * {@inheritDoc} + *

+ * Activation of this method decrements the active count and attempts to + * destroy the instance. + * + * @throws Exception if an exception occurs destroying the + * object + * @throws IllegalStateException if obj does not belong to this pool + */ + @Override + public void invalidateObject(final T obj) throws Exception { + final PooledObject p = allObjects.get(new IdentityWrapper(obj)); + if (p == null) { + if (isAbandonedConfig()) { + return; + } + throw new IllegalStateException( + "Invalidated object not currently part of this pool"); + } + synchronized (p) { + if (p.getState() != PooledObjectState.INVALID) { + destroy(p); + } + } + ensureIdle(1, false); + } + + /** + * Clears any objects sitting idle in the pool by removing them from the + * idle instance pool and then invoking the configured + * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each + * idle instance. + *

+ * Implementation notes: + *

    + *
  • This method does not destroy or effect in any way instances that are + * checked out of the pool when it is invoked.
  • + *
  • Invoking this method does not prevent objects being returned to the + * idle instance pool, even during its execution. Additional instances may + * be returned while removed items are being destroyed.
  • + *
  • Exceptions encountered destroying idle instances are swallowed + * but notified via a {@link SwallowedExceptionListener}.
  • + *
+ */ + @Override + public void clear() { + PooledObject p = idleObjects.poll(); + + while (p != null) { + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + p = idleObjects.poll(); + } + } + + @Override + public int getNumActive() { + return allObjects.size() - idleObjects.size(); + } + + @Override + public int getNumIdle() { + return idleObjects.size(); + } + + /** + * Closes the pool. Once the pool is closed, {@link #borrowObject()} will + * fail with IllegalStateException, but {@link #returnObject(Object)} and + * {@link #invalidateObject(Object)} will continue to work, with returned + * objects destroyed on return. + *

+ * Destroys idle instances in the pool by invoking {@link #clear()}. + */ + @Override + public void close() { + if (isClosed()) { + return; + } + + synchronized (closeLock) { + if (isClosed()) { + return; + } + + // Stop the evictor before the pool is closed since evict() calls + // assertOpen() + startEvictor(-1L); + + closed = true; + // This clear removes any idle objects + clear(); + + jmxUnregister(); + + // Release any threads that were waiting for an object + idleObjects.interuptTakeWaiters(); + } + } + + /** + * {@inheritDoc} + *

+ * Successive activations of this method examine objects in sequence, + * cycling through objects in oldest-to-youngest order. + */ + @Override + public void evict() throws Exception { + assertOpen(); + + if (idleObjects.size() > 0) { + + PooledObject underTest = null; + final EvictionPolicy evictionPolicy = getEvictionPolicy(); + + synchronized (evictionLock) { + final EvictionConfig evictionConfig = new EvictionConfig( + getMinEvictableIdleTimeMillis(), + getSoftMinEvictableIdleTimeMillis(), + getMinIdle()); + + final boolean testWhileIdle = getTestWhileIdle(); + + for (int i = 0, m = getNumTests(); i < m; i++) { + if (evictionIterator == null || !evictionIterator.hasNext()) { + evictionIterator = new EvictionIterator(idleObjects); + } + if (!evictionIterator.hasNext()) { + // Pool exhausted, nothing to do here + return; + } + + try { + underTest = evictionIterator.next(); + } catch (final NoSuchElementException nsee) { + // Object was borrowed in another thread + // Don't count this as an eviction test so reduce i; + i--; + evictionIterator = null; + continue; + } + + if (!underTest.startEvictionTest()) { + // Object was borrowed in another thread + // Don't count this as an eviction test so reduce i; + i--; + continue; + } + + // User provided eviction policy could throw all sorts of + // crazy exceptions. Protect against such an exception + // killing the eviction thread. + boolean evict; + try { + evict = evictionPolicy.evict(evictionConfig, underTest, + idleObjects.size()); + } catch (final Throwable t) { + // Slightly convoluted as SwallowedExceptionListener + // uses Exception rather than Throwable + PoolUtils.checkRethrow(t); + swallowException(new Exception(t)); + // Don't evict on error conditions + evict = false; + } + + if (evict) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } else { + if (testWhileIdle) { + boolean active = false; + try { + factory.activateObject(underTest); + active = true; + } catch (final Exception e) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } + if (active) { + if (!factory.validateObject(underTest)) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } else { + try { + factory.passivateObject(underTest); + } catch (final Exception e) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } + } + } + } + if (!underTest.endEvictionTest(idleObjects)) { + // TODO - May need to add code here once additional + // states are used + } + } + } + } + } + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { + removeAbandoned(ac); + } + } + + /** + * Tries to ensure that {@link #getMinIdle()} idle instances are available + * in the pool. + * + * @throws Exception If the associated factory throws an exception + * @since 2.4 + */ + public void preparePool() throws Exception { + if (getMinIdle() < 1) { + return; + } + ensureMinIdle(); + } + + /** + * Attempts to create a new wrapped pooled object. + *

+ * If there are {@link #getMaxTotal()} objects already in circulation + * or in process of being created, this method returns null. + * + * @return The new wrapped pooled object + * + * @throws Exception if the object factory's {@code makeObject} fails + */ + private PooledObject create() throws Exception { + int localMaxTotal = getMaxTotal(); + // This simplifies the code later in this method + if (localMaxTotal < 0) { + localMaxTotal = Integer.MAX_VALUE; + } + + // Flag that indicates if create should: + // - TRUE: call the factory to create an object + // - FALSE: return null + // - null: loop and re-test the condition that determines whether to + // call the factory + Boolean create = null; + while (create == null) { + synchronized (makeObjectCountLock) { + final long newCreateCount = createCount.incrementAndGet(); + if (newCreateCount > localMaxTotal) { + // The pool is currently at capacity or in the process of + // making enough new objects to take it to capacity. + createCount.decrementAndGet(); + if (makeObjectCount == 0) { + // There are no makeObject() calls in progress so the + // pool is at capacity. Do not attempt to create a new + // object. Return and wait for an object to be returned + create = Boolean.FALSE; + } else { + // There are makeObject() calls in progress that might + // bring the pool to capacity. Those calls might also + // fail so wait until they complete and then re-test if + // the pool is at capacity or not. + makeObjectCountLock.wait(); + } + } else { + // The pool is not at capacity. Create a new object. + makeObjectCount++; + create = Boolean.TRUE; + } + } + } + + if (!create.booleanValue()) { + return null; + } + + final PooledObject p; + try { + p = factory.makeObject(); + } catch (final Exception e) { + createCount.decrementAndGet(); + throw e; + } finally { + synchronized (makeObjectCountLock) { + makeObjectCount--; + makeObjectCountLock.notifyAll(); + } + } + + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getLogAbandoned()) { + p.setLogAbandoned(true); + // TODO: in 3.0, this can use the method defined on PooledObject + if (p instanceof DefaultPooledObject) { + ((DefaultPooledObject) p).setRequireFullStackTrace(ac.getRequireFullStackTrace()); + } + } + + createdCount.incrementAndGet(); + allObjects.put(new IdentityWrapper(p.getObject()), p); + return p; + } + + /** + * Destroys a wrapped pooled object. + * + * @param toDestroy The wrapped pooled object to destroy + * + * @throws Exception If the factory fails to destroy the pooled object + * cleanly + */ + private void destroy(final PooledObject toDestroy) throws Exception { + toDestroy.invalidate(); + idleObjects.remove(toDestroy); + allObjects.remove(new IdentityWrapper(toDestroy.getObject())); + try { + factory.destroyObject(toDestroy); + } finally { + destroyedCount.incrementAndGet(); + createCount.decrementAndGet(); + } + } + + @Override + void ensureMinIdle() throws Exception { + ensureIdle(getMinIdle(), true); + } + + /** + * Tries to ensure that {@code idleCount} idle instances exist in the pool. + *

+ * Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount} + * or the total number of objects (idle, checked out, or being created) reaches + * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless + * there are threads waiting to check out instances from the pool. + * + * @param idleCount the number of idle instances desired + * @param always true means create instances even if the pool has no threads waiting + * @throws Exception if the factory's makeObject throws + */ + private void ensureIdle(final int idleCount, final boolean always) throws Exception { + if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) { + return; + } + + while (idleObjects.size() < idleCount) { + final PooledObject p = create(); + if (p == null) { + // Can't create objects, no reason to think another call to + // create will work. Give up. + break; + } + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(); + } + } + + /** + * Create an object, and place it into the pool. addObject() is useful for + * "pre-loading" a pool with idle objects. + *

+ * If there is no capacity available to add to the pool, this is a no-op + * (no exception, no impact to the pool).

+ */ + @Override + public void addObject() throws Exception { + assertOpen(); + if (factory == null) { + throw new IllegalStateException( + "Cannot add objects without a factory."); + } + final PooledObject p = create(); + addIdleObject(p); + } + + /** + * Add the provided wrapped pooled object to the set of idle objects for + * this pool. The object must already be part of the pool. If {@code p} + * is null, this is a no-op (no exception, but no impact on the pool). + * + * @param p The object to make idle + * + * @throws Exception If the factory fails to passivate the object + */ + private void addIdleObject(final PooledObject p) throws Exception { + if (p != null) { + factory.passivateObject(p); + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + } + } + + /** + * Calculate the number of objects to test in a run of the idle object + * evictor. + * + * @return The number of objects to test for validity + */ + private int getNumTests() { + final int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); + if (numTestsPerEvictionRun >= 0) { + return Math.min(numTestsPerEvictionRun, idleObjects.size()); + } + return (int) (Math.ceil(idleObjects.size() / + Math.abs((double) numTestsPerEvictionRun))); + } + + /** + * Recover abandoned objects which have been checked out but + * not used since longer than the removeAbandonedTimeout. + * + * @param ac The configuration to use to identify abandoned objects + */ + private void removeAbandoned(final AbandonedConfig ac) { + // Generate a list of abandoned objects to remove + final long now = System.currentTimeMillis(); + final long timeout = + now - (ac.getRemoveAbandonedTimeout() * 1000L); + final ArrayList> remove = new ArrayList>(); + final Iterator> it = allObjects.values().iterator(); + while (it.hasNext()) { + final PooledObject pooledObject = it.next(); + synchronized (pooledObject) { + if (pooledObject.getState() == PooledObjectState.ALLOCATED && + pooledObject.getLastUsedTime() <= timeout) { + pooledObject.markAbandoned(); + remove.add(pooledObject); + } + } + } + + // Now remove the abandoned objects + final Iterator> itr = remove.iterator(); + while (itr.hasNext()) { + final PooledObject pooledObject = itr.next(); + if (ac.getLogAbandoned()) { + pooledObject.printStackTrace(ac.getLogWriter()); + } + try { + invalidateObject(pooledObject.getObject()); + } catch (final Exception e) { + e.printStackTrace(); + } + } + } + + + //--- Usage tracking support ----------------------------------------------- + + @Override + public void use(final T pooledObject) { + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getUseUsageTracking()) { + final PooledObject wrapper = allObjects.get(new IdentityWrapper(pooledObject)); + wrapper.use(); + } + } + + + //--- JMX support ---------------------------------------------------------- + + private volatile String factoryType = null; + + /** + * Return an estimate of the number of threads currently blocked waiting for + * an object from the pool. This is intended for monitoring only, not for + * synchronization control. + * + * @return The estimate of the number of threads currently blocked waiting + * for an object from the pool + */ + @Override + public int getNumWaiters() { + if (getBlockWhenExhausted()) { + return idleObjects.getTakeQueueLength(); + } + return 0; + } + + /** + * Return the type - including the specific type rather than the generic - + * of the factory. + * + * @return A string representation of the factory type + */ + @Override + public String getFactoryType() { + // Not thread safe. Accept that there may be multiple evaluations. + if (factoryType == null) { + final StringBuilder result = new StringBuilder(); + result.append(factory.getClass().getName()); + result.append('<'); + final Class pooledObjectType = + PoolImplUtils.getFactoryType(factory.getClass()); + result.append(pooledObjectType.getName()); + result.append('>'); + factoryType = result.toString(); + } + return factoryType; + } + + /** + * Provides information on all the objects in the pool, both idle (waiting + * to be borrowed) and active (currently borrowed). + *

+ * Note: This is named listAllObjects so it is presented as an operation via + * JMX. That means it won't be invoked unless the explicitly requested + * whereas all attributes will be automatically requested when viewing the + * attributes for an object in a tool like JConsole. + * + * @return Information grouped on all the objects in the pool + */ + @Override + public Set listAllObjects() { + final Set result = + new HashSet(allObjects.size()); + for (final PooledObject p : allObjects.values()) { + result.add(new DefaultPooledObjectInfo(p)); + } + return result; + } + + // --- configuration attributes -------------------------------------------- + + private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE; + private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE; + private final PooledObjectFactory factory; + + + // --- internal attributes ------------------------------------------------- + + /* + * All of the objects currently associated with this pool in any state. It + * excludes objects that have been destroyed. The size of + * {@link #allObjects} will always be less than or equal to {@link + * #_maxActive}. Map keys are pooled objects, values are the PooledObject + * wrappers used internally by the pool. + */ + private final Map, PooledObject> allObjects = + new ConcurrentHashMap, PooledObject>(); + /* + * The combined count of the currently created objects and those in the + * process of being created. Under load, it may exceed {@link #_maxActive} + * if multiple threads try and create a new object at the same time but + * {@link #create()} will ensure that there are never more than + * {@link #_maxActive} objects created at any one time. + */ + private final AtomicLong createCount = new AtomicLong(0); + private long makeObjectCount = 0; + private final Object makeObjectCountLock = new Object(); + private final LinkedBlockingDeque> idleObjects; + + // JMX specific attributes + private static final String ONAME_BASE = + "org.apache.commons.pool2:type=GenericObjectPool,name="; + + // Additional configuration properties for abandoned object tracking + private volatile AbandonedConfig abandonedConfig = null; + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + super.toStringAppendFields(builder); + builder.append(", factoryType="); + builder.append(factoryType); + builder.append(", maxIdle="); + builder.append(maxIdle); + builder.append(", minIdle="); + builder.append(minIdle); + builder.append(", factory="); + builder.append(factory); + builder.append(", allObjects="); + builder.append(allObjects); + builder.append(", createCount="); + builder.append(createCount); + builder.append(", idleObjects="); + builder.append(idleObjects); + builder.append(", abandonedConfig="); + builder.append(abandonedConfig); + } + +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolConfig.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolConfig.java new file mode 100755 index 000000000..02b4fa3cf --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolConfig.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +/** + * A simple "struct" encapsulating the configuration for a + * {@link GenericObjectPool}. + * + *

+ * This class is not thread-safe; it is only intended to be used to provide + * attributes used when creating a pool. + * + * @since 2.0 + */ +public class GenericObjectPoolConfig extends BaseObjectPoolConfig { + + /** + * The default value for the {@code maxTotal} configuration attribute. + * @see GenericObjectPool#getMaxTotal() + */ + public static final int DEFAULT_MAX_TOTAL = 8; + + /** + * The default value for the {@code maxIdle} configuration attribute. + * @see GenericObjectPool#getMaxIdle() + */ + public static final int DEFAULT_MAX_IDLE = 8; + + /** + * The default value for the {@code minIdle} configuration attribute. + * @see GenericObjectPool#getMinIdle() + */ + public static final int DEFAULT_MIN_IDLE = 0; + + + private int maxTotal = DEFAULT_MAX_TOTAL; + + private int maxIdle = DEFAULT_MAX_IDLE; + + private int minIdle = DEFAULT_MIN_IDLE; + + /** + * Get the value for the {@code maxTotal} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code maxTotal} for this + * configuration instance + * + * @see GenericObjectPool#getMaxTotal() + */ + public int getMaxTotal() { + return maxTotal; + } + + /** + * Set the value for the {@code maxTotal} configuration attribute for + * pools created with this configuration instance. + * + * @param maxTotal The new setting of {@code maxTotal} + * for this configuration instance + * + * @see GenericObjectPool#setMaxTotal(int) + */ + public void setMaxTotal(final int maxTotal) { + this.maxTotal = maxTotal; + } + + + /** + * Get the value for the {@code maxIdle} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code maxIdle} for this + * configuration instance + * + * @see GenericObjectPool#getMaxIdle() + */ + public int getMaxIdle() { + return maxIdle; + } + + /** + * Set the value for the {@code maxIdle} configuration attribute for + * pools created with this configuration instance. + * + * @param maxIdle The new setting of {@code maxIdle} + * for this configuration instance + * + * @see GenericObjectPool#setMaxIdle(int) + */ + public void setMaxIdle(final int maxIdle) { + this.maxIdle = maxIdle; + } + + + /** + * Get the value for the {@code minIdle} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code minIdle} for this + * configuration instance + * + * @see GenericObjectPool#getMinIdle() + */ + public int getMinIdle() { + return minIdle; + } + + /** + * Set the value for the {@code minIdle} configuration attribute for + * pools created with this configuration instance. + * + * @param minIdle The new setting of {@code minIdle} + * for this configuration instance + * + * @see GenericObjectPool#setMinIdle(int) + */ + public void setMinIdle(final int minIdle) { + this.minIdle = minIdle; + } + + @Override + public GenericObjectPoolConfig clone() { + try { + return (GenericObjectPoolConfig) super.clone(); + } catch (final CloneNotSupportedException e) { + throw new AssertionError(); // Can't happen + } + } + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + super.toStringAppendFields(builder); + builder.append(", maxTotal="); + builder.append(maxTotal); + builder.append(", maxIdle="); + builder.append(maxIdle); + builder.append(", minIdle="); + builder.append(minIdle); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolMXBean.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolMXBean.java new file mode 100755 index 000000000..68045f6e3 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/GenericObjectPoolMXBean.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.util.Set; + +/** + * Defines the methods that will be made available via JMX. + * + * NOTE: This interface exists only to define those attributes and methods that + * will be made available via JMX. It must not be implemented by clients + * as it is subject to change between major, minor and patch version + * releases of commons pool. Clients that implement this interface may + * not, therefore, be able to upgrade to a new minor or patch release + * without requiring code changes. + * + * @since 2.0 + */ +public interface GenericObjectPoolMXBean { + // Getters for basic configuration settings + /** + * See {@link GenericObjectPool#getBlockWhenExhausted()} + * @return See {@link GenericObjectPool#getBlockWhenExhausted()} + */ + boolean getBlockWhenExhausted(); + /** + * See {@link GenericObjectPool#getLifo()} + * @return See {@link GenericObjectPool#getLifo()} + */ + boolean getFairness(); + /** + * See {@link GenericObjectPool#getFairness()} + * @return See {@link GenericObjectPool#getFairness()} + */ + boolean getLifo(); + /** + * See {@link GenericObjectPool#getMaxIdle()} + * @return See {@link GenericObjectPool#getMaxIdle()} + */ + int getMaxIdle(); + /** + * See {@link GenericObjectPool#getMaxTotal()} + * @return See {@link GenericObjectPool#getMaxTotal()} + */ + int getMaxTotal(); + /** + * See {@link GenericObjectPool#getMaxWaitMillis()} + * @return See {@link GenericObjectPool#getMaxWaitMillis()} + */ + long getMaxWaitMillis(); + /** + * See {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} + * @return See {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} + */ + long getMinEvictableIdleTimeMillis(); + /** + * See {@link GenericObjectPool#getMinIdle()} + * @return See {@link GenericObjectPool#getMinIdle()} + */ + int getMinIdle(); + /** + * See {@link GenericObjectPool#getNumActive()} + * @return See {@link GenericObjectPool#getNumActive()} + */ + int getNumActive(); + /** + * See {@link GenericObjectPool#getNumIdle()} + * @return See {@link GenericObjectPool#getNumIdle()} + */ + int getNumIdle(); + /** + * See {@link GenericObjectPool#getNumTestsPerEvictionRun()} + * @return See {@link GenericObjectPool#getNumTestsPerEvictionRun()} + */ + int getNumTestsPerEvictionRun(); + /** + * See {@link GenericObjectPool#getTestOnCreate()} + * @return See {@link GenericObjectPool#getTestOnCreate()} + * @since 2.2 + */ + boolean getTestOnCreate(); + /** + * See {@link GenericObjectPool#getTestOnBorrow()} + * @return See {@link GenericObjectPool#getTestOnBorrow()} + */ + boolean getTestOnBorrow(); + /** + * See {@link GenericObjectPool#getTestOnReturn()} + * @return See {@link GenericObjectPool#getTestOnReturn()} + */ + boolean getTestOnReturn(); + /** + * See {@link GenericObjectPool#getTestWhileIdle()} + * @return See {@link GenericObjectPool#getTestWhileIdle()} + */ + boolean getTestWhileIdle(); + /** + * See {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} + * @return See {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis()} + */ + long getTimeBetweenEvictionRunsMillis(); + /** + * See {@link GenericObjectPool#isClosed()} + * @return See {@link GenericObjectPool#isClosed()} + */ + boolean isClosed(); + // Getters for monitoring attributes + /** + * See {@link GenericObjectPool#getBorrowedCount()} + * @return See {@link GenericObjectPool#getBorrowedCount()} + */ + long getBorrowedCount(); + /** + * See {@link GenericObjectPool#getReturnedCount()} + * @return See {@link GenericObjectPool#getReturnedCount()} + */ + long getReturnedCount(); + /** + * See {@link GenericObjectPool#getCreatedCount()} + * @return See {@link GenericObjectPool#getCreatedCount()} + */ + long getCreatedCount(); + /** + * See {@link GenericObjectPool#getDestroyedCount()} + * @return See {@link GenericObjectPool#getDestroyedCount()} + */ + long getDestroyedCount(); + /** + * See {@link GenericObjectPool#getDestroyedByEvictorCount()} + * @return See {@link GenericObjectPool#getDestroyedByEvictorCount()} + */ + long getDestroyedByEvictorCount(); + /** + * See {@link GenericObjectPool#getDestroyedByBorrowValidationCount()} + * @return See {@link GenericObjectPool#getDestroyedByBorrowValidationCount()} + */ + long getDestroyedByBorrowValidationCount(); + /** + * See {@link GenericObjectPool#getMeanActiveTimeMillis()} + * @return See {@link GenericObjectPool#getMeanActiveTimeMillis()} + */ + long getMeanActiveTimeMillis(); + /** + * See {@link GenericObjectPool#getMeanIdleTimeMillis()} + * @return See {@link GenericObjectPool#getMeanIdleTimeMillis()} + */ + long getMeanIdleTimeMillis(); + /** + * See {@link GenericObjectPool#getMeanBorrowWaitTimeMillis()} + * @return See {@link GenericObjectPool#getMeanBorrowWaitTimeMillis()} + */ + long getMeanBorrowWaitTimeMillis(); + /** + * See {@link GenericObjectPool#getMaxBorrowWaitTimeMillis()} + * @return See {@link GenericObjectPool#getMaxBorrowWaitTimeMillis()} + */ + long getMaxBorrowWaitTimeMillis(); + /** + * See {@link GenericObjectPool#getCreationStackTrace()} + * @return See {@link GenericObjectPool#getCreationStackTrace()} + */ + String getCreationStackTrace(); + /** + * See {@link GenericObjectPool#getNumWaiters()} + * @return See {@link GenericObjectPool#getNumWaiters()} + */ + int getNumWaiters(); + + // Getters for abandoned object removal configuration + /** + * See {@link GenericObjectPool#isAbandonedConfig()} + * @return See {@link GenericObjectPool#isAbandonedConfig()} + */ + boolean isAbandonedConfig(); + /** + * See {@link GenericObjectPool#getLogAbandoned()} + * @return See {@link GenericObjectPool#getLogAbandoned()} + */ + boolean getLogAbandoned(); + /** + * See {@link GenericObjectPool#getRemoveAbandonedOnBorrow()} + * @return See {@link GenericObjectPool#getRemoveAbandonedOnBorrow()} + */ + boolean getRemoveAbandonedOnBorrow(); + /** + * See {@link GenericObjectPool#getRemoveAbandonedOnMaintenance()} + * @return See {@link GenericObjectPool#getRemoveAbandonedOnMaintenance()} + */ + boolean getRemoveAbandonedOnMaintenance(); + /** + * See {@link GenericObjectPool#getRemoveAbandonedTimeout()} + * @return See {@link GenericObjectPool#getRemoveAbandonedTimeout()} + */ + int getRemoveAbandonedTimeout(); + /** + * See {@link GenericObjectPool#getFactoryType()} + * @return See {@link GenericObjectPool#getFactoryType()} + */ + public String getFactoryType(); + /** + * See {@link GenericObjectPool#listAllObjects()} + * @return See {@link GenericObjectPool#listAllObjects()} + */ + Set listAllObjects(); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/InterruptibleReentrantLock.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/InterruptibleReentrantLock.java new file mode 100755 index 000000000..636bf9dbf --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/InterruptibleReentrantLock.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.util.Collection; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * This sub-class was created to expose the waiting threads so that they can be + * interrupted when the pool using the queue that uses this lock is closed. The + * class is intended for internal use only. + *

+ * This class is intended to be thread-safe. + * + * @since 2.0 + */ +class InterruptibleReentrantLock extends ReentrantLock { + + private static final long serialVersionUID = 1L; + + /** + * Create a new InterruptibleReentrantLock with the given fairness policy. + * + * @param fairness true means threads should acquire contended locks as if + * waiting in a FIFO queue + */ + public InterruptibleReentrantLock(final boolean fairness) { + super(fairness); + } + + /** + * Interrupt the threads that are waiting on a specific condition + * + * @param condition the condition on which the threads are waiting. + */ + public void interruptWaiters(final Condition condition) { + final Collection threads = getWaitingThreads(condition); + for (final Thread thread : threads) { + thread.interrupt(); + } + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/LinkedBlockingDeque.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/LinkedBlockingDeque.java new file mode 100755 index 000000000..e8eea1aa7 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/LinkedBlockingDeque.java @@ -0,0 +1,1425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.io.Serializable; +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; + +/** + * An optionally-bounded {@linkplain java.util.concurrent.BlockingDeque blocking + * deque} based on linked nodes. + * + *

The optional capacity bound constructor argument serves as a + * way to prevent excessive expansion. The capacity, if unspecified, + * is equal to {@link Integer#MAX_VALUE}. Linked nodes are + * dynamically created upon each insertion unless this would bring the + * deque above capacity. + * + *

Most operations run in constant time (ignoring time spent + * blocking). Exceptions include {@link #remove(Object) remove}, + * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link + * #removeLastOccurrence removeLastOccurrence}, {@link #contains + * contains}, {@link #iterator iterator.remove()}, and the bulk + * operations, all of which run in linear time. + * + *

This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @param the type of elements held in this collection + * + * Note: This was copied from Apache Harmony and modified to suit the needs of + * Commons Pool. + * + * @since 2.0 + */ +class LinkedBlockingDeque extends AbstractQueue + implements Deque, Serializable { + + /* + * Implemented as a simple doubly-linked list protected by a + * single lock and using conditions to manage blocking. + * + * To implement weakly consistent iterators, it appears we need to + * keep all Nodes GC-reachable from a predecessor dequeued Node. + * That would cause two problems: + * - allow a rogue Iterator to cause unbounded memory retention + * - cause cross-generational linking of old Nodes to new Nodes if + * a Node was tenured while live, which generational GCs have a + * hard time dealing with, causing repeated major collections. + * However, only non-deleted Nodes need to be reachable from + * dequeued Nodes, and reachability does not necessarily have to + * be of the kind understood by the GC. We use the trick of + * linking a Node that has just been dequeued to itself. Such a + * self-link implicitly means to jump to "first" (for next links) + * or "last" (for prev links). + */ + + /* + * We have "diamond" multiple interface/abstract class inheritance + * here, and that introduces ambiguities. Often we want the + * BlockingDeque javadoc combined with the AbstractQueue + * implementation, so a lot of method specs are duplicated here. + */ + + private static final long serialVersionUID = -387911632671998426L; + + /** + * Doubly-linked list node class. + * + * @param node item type + */ + private static final class Node { + /** + * The item, or null if this node has been removed. + */ + E item; + + /** + * One of: + * - the real predecessor Node + * - this Node, meaning the predecessor is tail + * - null, meaning there is no predecessor + */ + Node prev; + + /** + * One of: + * - the real successor Node + * - this Node, meaning the successor is head + * - null, meaning there is no successor + */ + Node next; + + /** + * Create a new list node. + * + * @param x The list item + * @param p Previous item + * @param n Next item + */ + Node(final E x, final Node p, final Node n) { + item = x; + prev = p; + next = n; + } + } + + /** + * Pointer to first node. + * Invariant: (first == null && last == null) || + * (first.prev == null && first.item != null) + */ + private transient Node first; // @GuardedBy("lock") + + /** + * Pointer to last node. + * Invariant: (first == null && last == null) || + * (last.next == null && last.item != null) + */ + private transient Node last; // @GuardedBy("lock") + + /** Number of items in the deque */ + private transient int count; // @GuardedBy("lock") + + /** Maximum number of items in the deque */ + private final int capacity; + + /** Main lock guarding all access */ + private final InterruptibleReentrantLock lock; + + /** Condition for waiting takes */ + private final Condition notEmpty; + + /** Condition for waiting puts */ + private final Condition notFull; + + /** + * Creates a {@code LinkedBlockingDeque} with a capacity of + * {@link Integer#MAX_VALUE}. + */ + public LinkedBlockingDeque() { + this(Integer.MAX_VALUE); + } + + /** + * Creates a {@code LinkedBlockingDeque} with a capacity of + * {@link Integer#MAX_VALUE} and the given fairness policy. + * @param fairness true means threads waiting on the deque should be served + * as if waiting in a FIFO request queue + */ + public LinkedBlockingDeque(final boolean fairness) { + this(Integer.MAX_VALUE, fairness); + } + + /** + * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity. + * + * @param capacity the capacity of this deque + * @throws IllegalArgumentException if {@code capacity} is less than 1 + */ + public LinkedBlockingDeque(final int capacity) { + this(capacity, false); + } + + /** + * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity + * and fairness policy. + * + * @param capacity the capacity of this deque + * @param fairness true means threads waiting on the deque should be served + * as if waiting in a FIFO request queue + * @throws IllegalArgumentException if {@code capacity} is less than 1 + */ + public LinkedBlockingDeque(final int capacity, final boolean fairness) { + if (capacity <= 0) { + throw new IllegalArgumentException(); + } + this.capacity = capacity; + lock = new InterruptibleReentrantLock(fairness); + notEmpty = lock.newCondition(); + notFull = lock.newCondition(); + } + + /** + * Creates a {@code LinkedBlockingDeque} with a capacity of + * {@link Integer#MAX_VALUE}, initially containing the elements of + * the given collection, added in traversal order of the + * collection's iterator. + * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + public LinkedBlockingDeque(final Collection c) { + this(Integer.MAX_VALUE); + lock.lock(); // Never contended, but necessary for visibility + try { + for (final E e : c) { + if (e == null) { + throw new NullPointerException(); + } + if (!linkLast(e)) { + throw new IllegalStateException("Deque full"); + } + } + } finally { + lock.unlock(); + } + } + + + // Basic linking and unlinking operations, called only while holding lock + + /** + * Links provided element as first element, or returns false if full. + * + * @param e The element to link as the first element. + * + * @return {@code true} if successful, otherwise {@code false} + */ + private boolean linkFirst(final E e) { + // assert lock.isHeldByCurrentThread(); + if (count >= capacity) { + return false; + } + final Node f = first; + final Node x = new Node(e, null, f); + first = x; + if (last == null) { + last = x; + } else { + f.prev = x; + } + ++count; + notEmpty.signal(); + return true; + } + + /** + * Links provided element as last element, or returns false if full. + * + * @param e The element to link as the last element. + * + * @return {@code true} if successful, otherwise {@code false} + */ + private boolean linkLast(final E e) { + // assert lock.isHeldByCurrentThread(); + if (count >= capacity) { + return false; + } + final Node l = last; + final Node x = new Node(e, l, null); + last = x; + if (first == null) { + first = x; + } else { + l.next = x; + } + ++count; + notEmpty.signal(); + return true; + } + + /** + * Removes and returns the first element, or null if empty. + * + * @return The first element or {@code null} if empty + */ + private E unlinkFirst() { + // assert lock.isHeldByCurrentThread(); + final Node f = first; + if (f == null) { + return null; + } + final Node n = f.next; + final E item = f.item; + f.item = null; + f.next = f; // help GC + first = n; + if (n == null) { + last = null; + } else { + n.prev = null; + } + --count; + notFull.signal(); + return item; + } + + /** + * Removes and returns the last element, or null if empty. + * + * @return The first element or {@code null} if empty + */ + private E unlinkLast() { + // assert lock.isHeldByCurrentThread(); + final Node l = last; + if (l == null) { + return null; + } + final Node p = l.prev; + final E item = l.item; + l.item = null; + l.prev = l; // help GC + last = p; + if (p == null) { + first = null; + } else { + p.next = null; + } + --count; + notFull.signal(); + return item; + } + + /** + * Unlinks the provided node. + * + * @param x The node to unlink + */ + private void unlink(final Node x) { + // assert lock.isHeldByCurrentThread(); + final Node p = x.prev; + final Node n = x.next; + if (p == null) { + unlinkFirst(); + } else if (n == null) { + unlinkLast(); + } else { + p.next = n; + n.prev = p; + x.item = null; + // Don't mess with x's links. They may still be in use by + // an iterator. + --count; + notFull.signal(); + } + } + + // BlockingDeque methods + + /** + * {@inheritDoc} + */ + @Override + public void addFirst(final E e) { + if (!offerFirst(e)) { + throw new IllegalStateException("Deque full"); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addLast(final E e) { + if (!offerLast(e)) { + throw new IllegalStateException("Deque full"); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean offerFirst(final E e) { + if (e == null) { + throw new NullPointerException(); + } + lock.lock(); + try { + return linkFirst(e); + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean offerLast(final E e) { + if (e == null) { + throw new NullPointerException(); + } + lock.lock(); + try { + return linkLast(e); + } finally { + lock.unlock(); + } + } + + /** + * Links the provided element as the first in the queue, waiting until there + * is space to do so if the queue is full. + * + * @param e element to link + * + * @throws NullPointerException if e is null + * @throws InterruptedException if the thread is interrupted whilst waiting + * for space + */ + public void putFirst(final E e) throws InterruptedException { + if (e == null) { + throw new NullPointerException(); + } + lock.lock(); + try { + while (!linkFirst(e)) { + notFull.await(); + } + } finally { + lock.unlock(); + } + } + + /** + * Links the provided element as the last in the queue, waiting until there + * is space to do so if the queue is full. + * + * @param e element to link + * + * @throws NullPointerException if e is null + * @throws InterruptedException if the thread is interrupted whilst waiting + * for space + */ + public void putLast(final E e) throws InterruptedException { + if (e == null) { + throw new NullPointerException(); + } + lock.lock(); + try { + while (!linkLast(e)) { + notFull.await(); + } + } finally { + lock.unlock(); + } + } + + /** + * Links the provided element as the first in the queue, waiting up to the + * specified time to do so if the queue is full. + * + * @param e element to link + * @param timeout length of time to wait + * @param unit units that timeout is expressed in + * + * @return {@code true} if successful, otherwise {@code false} + * + * @throws NullPointerException if e is null + * @throws InterruptedException if the thread is interrupted whilst waiting + * for space + */ + public boolean offerFirst(final E e, final long timeout, final TimeUnit unit) + throws InterruptedException { + if (e == null) { + throw new NullPointerException(); + } + long nanos = unit.toNanos(timeout); + lock.lockInterruptibly(); + try { + while (!linkFirst(e)) { + if (nanos <= 0) { + return false; + } + nanos = notFull.awaitNanos(nanos); + } + return true; + } finally { + lock.unlock(); + } + } + + /** + * Links the provided element as the last in the queue, waiting up to the + * specified time to do so if the queue is full. + * + * @param e element to link + * @param timeout length of time to wait + * @param unit units that timeout is expressed in + * + * @return {@code true} if successful, otherwise {@code false} + * + * @throws NullPointerException if e is null + * @throws InterruptedException if the thread is interrupted whist waiting + * for space + */ + public boolean offerLast(final E e, final long timeout, final TimeUnit unit) + throws InterruptedException { + if (e == null) { + throw new NullPointerException(); + } + long nanos = unit.toNanos(timeout); + lock.lockInterruptibly(); + try { + while (!linkLast(e)) { + if (nanos <= 0) { + return false; + } + nanos = notFull.awaitNanos(nanos); + } + return true; + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public E removeFirst() { + final E x = pollFirst(); + if (x == null) { + throw new NoSuchElementException(); + } + return x; + } + + /** + * {@inheritDoc} + */ + @Override + public E removeLast() { + final E x = pollLast(); + if (x == null) { + throw new NoSuchElementException(); + } + return x; + } + + @Override + public E pollFirst() { + lock.lock(); + try { + return unlinkFirst(); + } finally { + lock.unlock(); + } + } + + @Override + public E pollLast() { + lock.lock(); + try { + return unlinkLast(); + } finally { + lock.unlock(); + } + } + + /** + * Unlinks the first element in the queue, waiting until there is an element + * to unlink if the queue is empty. + * + * @return the unlinked element + * @throws InterruptedException if the current thread is interrupted + */ + public E takeFirst() throws InterruptedException { + lock.lock(); + try { + E x; + while ( (x = unlinkFirst()) == null) { + notEmpty.await(); + } + return x; + } finally { + lock.unlock(); + } + } + + /** + * Unlinks the last element in the queue, waiting until there is an element + * to unlink if the queue is empty. + * + * @return the unlinked element + * @throws InterruptedException if the current thread is interrupted + */ + public E takeLast() throws InterruptedException { + lock.lock(); + try { + E x; + while ( (x = unlinkLast()) == null) { + notEmpty.await(); + } + return x; + } finally { + lock.unlock(); + } + } + + /** + * Unlinks the first element in the queue, waiting up to the specified time + * to do so if the queue is empty. + * + * @param timeout length of time to wait + * @param unit units that timeout is expressed in + * + * @return the unlinked element + * @throws InterruptedException if the current thread is interrupted + */ + public E pollFirst(final long timeout, final TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + lock.lockInterruptibly(); + try { + E x; + while ( (x = unlinkFirst()) == null) { + if (nanos <= 0) { + return null; + } + nanos = notEmpty.awaitNanos(nanos); + } + return x; + } finally { + lock.unlock(); + } + } + + /** + * Unlinks the last element in the queue, waiting up to the specified time + * to do so if the queue is empty. + * + * @param timeout length of time to wait + * @param unit units that timeout is expressed in + * + * @return the unlinked element + * @throws InterruptedException if the current thread is interrupted + */ + public E pollLast(final long timeout, final TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + lock.lockInterruptibly(); + try { + E x; + while ( (x = unlinkLast()) == null) { + if (nanos <= 0) { + return null; + } + nanos = notEmpty.awaitNanos(nanos); + } + return x; + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public E getFirst() { + final E x = peekFirst(); + if (x == null) { + throw new NoSuchElementException(); + } + return x; + } + + /** + * {@inheritDoc} + */ + @Override + public E getLast() { + final E x = peekLast(); + if (x == null) { + throw new NoSuchElementException(); + } + return x; + } + + @Override + public E peekFirst() { + lock.lock(); + try { + return first == null ? null : first.item; + } finally { + lock.unlock(); + } + } + + @Override + public E peekLast() { + lock.lock(); + try { + return last == null ? null : last.item; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeFirstOccurrence(final Object o) { + if (o == null) { + return false; + } + lock.lock(); + try { + for (Node p = first; p != null; p = p.next) { + if (o.equals(p.item)) { + unlink(p); + return true; + } + } + return false; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeLastOccurrence(final Object o) { + if (o == null) { + return false; + } + lock.lock(); + try { + for (Node p = last; p != null; p = p.prev) { + if (o.equals(p.item)) { + unlink(p); + return true; + } + } + return false; + } finally { + lock.unlock(); + } + } + + // BlockingQueue methods + + /** + * {@inheritDoc} + */ + @Override + public boolean add(final E e) { + addLast(e); + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean offer(final E e) { + return offerLast(e); + } + + /** + * Links the provided element as the last in the queue, waiting until there + * is space to do so if the queue is full. + * + *

This method is equivalent to {@link #putLast(Object)}. + * + * @param e element to link + * + * @throws NullPointerException if e is null + * @throws InterruptedException if the thread is interrupted whilst waiting + * for space + */ + public void put(final E e) throws InterruptedException { + putLast(e); + } + + /** + * Links the provided element as the last in the queue, waiting up to the + * specified time to do so if the queue is full. + *

+ * This method is equivalent to {@link #offerLast(Object, long, TimeUnit)} + * + * @param e element to link + * @param timeout length of time to wait + * @param unit units that timeout is expressed in + * + * @return {@code true} if successful, otherwise {@code false} + * + * @throws NullPointerException if e is null + * @throws InterruptedException if the thread is interrupted whilst waiting + * for space + */ + public boolean offer(final E e, final long timeout, final TimeUnit unit) + throws InterruptedException { + return offerLast(e, timeout, unit); + } + + /** + * Retrieves and removes the head of the queue represented by this deque. + * This method differs from {@link #poll poll} only in that it throws an + * exception if this deque is empty. + * + *

This method is equivalent to {@link #removeFirst() removeFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + @Override + public E remove() { + return removeFirst(); + } + + @Override + public E poll() { + return pollFirst(); + } + + /** + * Unlinks the first element in the queue, waiting until there is an element + * to unlink if the queue is empty. + * + *

This method is equivalent to {@link #takeFirst()}. + * + * @return the unlinked element + * @throws InterruptedException if the current thread is interrupted + */ + public E take() throws InterruptedException { + return takeFirst(); + } + + /** + * Unlinks the first element in the queue, waiting up to the specified time + * to do so if the queue is empty. + * + *

This method is equivalent to {@link #pollFirst(long, TimeUnit)}. + * + * @param timeout length of time to wait + * @param unit units that timeout is expressed in + * + * @return the unlinked element + * @throws InterruptedException if the current thread is interrupted + */ + public E poll(final long timeout, final TimeUnit unit) throws InterruptedException { + return pollFirst(timeout, unit); + } + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque. This method differs from {@link #peek peek} only in that + * it throws an exception if this deque is empty. + * + *

This method is equivalent to {@link #getFirst() getFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + @Override + public E element() { + return getFirst(); + } + + @Override + public E peek() { + return peekFirst(); + } + + /** + * Returns the number of additional elements that this deque can ideally + * (in the absence of memory or resource constraints) accept without + * blocking. This is always equal to the initial capacity of this deque + * less the current {@code size} of this deque. + * + *

Note that you cannot always tell if an attempt to insert + * an element will succeed by inspecting {@code remainingCapacity} + * because it may be the case that another thread is about to + * insert or remove an element. + * + * @return The number of additional elements the queue is able to accept + */ + public int remainingCapacity() { + lock.lock(); + try { + return capacity - count; + } finally { + lock.unlock(); + } + } + + /** + * Empty the queue to the specified collection. + * + * @param c The collection to add the elements to + * + * @return number of elements added to the collection + * + * @throws UnsupportedOperationException if the add operation is not + * supported by the specified collection + * @throws ClassCastException if the class of the elements held by this + * collection prevents them from being added to the specified + * collection + * @throws NullPointerException if c is null + * @throws IllegalArgumentException if c is this instance + */ + public int drainTo(final Collection c) { + return drainTo(c, Integer.MAX_VALUE); + } + + /** + * Empty no more than the specified number of elements from the queue to the + * specified collection. + * + * @param c collection to add the elements to + * @param maxElements maximum number of elements to remove from the queue + * + * @return number of elements added to the collection + * @throws UnsupportedOperationException if the add operation is not + * supported by the specified collection + * @throws ClassCastException if the class of the elements held by this + * collection prevents them from being added to the specified + * collection + * @throws NullPointerException if c is null + * @throws IllegalArgumentException if c is this instance + */ + public int drainTo(final Collection c, final int maxElements) { + if (c == null) { + throw new NullPointerException(); + } + if (c == this) { + throw new IllegalArgumentException(); + } + lock.lock(); + try { + final int n = Math.min(maxElements, count); + for (int i = 0; i < n; i++) { + c.add(first.item); // In this order, in case add() throws. + unlinkFirst(); + } + return n; + } finally { + lock.unlock(); + } + } + + // Stack methods + + /** + * {@inheritDoc} + */ + @Override + public void push(final E e) { + addFirst(e); + } + + /** + * {@inheritDoc} + */ + @Override + public E pop() { + return removeFirst(); + } + + // Collection methods + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + *

This method is equivalent to + * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if this deque changed as a result of the call + */ + @Override + public boolean remove(final Object o) { + return removeFirstOccurrence(o); + } + + /** + * Returns the number of elements in this deque. + * + * @return the number of elements in this deque + */ + @Override + public int size() { + lock.lock(); + try { + return count; + } finally { + lock.unlock(); + } + } + + /** + * Returns {@code true} if this deque contains the specified element. + * More formally, returns {@code true} if and only if this deque contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this deque + * @return {@code true} if this deque contains the specified element + */ + @Override + public boolean contains(final Object o) { + if (o == null) { + return false; + } + lock.lock(); + try { + for (Node p = first; p != null; p = p.next) { + if (o.equals(p.item)) { + return true; + } + } + return false; + } finally { + lock.unlock(); + } + } + + /* + * TODO: Add support for more efficient bulk operations. + * + * We don't want to acquire the lock for every iteration, but we + * also want other threads a chance to interact with the + * collection, especially when count is close to capacity. + */ + +// /** +// * Adds all of the elements in the specified collection to this +// * queue. Attempts to addAll of a queue to itself result in +// * {@code IllegalArgumentException}. Further, the behavior of +// * this operation is undefined if the specified collection is +// * modified while the operation is in progress. +// * +// * @param c collection containing elements to be added to this queue +// * @return {@code true} if this queue changed as a result of the call +// * @throws ClassCastException +// * @throws NullPointerException +// * @throws IllegalArgumentException +// * @throws IllegalStateException +// * @see #add(Object) +// */ +// public boolean addAll(Collection c) { +// if (c == null) +// throw new NullPointerException(); +// if (c == this) +// throw new IllegalArgumentException(); +// final ReentrantLock lock = this.lock; +// lock.lock(); +// try { +// boolean modified = false; +// for (E e : c) +// if (linkLast(e)) +// modified = true; +// return modified; +// } finally { +// lock.unlock(); +// } +// } + + /** + * Returns an array containing all of the elements in this deque, in + * proper sequence (from first to last element). + * + *

The returned array will be "safe" in that no references to it are + * maintained by this deque. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this deque + */ + @Override + public Object[] toArray() { + lock.lock(); + try { + final Object[] a = new Object[count]; + int k = 0; + for (Node p = first; p != null; p = p.next) { + a[k++] = p.item; + } + return a; + } finally { + lock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + @Override + public T[] toArray(T[] a) { + lock.lock(); + try { + if (a.length < count) { + a = (T[])java.lang.reflect.Array.newInstance + (a.getClass().getComponentType(), count); + } + int k = 0; + for (Node p = first; p != null; p = p.next) { + a[k++] = (T)p.item; + } + if (a.length > k) { + a[k] = null; + } + return a; + } finally { + lock.unlock(); + } + } + + @Override + public String toString() { + lock.lock(); + try { + return super.toString(); + } finally { + lock.unlock(); + } + } + + /** + * Atomically removes all of the elements from this deque. + * The deque will be empty after this call returns. + */ + @Override + public void clear() { + lock.lock(); + try { + for (Node f = first; f != null; ) { + f.item = null; + final Node n = f.next; + f.prev = null; + f.next = null; + f = n; + } + first = last = null; + count = 0; + notFull.signalAll(); + } finally { + lock.unlock(); + } + } + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * The returned {@code Iterator} is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * + * @return an iterator over the elements in this deque in proper sequence + */ + @Override + public Iterator iterator() { + return new Itr(); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator descendingIterator() { + return new DescendingItr(); + } + + /** + * Base class for Iterators for LinkedBlockingDeque + */ + private abstract class AbstractItr implements Iterator { + /** + * The next node to return in next() + */ + Node next; + + /** + * nextItem holds on to item fields because once we claim that + * an element exists in hasNext(), we must return item read + * under lock (in advance()) even if it was in the process of + * being removed when hasNext() was called. + */ + E nextItem; + + /** + * Node returned by most recent call to next. Needed by remove. + * Reset to null if this element is deleted by a call to remove. + */ + private Node lastRet; + + /** + * Obtain the first node to be returned by the iterator. + * + * @return first node + */ + abstract Node firstNode(); + + /** + * For a given node, obtain the next node to be returned by the + * iterator. + * + * @param n given node + * + * @return next node + */ + abstract Node nextNode(Node n); + + /** + * Create a new iterator. Sets the initial position. + */ + AbstractItr() { + // set to initial position + lock.lock(); + try { + next = firstNode(); + nextItem = next == null ? null : next.item; + } finally { + lock.unlock(); + } + } + + /** + * Returns the successor node of the given non-null, but + * possibly previously deleted, node. + * + * @param n node whose successor is sought + * @return successor node + */ + private Node succ(Node n) { + // Chains of deleted nodes ending in null or self-links + // are possible if multiple interior nodes are removed. + for (;;) { + final Node s = nextNode(n); + if (s == null) { + return null; + } else if (s.item != null) { + return s; + } else if (s == n) { + return firstNode(); + } else { + n = s; + } + } + } + + /** + * Advances next. + */ + void advance() { + lock.lock(); + try { + // assert next != null; + next = succ(next); + nextItem = next == null ? null : next.item; + } finally { + lock.unlock(); + } + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public E next() { + if (next == null) { + throw new NoSuchElementException(); + } + lastRet = next; + final E x = nextItem; + advance(); + return x; + } + + @Override + public void remove() { + final Node n = lastRet; + if (n == null) { + throw new IllegalStateException(); + } + lastRet = null; + lock.lock(); + try { + if (n.item != null) { + unlink(n); + } + } finally { + lock.unlock(); + } + } + } + + /** Forward iterator */ + private class Itr extends AbstractItr { + @Override + Node firstNode() { return first; } + @Override + Node nextNode(final Node n) { return n.next; } + } + + /** Descending iterator */ + private class DescendingItr extends AbstractItr { + @Override + Node firstNode() { return last; } + @Override + Node nextNode(final Node n) { return n.prev; } + } + + /** + * Save the state of this deque to a stream (that is, serialize it). + * + * @serialData The capacity (int), followed by elements (each an + * {@code Object}) in the proper order, followed by a null + * @param s the stream + */ + private void writeObject(final java.io.ObjectOutputStream s) + throws java.io.IOException { + lock.lock(); + try { + // Write out capacity and any hidden stuff + s.defaultWriteObject(); + // Write out all elements in the proper order. + for (Node p = first; p != null; p = p.next) { + s.writeObject(p.item); + } + // Use trailing null as sentinel + s.writeObject(null); + } finally { + lock.unlock(); + } + } + + /** + * Reconstitute this deque from a stream (that is, + * deserialize it). + * @param s the stream + */ + private void readObject(final java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + count = 0; + first = null; + last = null; + // Read in all elements and place in queue + for (;;) { + @SuppressWarnings("unchecked") + final + E item = (E)s.readObject(); + if (item == null) { + break; + } + add(item); + } + } + + // Monitoring methods + + /** + * Returns true if there are threads waiting to take instances from this deque. + * See disclaimer on accuracy in + * {@link java.util.concurrent.locks.ReentrantLock#hasWaiters(Condition)}. + * + * @return true if there is at least one thread waiting on this deque's notEmpty condition. + */ + public boolean hasTakeWaiters() { + lock.lock(); + try { + return lock.hasWaiters(notEmpty); + } finally { + lock.unlock(); + } + } + + /** + * Returns the length of the queue of threads waiting to take instances from this deque. + * See disclaimer on accuracy in {@link ReentrantLock#getWaitQueueLength(Condition)}. + * + * @return number of threads waiting on this deque's notEmpty condition. + */ + public int getTakeQueueLength() { + lock.lock(); + try { + return lock.getWaitQueueLength(notEmpty); + } finally { + lock.unlock(); + } + } + + /** + * Interrupts the threads currently waiting to take an object from the pool. + * See disclaimer on accuracy in + * {@link ReentrantLock#getWaitingThreads(Condition)}. + */ + public void interuptTakeWaiters() { + lock.lock(); + try { + lock.interruptWaiters(notEmpty); + } finally { + lock.unlock(); + } + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/NoOpCallStack.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/NoOpCallStack.java new file mode 100755 index 000000000..e0e1c0da2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/NoOpCallStack.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.io.PrintWriter; + +/** + * CallStack strategy using no-op implementations of all functionality. Can be used by default when abandoned object + * logging is disabled. + * + * @since 2.5 + */ +public class NoOpCallStack implements CallStack { + + public static final CallStack INSTANCE = new NoOpCallStack(); + + private NoOpCallStack() { + } + + @Override + public boolean printStackTrace(PrintWriter writer) { + return false; + } + + @Override + public void fillInStackTrace() { + // no-op + } + + @Override + public void clear() { + // no-op + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PoolImplUtils.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PoolImplUtils.java new file mode 100755 index 000000000..ca230eabd --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PoolImplUtils.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; + +/** + * Implementation specific utilities. + * + * @since 2.0 + */ +class PoolImplUtils { + + /** + * Identifies the concrete type of object that an object factory creates. + * + * @param factory The factory to examine + * + * @return the type of object the factory creates + */ + @SuppressWarnings("rawtypes") + static Class getFactoryType(final Class factory) { + return (Class) getGenericType(PooledObjectFactory.class, factory); + } + + + /** + * Obtain the concrete type used by an implementation of an interface that + * uses a generic type. + * + * @param type The interface that defines a generic type + * @param clazz The class that implements the interface with a concrete type + * @param The interface type + * + * @return concrete type used by the implementation + */ + private static Object getGenericType(final Class type, + final Class clazz) { + + // Look to see if this class implements the generic interface + + // Get all the interfaces + final Type[] interfaces = clazz.getGenericInterfaces(); + for (final Type iface : interfaces) { + // Only need to check interfaces that use generics + if (iface instanceof ParameterizedType) { + final ParameterizedType pi = (ParameterizedType) iface; + // Look for the generic interface + if (pi.getRawType() instanceof Class) { + if (type.isAssignableFrom((Class) pi.getRawType())) { + return getTypeParameter( + clazz, pi.getActualTypeArguments()[0]); + } + } + } + } + + // Interface not found on this class. Look at the superclass. + @SuppressWarnings("unchecked") + final + Class superClazz = + (Class) clazz.getSuperclass(); + + final Object result = getGenericType(type, superClazz); + if (result instanceof Class) { + // Superclass implements interface and defines explicit type for + // generic + return result; + } else if (result instanceof Integer) { + // Superclass implements interface and defines unknown type for + // generic + // Map that unknown type to the generic types defined in this class + final ParameterizedType superClassType = + (ParameterizedType) clazz.getGenericSuperclass(); + return getTypeParameter(clazz, + superClassType.getActualTypeArguments()[ + ((Integer) result).intValue()]); + } else { + // Error will be logged further up the call stack + return null; + } + } + + + /** + * For a generic parameter, return either the Class used or if the type + * is unknown, the index for the type in definition of the class + * + * @param clazz defining class + * @param argType the type argument of interest + * + * @return An instance of {@link Class} representing the type used by the + * type parameter or an instance of {@link Integer} representing + * the index for the type in the definition of the defining class + */ + private static Object getTypeParameter(final Class clazz, final Type argType) { + if (argType instanceof Class) { + return argType; + } + final TypeVariable[] tvs = clazz.getTypeParameters(); + for (int i = 0; i < tvs.length; i++) { + if (tvs[i].equals(argType)) { + return Integer.valueOf(i); + } + } + return null; + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PooledSoftReference.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PooledSoftReference.java new file mode 100755 index 000000000..da6b642d2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/PooledSoftReference.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.lang.ref.SoftReference; + +/** + * Extension of {@link DefaultPooledObject} to wrap pooled soft references. + * + *

This class is intended to be thread-safe.

+ * + * @param the type of the underlying object that the wrapped SoftReference + * refers to. + * + * @since 2.0 + */ +public class PooledSoftReference extends DefaultPooledObject { + + /** SoftReference wrapped by this object */ + private volatile SoftReference reference; + + /** + * Creates a new PooledSoftReference wrapping the provided reference. + * + * @param reference SoftReference to be managed by the pool + */ + public PooledSoftReference(final SoftReference reference) { + super(null); // Null the hard reference in the parent + this.reference = reference; + } + + /** + * Returns the object that the wrapped SoftReference refers to. + *

+ * Note that if the reference has been cleared, this method will return + * null. + * + * @return Object referred to by the SoftReference + */ + @Override + public T getObject() { + return reference.get(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder result = new StringBuilder(); + result.append("Referenced Object: "); + result.append(getObject().toString()); + result.append(", State: "); + synchronized (this) { + result.append(getState().toString()); + } + return result.toString(); + // TODO add other attributes + // TODO encapsulate state and other attribute display in parent + } + + /** + * Returns the SoftReference wrapped by this object. + * + * @return underlying SoftReference + */ + public synchronized SoftReference getReference() { + return reference; + } + + /** + * Sets the wrapped reference. + * + *

This method exists to allow a new, non-registered reference to be + * held by the pool to track objects that have been checked out of the pool. + * The actual parameter should be a reference to the same + * object that {@link #getObject()} returns before calling this method.

+ * + * @param reference new reference + */ + public synchronized void setReference(final SoftReference reference) { + this.reference = reference; + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SecurityManagerCallStack.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SecurityManagerCallStack.java new file mode 100755 index 000000000..b94afe568 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SecurityManagerCallStack.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * CallStack strategy using a {@link SecurityManager}. Obtaining the current call stack is much faster via a + * SecurityManger, but access to the underlying method may be restricted by the current SecurityManager. In environments + * where a SecurityManager cannot be created, {@link ThrowableCallStack} should be used instead. + * + * @see RuntimePermission + * @see SecurityManager#getClassContext() + * @since 2.4.3 + */ +public class SecurityManagerCallStack implements CallStack { + + private final String messageFormat; + //@GuardedBy("dateFormat") + private final DateFormat dateFormat; + private final PrivateSecurityManager securityManager; + + private volatile Snapshot snapshot; + + /** + * Create a new instance. + * + * @param messageFormat message format + * @param useTimestamp whether to format the dates in the output message or not + */ + public SecurityManagerCallStack(final String messageFormat, final boolean useTimestamp) { + this.messageFormat = messageFormat; + this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null; + this.securityManager = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public PrivateSecurityManager run() { + return new PrivateSecurityManager(); + } + }); + } + + @Override + public boolean printStackTrace(final PrintWriter writer) { + final Snapshot snapshotRef = this.snapshot; + if (snapshotRef == null) { + return false; + } + final String message; + if (dateFormat == null) { + message = messageFormat; + } else { + synchronized (dateFormat) { + message = dateFormat.format(Long.valueOf(snapshotRef.timestamp)); + } + } + writer.println(message); + for (final WeakReference> reference : snapshotRef.stack) { + writer.println(reference.get()); + } + return true; + } + + @Override + public void fillInStackTrace() { + snapshot = new Snapshot(securityManager.getCallStack()); + } + + @Override + public void clear() { + snapshot = null; + } + + /** + * A custom security manager. + */ + private static class PrivateSecurityManager extends SecurityManager { + /** + * Get the class stack. + * + * @return class stack + */ + private List>> getCallStack() { + final Class[] classes = getClassContext(); + final List>> stack = new ArrayList>>(classes.length); + for (final Class klass : classes) { + stack.add(new WeakReference>(klass)); + } + return stack; + } + } + + /** + * A snapshot of a class stack. + */ + private static class Snapshot { + private final long timestamp = System.currentTimeMillis(); + private final List>> stack; + + /** + * Create a new snapshot with a class stack. + * + * @param stack class stack + */ + private Snapshot(final List>> stack) { + this.stack = stack; + } + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java new file mode 100755 index 000000000..17cfee166 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java @@ -0,0 +1,453 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import com.fr.third.org.apache.commons.pool2.BaseObjectPool; +import com.fr.third.org.apache.commons.pool2.ObjectPool; +import com.fr.third.org.apache.commons.pool2.PoolUtils; +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; + +/** + * A {@link SoftReference SoftReference} based {@link ObjectPool}. + *

+ * This class is intended to be thread-safe. + * + * @param + * Type of element pooled in this pool. + * + * @since 2.0 + */ +public class SoftReferenceObjectPool extends BaseObjectPool { + + /** Factory to source pooled objects */ + private final PooledObjectFactory factory; + + /** + * Queue of broken references that might be able to be removed from + * _pool. This is used to help {@link #getNumIdle()} be more + * accurate with minimal performance overhead. + */ + private final ReferenceQueue refQueue = new ReferenceQueue(); + + /** Count of instances that have been checkout out to pool clients */ + private int numActive = 0; // @GuardedBy("this") + + /** Total number of instances that have been destroyed */ + private long destroyCount = 0; // @GuardedBy("this") + + + /** Total number of instances that have been created */ + private long createCount = 0; // @GuardedBy("this") + + /** Idle references - waiting to be borrowed */ + private final LinkedBlockingDeque> idleReferences = + new LinkedBlockingDeque>(); + + /** All references - checked out or waiting to be borrowed. */ + private final ArrayList> allReferences = + new ArrayList>(); + + /** + * Create a SoftReferenceObjectPool with the specified factory. + * + * @param factory object factory to use. + */ + public SoftReferenceObjectPool(final PooledObjectFactory factory) { + this.factory = factory; + } + + /** + * Borrow an object from the pool. If there are no idle instances available + * in the pool, the configured factory's + * {@link PooledObjectFactory#makeObject()} method is invoked to create a + * new instance. + *

+ * All instances are {@link PooledObjectFactory#activateObject( + * org.apache.commons.pool2.PooledObject) activated} + * and {@link PooledObjectFactory#validateObject( + * org.apache.commons.pool2.PooledObject) + * validated} before being returned by this method. If validation fails or + * an exception occurs activating or validating an idle instance, the + * failing instance is {@link PooledObjectFactory#destroyObject( + * org.apache.commons.pool2.PooledObject) + * destroyed} and another instance is retrieved from the pool, validated and + * activated. This process continues until either the pool is empty or an + * instance passes validation. If the pool is empty on activation or it does + * not contain any valid instances, the factory's makeObject + * method is used to create a new instance. If the created instance either + * raises an exception on activation or fails validation, + * NoSuchElementException is thrown. Exceptions thrown by + * MakeObject are propagated to the caller; but other than + * ThreadDeath or VirtualMachineError, exceptions + * generated by activation, validation or destroy methods are swallowed + * silently. + * + * @throws NoSuchElementException + * if a valid object cannot be provided + * @throws IllegalStateException + * if invoked on a {@link #close() closed} pool + * @throws Exception + * if an exception occurs creating a new instance + * @return a valid, activated object instance + */ + @SuppressWarnings("null") // ref cannot be null + @Override + public synchronized T borrowObject() throws Exception { + assertOpen(); + T obj = null; + boolean newlyCreated = false; + PooledSoftReference ref = null; + while (null == obj) { + if (idleReferences.isEmpty()) { + if (null == factory) { + throw new NoSuchElementException(); + } + newlyCreated = true; + obj = factory.makeObject().getObject(); + createCount++; + // Do not register with the queue + ref = new PooledSoftReference(new SoftReference(obj)); + allReferences.add(ref); + } else { + ref = idleReferences.pollFirst(); + obj = ref.getObject(); + // Clear the reference so it will not be queued, but replace with a + // a new, non-registered reference so we can still track this object + // in allReferences + ref.getReference().clear(); + ref.setReference(new SoftReference(obj)); + } + if (null != factory && null != obj) { + try { + factory.activateObject(ref); + if (!factory.validateObject(ref)) { + throw new Exception("ValidateObject failed"); + } + } catch (final Throwable t) { + PoolUtils.checkRethrow(t); + try { + destroy(ref); + } catch (final Throwable t2) { + PoolUtils.checkRethrow(t2); + // Swallowed + } finally { + obj = null; + } + if (newlyCreated) { + throw new NoSuchElementException( + "Could not create a validated object, cause: " + + t.getMessage()); + } + } + } + } + numActive++; + ref.allocate(); + return obj; + } + + /** + * Returns an instance to the pool after successful validation and + * passivation. The returning instance is destroyed if any of the following + * are true: + *

    + *
  • the pool is closed
  • + *
  • {@link PooledObjectFactory#validateObject( + * org.apache.commons.pool2.PooledObject) validation} fails + *
  • + *
  • {@link PooledObjectFactory#passivateObject( + * org.apache.commons.pool2.PooledObject) passivation} + * throws an exception
  • + *
+ * Exceptions passivating or destroying instances are silently swallowed. + * Exceptions validating instances are propagated to the client. + * + * @param obj + * instance to return to the pool + */ + @Override + public synchronized void returnObject(final T obj) throws Exception { + boolean success = !isClosed(); + final PooledSoftReference ref = findReference(obj); + if (ref == null) { + throw new IllegalStateException( + "Returned object not currently part of this pool"); + } + if (factory != null) { + if (!factory.validateObject(ref)) { + success = false; + } else { + try { + factory.passivateObject(ref); + } catch (final Exception e) { + success = false; + } + } + } + + final boolean shouldDestroy = !success; + numActive--; + if (success) { + + // Deallocate and add to the idle instance pool + ref.deallocate(); + idleReferences.add(ref); + } + notifyAll(); // numActive has changed + + if (shouldDestroy && factory != null) { + try { + destroy(ref); + } catch (final Exception e) { + // ignored + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void invalidateObject(final T obj) throws Exception { + final PooledSoftReference ref = findReference(obj); + if (ref == null) { + throw new IllegalStateException( + "Object to invalidate is not currently part of this pool"); + } + if (factory != null) { + destroy(ref); + } + numActive--; + notifyAll(); // numActive has changed + } + + /** + * Create an object, and place it into the pool. addObject() is useful for + * "pre-loading" a pool with idle objects. + *

+ * Before being added to the pool, the newly created instance is + * {@link PooledObjectFactory#validateObject( + * org.apache.commons.pool2.PooledObject) validated} and + * {@link PooledObjectFactory#passivateObject( + * org.apache.commons.pool2.PooledObject) passivated}. If + * validation fails, the new instance is + * {@link PooledObjectFactory#destroyObject( + * org.apache.commons.pool2.PooledObject) destroyed}. Exceptions + * generated by the factory makeObject or + * passivate are propagated to the caller. Exceptions + * destroying instances are silently swallowed. + * + * @throws IllegalStateException + * if invoked on a {@link #close() closed} pool + * @throws Exception + * when the {@link #getFactory() factory} has a problem creating + * or passivating an object. + */ + @Override + public synchronized void addObject() throws Exception { + assertOpen(); + if (factory == null) { + throw new IllegalStateException( + "Cannot add objects without a factory."); + } + final T obj = factory.makeObject().getObject(); + createCount++; + // Create and register with the queue + final PooledSoftReference ref = new PooledSoftReference( + new SoftReference(obj, refQueue)); + allReferences.add(ref); + + boolean success = true; + if (!factory.validateObject(ref)) { + success = false; + } else { + factory.passivateObject(ref); + } + + final boolean shouldDestroy = !success; + if (success) { + idleReferences.add(ref); + notifyAll(); // numActive has changed + } + + if (shouldDestroy) { + try { + destroy(ref); + } catch (final Exception e) { + // ignored + } + } + } + + /** + * Returns an approximation not less than the of the number of idle + * instances in the pool. + * + * @return estimated number of idle instances in the pool + */ + @Override + public synchronized int getNumIdle() { + pruneClearedReferences(); + return idleReferences.size(); + } + + /** + * Return the number of instances currently borrowed from this pool. + * + * @return the number of instances currently borrowed from this pool + */ + @Override + public synchronized int getNumActive() { + return numActive; + } + + /** + * Clears any objects sitting idle in the pool. + */ + @Override + public synchronized void clear() { + if (null != factory) { + final Iterator> iter = idleReferences.iterator(); + while (iter.hasNext()) { + try { + final PooledSoftReference ref = iter.next(); + if (null != ref.getObject()) { + factory.destroyObject(ref); + } + } catch (final Exception e) { + // ignore error, keep destroying the rest + } + } + } + idleReferences.clear(); + pruneClearedReferences(); + } + + /** + * Close this pool, and free any resources associated with it. Invokes + * {@link #clear()} to destroy and remove instances in the pool. + *

+ * Calling {@link #addObject} or {@link #borrowObject} after invoking this + * method on a pool will cause them to throw an + * {@link IllegalStateException}. + */ + @Override + public void close() { + super.close(); + clear(); + } + + /** + * Returns the {@link PooledObjectFactory} used by this pool to create and + * manage object instances. + * + * @return the factory + */ + public synchronized PooledObjectFactory getFactory() { + return factory; + } + + /** + * If any idle objects were garbage collected, remove their + * {@link Reference} wrappers from the idle object pool. + */ + private void pruneClearedReferences() { + // Remove wrappers for enqueued references from idle and allReferences lists + removeClearedReferences(idleReferences.iterator()); + removeClearedReferences(allReferences.iterator()); + while (refQueue.poll() != null) { + // empty + } + } + + /** + * Find the PooledSoftReference in allReferences that points to obj. + * + * @param obj returning object + * @return PooledSoftReference wrapping a soft reference to obj + */ + private PooledSoftReference findReference(final T obj) { + final Iterator> iterator = allReferences.iterator(); + while (iterator.hasNext()) { + final PooledSoftReference reference = iterator.next(); + if (reference.getObject() != null && reference.getObject().equals(obj)) { + return reference; + } + } + return null; + } + + /** + * Destroy a {@code PooledSoftReference} and remove it from the idle and all + * references pools. + * + * @param toDestroy PooledSoftReference to destroy + * + * @throws Exception If an error occurs while trying to destroy the object + */ + private void destroy(final PooledSoftReference toDestroy) throws Exception { + toDestroy.invalidate(); + idleReferences.remove(toDestroy); + allReferences.remove(toDestroy); + try { + factory.destroyObject(toDestroy); + } finally { + destroyCount++; + toDestroy.getReference().clear(); + } + } + + /** + * Clears cleared references from iterator's collection + * @param iterator iterator over idle/allReferences + */ + private void removeClearedReferences(final Iterator> iterator) { + PooledSoftReference ref; + while (iterator.hasNext()) { + ref = iterator.next(); + if (ref.getReference() == null || ref.getReference().isEnqueued()) { + iterator.remove(); + } + } + } + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + super.toStringAppendFields(builder); + builder.append(", factory="); + builder.append(factory); + builder.append(", refQueue="); + builder.append(refQueue); + builder.append(", numActive="); + builder.append(numActive); + builder.append(", destroyCount="); + builder.append(destroyCount); + builder.append(", createCount="); + builder.append(createCount); + builder.append(", idleReferences="); + builder.append(idleReferences); + builder.append(", allReferences="); + builder.append(allReferences); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/ThrowableCallStack.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/ThrowableCallStack.java new file mode 100755 index 000000000..32204fd10 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/ThrowableCallStack.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.impl; + +import java.io.PrintWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +/** + * CallStack strategy that uses the stack trace from a {@link Throwable}. This strategy, while slower than the + * SecurityManager implementation, provides call stack method names and other metadata in addition to the call stack + * of classes. + * + * @see Throwable#fillInStackTrace() + * @since 2.4.3 + */ +public class ThrowableCallStack implements CallStack { + + private final String messageFormat; + //@GuardedBy("dateFormat") + private final DateFormat dateFormat; + + private volatile Snapshot snapshot; + + /** + * Create a new instance. + * + * @param messageFormat message format + * @param useTimestamp whether to format the dates in the output message or not + */ + public ThrowableCallStack(final String messageFormat, final boolean useTimestamp) { + this.messageFormat = messageFormat; + this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null; + } + + @Override + public synchronized boolean printStackTrace(final PrintWriter writer) { + final Snapshot snapshotRef = this.snapshot; + if (snapshotRef == null) { + return false; + } + final String message; + if (dateFormat == null) { + message = messageFormat; + } else { + synchronized (dateFormat) { + message = dateFormat.format(Long.valueOf(snapshotRef.timestamp)); + } + } + writer.println(message); + snapshotRef.printStackTrace(writer); + return true; + } + + @Override + public void fillInStackTrace() { + snapshot = new Snapshot(); + } + + @Override + public void clear() { + snapshot = null; + } + + /** + * A snapshot of a throwable. + */ + private static class Snapshot extends Throwable { + private static final long serialVersionUID = 1L; + private final long timestamp = System.currentTimeMillis(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/package.html b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/package.html new file mode 100755 index 000000000..7a0cbb316 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/impl/package.html @@ -0,0 +1,42 @@ + + + + + Package Documentation for org.apache.commons.pool2.impl + + +

+ Object pooling API implementations. +

+

+ {@link org.apache.commons.pool2.impl.GenericObjectPool GenericObjectPool} + ({@link org.apache.commons.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool}) + provides a more robust (but also more complicated) + implementation of {@link org.apache.commons.pool2.ObjectPool ObjectPool} + ({@link org.apache.commons.pool2.KeyedObjectPool KeyedObjectPool}). +

+

+ {@link org.apache.commons.pool2.impl.SoftReferenceObjectPool SoftReferenceObjectPool} + provides a {@link java.lang.ref.SoftReference SoftReference} based + {@link org.apache.commons.pool2.ObjectPool ObjectPool}. +

+

+ See also the {@link org.apache.commons.pool2} package. +

+ + diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/overview.html b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/overview.html new file mode 100755 index 000000000..d79a61048 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/overview.html @@ -0,0 +1,50 @@ + + + + + Overview of the org.apache.commons.pool2 component + + +

+ Generic Object pooling API with several implementations. +

+

+ The org.apache.commons.pool2 package defines a simple + interface for a pool of object instances, and a handful of base + classes that may be useful when creating pool implementations. + The API supports pooling of unique objects which can be requested + via a key as well as pools where all objects are equivalent. +

+

+ The org.apache.commons.pool2.impl package contains + several pool implementations. + {@link org.apache.commons.pool2.impl.GenericObjectPool + GenericObjectPool} has many configuration options and can support + a limited set of objects such as would be useful in a database + connection pool. + {@link org.apache.commons.pool2.impl.SoftReferenceObjectPool + SoftReferenceObjectPool} has no limit on the number of objects in the + pool, but the garbage collector can remove idle objects from the pool + as needed. There is also a keyed version of + {@link org.apache.commons.pool2.impl.GenericObjectPool + GenericObjectPool}, + {@link org.apache.commons.pool2.impl.GenericKeyedObjectPool + GenericKeyedObjectPool} +

+ + \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/package.html b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/package.html new file mode 100755 index 000000000..28573adce --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/package.html @@ -0,0 +1,63 @@ + + + + + Package Documentation for org.apache.commons.pool2 + + +

+ Object pooling API. +

+

+ The org.apache.commons.pool2 package defines a simple + interface for a pool of object instances, and a handful of base + classes that may be useful when creating pool implementations. +

+

+ The pool package itself doesn't define a specific object + pooling implementation, but rather a contract that implementations may + support in order to be fully interchangeable. +

+

+ The pool package separates the way in which instances are + pooled from the way in which they are created, resulting in a pair of + interfaces: +

+
+
{@link org.apache.commons.pool2.ObjectPool ObjectPool}
+
+ defines a simple object pooling interface, with methods for + borrowing instances from and returning them to the pool. +
+
{@link org.apache.commons.pool2.PooledObjectFactory PooledObjectFactory}
+
+ defines lifecycle methods for object instances contained within a pool. + By associating a factory with a pool, the pool can create new object + instances as needed. +
+
+

+ The pool package also provides a keyed pool interface, + which pools instances of multiple types, accessed according to an + arbitrary key. See + {@link org.apache.commons.pool2.KeyedObjectPool KeyedObjectPool} and + {@link org.apache.commons.pool2.KeyedPooledObjectFactory + KeyedPooledObjectFactory}. +

+ + diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/BaseProxyHandler.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/BaseProxyHandler.java new file mode 100755 index 000000000..638603340 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/BaseProxyHandler.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import java.lang.reflect.Method; + +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * Base implementation for object wrappers when using a + * {@link ProxiedObjectPool}. + * + * @param type of the wrapped pooled object + * + * @since 2.0 + */ +class BaseProxyHandler { + + private volatile T pooledObject; + private final UsageTracking usageTracking; + + + /** + * Create a new wrapper for the given pooled object. + * + * @param pooledObject The object to wrap + * @param usageTracking The instance, if any (usually the object pool) to + * be provided with usage tracking information for this + * wrapped object + */ + BaseProxyHandler(final T pooledObject, final UsageTracking usageTracking) { + this.pooledObject = pooledObject; + this.usageTracking = usageTracking; + } + + + /** + * Obtain the wrapped, pooled object. + * + * @return the underlying pooled object + */ + T getPooledObject() { + return pooledObject; + } + + + /** + * Disable the proxy wrapper. Called when the object has been returned to + * the pool. Further use of the wrapper should result in an + * {@link IllegalStateException}. + * + * @return the object that this proxy was wrapping + */ + T disableProxy() { + final T result = pooledObject; + pooledObject = null; + return result; + } + + + /** + * Check that the proxy is still valid (i.e. that {@link #disableProxy()} + * has not been called). + * + * @throws IllegalStateException if {@link #disableProxy()} has been called + */ + void validateProxiedObject() { + if (pooledObject == null) { + throw new IllegalStateException("This object may no longer be " + + "used as it has been returned to the Object Pool."); + } + } + + + /** + * Invoke the given method on the wrapped object. + * + * @param method The method to invoke + * @param args The arguments to the method + * @return The result of the method call + * @throws Throwable If the method invocation fails + */ + Object doInvoke(final Method method, final Object[] args) throws Throwable { + validateProxiedObject(); + final T object = getPooledObject(); + if (usageTracking != null) { + usageTracking.use(object); + } + return method.invoke(object, args); + } + + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()); + builder.append(" [pooledObject="); + builder.append(pooledObject); + builder.append(", usageTracking="); + builder.append(usageTracking); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxyHandler.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxyHandler.java new file mode 100755 index 000000000..d499502aa --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxyHandler.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import java.lang.reflect.Method; + +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +import com.fr.third.net.sf.cglib.proxy.MethodInterceptor; +import com.fr.third.net.sf.cglib.proxy.MethodProxy; + +/** + * CGLib implementation of the proxy handler. + * + * @param type of the wrapped pooled object + * + * @since 2.0 + */ +class CglibProxyHandler extends BaseProxyHandler + implements MethodInterceptor { + + + /** + * Create a CGLib proxy instance. + * + * @param pooledObject The object to wrap + * @param usageTracking The instance, if any (usually the object pool) to + * be provided with usage tracking information for this + * wrapped object + */ + CglibProxyHandler(final T pooledObject, final UsageTracking usageTracking) { + super(pooledObject, usageTracking); + } + + @Override + public Object intercept(final Object object, final Method method, final Object[] args, + final MethodProxy methodProxy) throws Throwable { + return doInvoke(method, args); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxySource.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxySource.java new file mode 100755 index 000000000..9feee6186 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/CglibProxySource.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import com.fr.third.net.sf.cglib.proxy.Enhancer; +import com.fr.third.net.sf.cglib.proxy.Factory; + +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * Provides proxy objects using CGLib. + * + * @param type of the pooled object to be proxied + * + * @since 2.0 + */ +public class CglibProxySource implements ProxySource { + + private final Class superclass; + + /** + * Create a new proxy source for the given class. + * + * @param superclass The class to proxy + */ + public CglibProxySource(final Class superclass) { + this.superclass = superclass; + } + + @Override + public T createProxy(final T pooledObject, final UsageTracking usageTracking) { + final Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(superclass); + + final CglibProxyHandler proxyInterceptor = + new CglibProxyHandler(pooledObject, usageTracking); + enhancer.setCallback(proxyInterceptor); + + @SuppressWarnings("unchecked") + final + T proxy = (T) enhancer.create(); + + return proxy; + } + + + @Override + public T resolveProxy(final T proxy) { + @SuppressWarnings("unchecked") + final + CglibProxyHandler cglibProxyHandler = + (CglibProxyHandler) ((Factory) proxy).getCallback(0); + final T pooledObject = cglibProxyHandler.disableProxy(); + return pooledObject; + } + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("CglibProxySource [superclass="); + builder.append(superclass); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxyHandler.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxyHandler.java new file mode 100755 index 000000000..41d395c4e --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxyHandler.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * Java reflection implementation of the proxy handler. + * + * @param type of the wrapped pooled object + * + * @since 2.0 + */ +class JdkProxyHandler extends BaseProxyHandler + implements InvocationHandler { + + /** + * Create a Java reflection proxy instance. + * + * @param pooledObject The object to wrap + * @param usageTracking The instance, if any (usually the object pool) to + * be provided with usage tracking information for this + * wrapped object + */ + JdkProxyHandler(final T pooledObject, final UsageTracking usageTracking) { + super(pooledObject, usageTracking); + } + + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) + throws Throwable { + return doInvoke(method, args); + } +} + diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxySource.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxySource.java new file mode 100755 index 000000000..5205fb08d --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/JdkProxySource.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import java.lang.reflect.Proxy; +import java.util.Arrays; + +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * Provides proxy objects using Java reflection. + * + * @param type of the pooled object to be proxied + * + * @since 2.0 + */ +public class JdkProxySource implements ProxySource { + + private final ClassLoader classLoader; + private final Class[] interfaces; + + + /** + * Create a new proxy source for the given interfaces. + * + * @param classLoader The class loader with which to create the proxy + * @param interfaces The interfaces to proxy + */ + public JdkProxySource(final ClassLoader classLoader, final Class[] interfaces) { + this.classLoader = classLoader; + // Defensive copy + this.interfaces = new Class[interfaces.length]; + System.arraycopy(interfaces, 0, this.interfaces, 0, interfaces.length); + } + + + @Override + public T createProxy(final T pooledObject, final UsageTracking usageTracking) { + @SuppressWarnings("unchecked") + final + T proxy = (T) Proxy.newProxyInstance(classLoader, interfaces, + new JdkProxyHandler(pooledObject, usageTracking)); + return proxy; + } + + + @Override + public T resolveProxy(final T proxy) { + @SuppressWarnings("unchecked") + final + JdkProxyHandler jdkProxyHandler = + (JdkProxyHandler) Proxy.getInvocationHandler(proxy); + final T pooledObject = jdkProxyHandler.disableProxy(); + return pooledObject; + } + + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("JdkProxySource [classLoader="); + builder.append(classLoader); + builder.append(", interfaces="); + builder.append(Arrays.toString(interfaces)); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedKeyedObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedKeyedObjectPool.java new file mode 100755 index 000000000..bf4af513c --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedKeyedObjectPool.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import java.util.NoSuchElementException; + +import com.fr.third.org.apache.commons.pool2.KeyedObjectPool; +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * Create a new keyed object pool where the pooled objects are wrapped in + * proxies allowing better control of pooled objects and in particular the + * prevention of the continued use of an object by a client after that client + * returns the object to the pool. + * + * @param type of the key + * @param type of the pooled object + * + * @since 2.0 + */ +public class ProxiedKeyedObjectPool implements KeyedObjectPool { + + private final KeyedObjectPool pool; + private final ProxySource proxySource; + + + /** + * Create a new proxied object pool. + * + * @param pool The object pool to wrap + * @param proxySource The source of the proxy objects + */ + public ProxiedKeyedObjectPool(final KeyedObjectPool pool, + final ProxySource proxySource) { + this.pool = pool; + this.proxySource = proxySource; + } + + + @SuppressWarnings("unchecked") + @Override + public V borrowObject(final K key) throws Exception, NoSuchElementException, + IllegalStateException { + UsageTracking usageTracking = null; + if (pool instanceof UsageTracking) { + usageTracking = (UsageTracking) pool; + } + final V pooledObject = pool.borrowObject(key); + final V proxy = proxySource.createProxy(pooledObject, usageTracking); + return proxy; + } + + @Override + public void returnObject(final K key, final V proxy) throws Exception { + final V pooledObject = proxySource.resolveProxy(proxy); + pool.returnObject(key, pooledObject); + } + + @Override + public void invalidateObject(final K key, final V proxy) throws Exception { + final V pooledObject = proxySource.resolveProxy(proxy); + pool.invalidateObject(key, pooledObject); + } + + @Override + public void addObject(final K key) throws Exception, IllegalStateException, + UnsupportedOperationException { + pool.addObject(key); + } + + @Override + public int getNumIdle(final K key) { + return pool.getNumIdle(key); + } + + @Override + public int getNumActive(final K key) { + return pool.getNumActive(key); + } + + @Override + public int getNumIdle() { + return pool.getNumIdle(); + } + + @Override + public int getNumActive() { + return pool.getNumActive(); + } + + @Override + public void clear() throws Exception, UnsupportedOperationException { + pool.clear(); + } + + @Override + public void clear(final K key) throws Exception, UnsupportedOperationException { + pool.clear(key); + } + + @Override + public void close() { + pool.close(); + } + + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("ProxiedKeyedObjectPool [pool="); + builder.append(pool); + builder.append(", proxySource="); + builder.append(proxySource); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedObjectPool.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedObjectPool.java new file mode 100755 index 000000000..90e20bbdd --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxiedObjectPool.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import java.util.NoSuchElementException; + +import com.fr.third.org.apache.commons.pool2.ObjectPool; +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * Create a new object pool where the pooled objects are wrapped in proxies + * allowing better control of pooled objects and in particular the prevention + * of the continued use of an object by a client after that client returns the + * object to the pool. + * + * @param type of the pooled object + * + * @since 2.0 + */ +public class ProxiedObjectPool implements ObjectPool { + + private final ObjectPool pool; + private final ProxySource proxySource; + + + /** + * Create a new proxied object pool. + * + * @param pool The object pool to wrap + * @param proxySource The source of the proxy objects + */ + public ProxiedObjectPool(final ObjectPool pool, final ProxySource proxySource) { + this.pool = pool; + this.proxySource = proxySource; + } + + + // --------------------------------------------------- ObjectPool methods + + @SuppressWarnings("unchecked") + @Override + public T borrowObject() throws Exception, NoSuchElementException, + IllegalStateException { + UsageTracking usageTracking = null; + if (pool instanceof UsageTracking) { + usageTracking = (UsageTracking) pool; + } + final T pooledObject = pool.borrowObject(); + final T proxy = proxySource.createProxy(pooledObject, usageTracking); + return proxy; + } + + + @Override + public void returnObject(final T proxy) throws Exception { + final T pooledObject = proxySource.resolveProxy(proxy); + pool.returnObject(pooledObject); + } + + + @Override + public void invalidateObject(final T proxy) throws Exception { + final T pooledObject = proxySource.resolveProxy(proxy); + pool.invalidateObject(pooledObject); + } + + + @Override + public void addObject() throws Exception, IllegalStateException, + UnsupportedOperationException { + pool.addObject(); + } + + + @Override + public int getNumIdle() { + return pool.getNumIdle(); + } + + + @Override + public int getNumActive() { + return pool.getNumActive(); + } + + + @Override + public void clear() throws Exception, UnsupportedOperationException { + pool.clear(); + } + + + @Override + public void close() { + pool.close(); + } + + + /** + * @since 2.4.3 + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("ProxiedObjectPool [pool="); + builder.append(pool); + builder.append(", proxySource="); + builder.append(proxySource); + builder.append("]"); + return builder.toString(); + } +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxySource.java b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxySource.java new file mode 100755 index 000000000..0e5a55a34 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/ProxySource.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.fr.third.org.apache.commons.pool2.proxy; + +import com.fr.third.org.apache.commons.pool2.UsageTracking; + +/** + * The interface that any provider of proxy instances must implement to allow the + * {@link ProxiedObjectPool} to create proxies as required. + * + * @param type of the pooled object to be proxied + * + * @since 2.0 + */ +interface ProxySource { + + /** + * Create a new proxy object, wrapping the given pooled object. + * + * @param pooledObject The object to wrap + * @param usageTracking The instance, if any (usually the object pool) to + * be provided with usage tracking information for this + * wrapped object + * + * @return the new proxy object + */ + T createProxy(T pooledObject, UsageTracking usageTracking); + + /** + * Obtain the wrapped object from the given proxy. + * + * @param proxy The proxy object + * + * @return The pooled object wrapped by the given proxy + */ + T resolveProxy(T proxy); +} diff --git a/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/package.html b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/package.html new file mode 100755 index 000000000..5f2bf3af4 --- /dev/null +++ b/fine-jedis/src/com/fr/third/org/apache/commons/pool2/proxy/package.html @@ -0,0 +1,37 @@ + + + + Package Documentation for org.apache.commons.pool2.proxy + + +

+ Object pooling proxy implementation. +

+

+ The org.apache.commons.pool2.proxy package defines a + object pool that wraps all objects returned to clients. This allows it + to disable those proxies when the objects are returned thereby enabling + the continued use of those objects by clients to be detected.. +

+

+ Support is provided for java.lang.reflect.Proxy and for + net.sf.cglib.proxy based proxies. The latter, requires the + additional of the optional Code Generation Library (GCLib). +

+ + diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryClient.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryClient.java new file mode 100755 index 000000000..3ecf6fcf9 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryClient.java @@ -0,0 +1,1205 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.toByteArray; +import static com.fr.third.redis.clients.jedis.Protocol.Command.*; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.ENCODING; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.IDLETIME; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.LEN; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.LIMIT; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.NO; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.ONE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.REFCOUNT; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.RESET; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.STORE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.WITHSCORES; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.redis.clients.jedis.Protocol.Command; +import com.fr.third.redis.clients.jedis.Protocol.Keyword; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +public class BinaryClient extends Connection { + + private boolean isInMulti; + + private String password; + + private int db; + + private boolean isInWatch; + + public BinaryClient() { + super(); + } + + public BinaryClient(final String host) { + super(host); + } + + public BinaryClient(final String host, final int port) { + super(host, port); + } + + public BinaryClient(final String host, final int port, final boolean ssl) { + super(host, port, ssl); + } + + public BinaryClient(final String host, final int port, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public boolean isInMulti() { + return isInMulti; + } + + public boolean isInWatch() { + return isInWatch; + } + + private byte[][] joinParameters(byte[] first, byte[][] rest) { + byte[][] result = new byte[rest.length + 1][]; + result[0] = first; + System.arraycopy(rest, 0, result, 1, rest.length); + return result; + } + + private byte[][] joinParameters(byte[] first, byte[] second, byte[][] rest) { + byte[][] result = new byte[rest.length + 2][]; + result[0] = first; + result[1] = second; + System.arraycopy(rest, 0, result, 2, rest.length); + return result; + } + + public void setPassword(final String password) { + this.password = password; + } + + public void setDb(int db) { + this.db = db; + } + + @Override + public void connect() { + if (!isConnected()) { + super.connect(); + if (password != null) { + auth(password); + getStatusCodeReply(); + } + if (db > 0) { + select(db); + getStatusCodeReply(); + } + } + } + + public void ping() { + sendCommand(PING); + } + + public void ping(final byte[] message) { + sendCommand(PING, message); + } + + public void set(final byte[] key, final byte[] value) { + sendCommand(SET, key, value); + } + + public void set(final byte[] key, final byte[] value, final SetParams params) { + sendCommand(SET, params.getByteParams(key, value)); + } + + public void get(final byte[] key) { + sendCommand(GET, key); + } + + public void quit() { + db = 0; + sendCommand(QUIT); + } + + public void exists(final byte[]... keys) { + sendCommand(EXISTS, keys); + } + + public void del(final byte[]... keys) { + sendCommand(DEL, keys); + } + + public void unlink(final byte[]... keys) { + sendCommand(UNLINK, keys); + } + + public void type(final byte[] key) { + sendCommand(TYPE, key); + } + + public void flushDB() { + sendCommand(FLUSHDB); + } + + public void keys(final byte[] pattern) { + sendCommand(KEYS, pattern); + } + + public void randomKey() { + sendCommand(RANDOMKEY); + } + + public void rename(final byte[] oldkey, final byte[] newkey) { + sendCommand(RENAME, oldkey, newkey); + } + + public void renamenx(final byte[] oldkey, final byte[] newkey) { + sendCommand(RENAMENX, oldkey, newkey); + } + + public void dbSize() { + sendCommand(DBSIZE); + } + + public void expire(final byte[] key, final int seconds) { + sendCommand(EXPIRE, key, toByteArray(seconds)); + } + + public void expireAt(final byte[] key, final long unixTime) { + sendCommand(EXPIREAT, key, toByteArray(unixTime)); + } + + public void ttl(final byte[] key) { + sendCommand(TTL, key); + } + + public void touch(final byte[]... keys) { + sendCommand(TOUCH, keys); + } + + public void select(final int index) { + sendCommand(SELECT, toByteArray(index)); + } + + public void swapDB(final int index1, final int index2) { + sendCommand(SWAPDB, toByteArray(index1), toByteArray(index2)); + } + + public void move(final byte[] key, final int dbIndex) { + sendCommand(MOVE, key, toByteArray(dbIndex)); + } + + public void flushAll() { + sendCommand(FLUSHALL); + } + + public void getSet(final byte[] key, final byte[] value) { + sendCommand(GETSET, key, value); + } + + public void mget(final byte[]... keys) { + sendCommand(MGET, keys); + } + + public void setnx(final byte[] key, final byte[] value) { + sendCommand(SETNX, key, value); + } + + public void setex(final byte[] key, final int seconds, final byte[] value) { + sendCommand(SETEX, key, toByteArray(seconds), value); + } + + public void mset(final byte[]... keysvalues) { + sendCommand(MSET, keysvalues); + } + + public void msetnx(final byte[]... keysvalues) { + sendCommand(MSETNX, keysvalues); + } + + public void decrBy(final byte[] key, final long decrement) { + sendCommand(DECRBY, key, toByteArray(decrement)); + } + + public void decr(final byte[] key) { + sendCommand(DECR, key); + } + + public void incrBy(final byte[] key, final long increment) { + sendCommand(INCRBY, key, toByteArray(increment)); + } + + public void incrByFloat(final byte[] key, final double increment) { + sendCommand(INCRBYFLOAT, key, toByteArray(increment)); + } + + public void incr(final byte[] key) { + sendCommand(INCR, key); + } + + public void append(final byte[] key, final byte[] value) { + sendCommand(APPEND, key, value); + } + + public void substr(final byte[] key, final int start, final int end) { + sendCommand(SUBSTR, key, toByteArray(start), toByteArray(end)); + } + + public void hset(final byte[] key, final byte[] field, final byte[] value) { + sendCommand(HSET, key, field, value); + } + + public void hset(final byte[] key, final Map hash) { + final byte[][] params = new byte[1 + hash.size() * 2][]; + + int index = 0; + params[index++] = key; + for (final Entry entry : hash.entrySet()) { + params[index++] = entry.getKey(); + params[index++] = entry.getValue(); + } + sendCommand(HSET, params); + } + + public void hget(final byte[] key, final byte[] field) { + sendCommand(HGET, key, field); + } + + public void hsetnx(final byte[] key, final byte[] field, final byte[] value) { + sendCommand(HSETNX, key, field, value); + } + + public void hmset(final byte[] key, final Map hash) { + final List params = new ArrayList(); + params.add(key); + + for (final Entry entry : hash.entrySet()) { + params.add(entry.getKey()); + params.add(entry.getValue()); + } + sendCommand(HMSET, params.toArray(new byte[params.size()][])); + } + + public void hmget(final byte[] key, final byte[]... fields) { + sendCommand(HMGET, joinParameters(key, fields)); + } + + public void hincrBy(final byte[] key, final byte[] field, final long value) { + sendCommand(HINCRBY, key, field, toByteArray(value)); + } + + public void hexists(final byte[] key, final byte[] field) { + sendCommand(HEXISTS, key, field); + } + + public void hdel(final byte[] key, final byte[]... fields) { + sendCommand(HDEL, joinParameters(key, fields)); + } + + public void hlen(final byte[] key) { + sendCommand(HLEN, key); + } + + public void hkeys(final byte[] key) { + sendCommand(HKEYS, key); + } + + public void hvals(final byte[] key) { + sendCommand(HVALS, key); + } + + public void hgetAll(final byte[] key) { + sendCommand(HGETALL, key); + } + + public void rpush(final byte[] key, final byte[]... strings) { + sendCommand(RPUSH, joinParameters(key, strings)); + } + + public void lpush(final byte[] key, final byte[]... strings) { + sendCommand(LPUSH, joinParameters(key, strings)); + } + + public void llen(final byte[] key) { + sendCommand(LLEN, key); + } + + public void lrange(final byte[] key, final long start, final long stop) { + sendCommand(LRANGE, key, toByteArray(start), toByteArray(stop)); + } + + public void ltrim(final byte[] key, final long start, final long stop) { + sendCommand(LTRIM, key, toByteArray(start), toByteArray(stop)); + } + + public void lindex(final byte[] key, final long index) { + sendCommand(LINDEX, key, toByteArray(index)); + } + + public void lset(final byte[] key, final long index, final byte[] value) { + sendCommand(LSET, key, toByteArray(index), value); + } + + public void lrem(final byte[] key, final long count, final byte[] value) { + sendCommand(LREM, key, toByteArray(count), value); + } + + public void lpop(final byte[] key) { + sendCommand(LPOP, key); + } + + public void rpop(final byte[] key) { + sendCommand(RPOP, key); + } + + public void rpoplpush(final byte[] srckey, final byte[] dstkey) { + sendCommand(RPOPLPUSH, srckey, dstkey); + } + + public void sadd(final byte[] key, final byte[]... members) { + sendCommand(SADD, joinParameters(key, members)); + } + + public void smembers(final byte[] key) { + sendCommand(SMEMBERS, key); + } + + public void srem(final byte[] key, final byte[]... members) { + sendCommand(SREM, joinParameters(key, members)); + } + + public void spop(final byte[] key) { + sendCommand(SPOP, key); + } + + public void spop(final byte[] key, final long count) { + sendCommand(SPOP, key, toByteArray(count)); + } + + public void smove(final byte[] srckey, final byte[] dstkey, final byte[] member) { + sendCommand(SMOVE, srckey, dstkey, member); + } + + public void scard(final byte[] key) { + sendCommand(SCARD, key); + } + + public void sismember(final byte[] key, final byte[] member) { + sendCommand(SISMEMBER, key, member); + } + + public void sinter(final byte[]... keys) { + sendCommand(SINTER, keys); + } + + public void sinterstore(final byte[] dstkey, final byte[]... keys) { + sendCommand(SINTERSTORE, joinParameters(dstkey, keys)); + } + + public void sunion(final byte[]... keys) { + sendCommand(SUNION, keys); + } + + public void sunionstore(final byte[] dstkey, final byte[]... keys) { + sendCommand(SUNIONSTORE, joinParameters(dstkey, keys)); + } + + public void sdiff(final byte[]... keys) { + sendCommand(SDIFF, keys); + } + + public void sdiffstore(final byte[] dstkey, final byte[]... keys) { + sendCommand(SDIFFSTORE, joinParameters(dstkey, keys)); + } + + public void srandmember(final byte[] key) { + sendCommand(SRANDMEMBER, key); + } + + public void zadd(final byte[] key, final double score, final byte[] member) { + sendCommand(ZADD, key, toByteArray(score), member); + } + + public void zadd(final byte[] key, final double score, final byte[] member, + final ZAddParams params) { + sendCommand(ZADD, params.getByteParams(key, toByteArray(score), member)); + } + + public void zadd(final byte[] key, final Map scoreMembers) { + ArrayList args = new ArrayList(scoreMembers.size() * 2 + 1); + args.add(key); + args.addAll(convertScoreMembersToByteArrays(scoreMembers)); + + byte[][] argsArray = new byte[args.size()][]; + args.toArray(argsArray); + + sendCommand(ZADD, argsArray); + } + + public void zadd(final byte[] key, final Map scoreMembers, final ZAddParams params) { + ArrayList args = convertScoreMembersToByteArrays(scoreMembers); + byte[][] argsArray = new byte[args.size()][]; + args.toArray(argsArray); + + sendCommand(ZADD, params.getByteParams(key, argsArray)); + } + + public void zrange(final byte[] key, final long start, final long stop) { + sendCommand(ZRANGE, key, toByteArray(start), toByteArray(stop)); + } + + public void zrem(final byte[] key, final byte[]... members) { + sendCommand(ZREM, joinParameters(key, members)); + } + + public void zincrby(final byte[] key, final double increment, final byte[] member) { + sendCommand(ZINCRBY, key, toByteArray(increment), member); + } + + public void zincrby(final byte[] key, final double increment, final byte[] member, + final ZIncrByParams params) { + // Note that it actually calls ZADD with INCR option, so it requires Redis 3.0.2 or upper. + sendCommand(ZADD, params.getByteParams(key, toByteArray(increment), member)); + } + + public void zrank(final byte[] key, final byte[] member) { + sendCommand(ZRANK, key, member); + } + + public void zrevrank(final byte[] key, final byte[] member) { + sendCommand(ZREVRANK, key, member); + } + + public void zrevrange(final byte[] key, final long start, final long stop) { + sendCommand(ZREVRANGE, key, toByteArray(start), toByteArray(stop)); + } + + public void zrangeWithScores(final byte[] key, final long start, final long stop) { + sendCommand(ZRANGE, key, toByteArray(start), toByteArray(stop), WITHSCORES.raw); + } + + public void zrevrangeWithScores(final byte[] key, final long start, final long stop) { + sendCommand(ZREVRANGE, key, toByteArray(start), toByteArray(stop), WITHSCORES.raw); + } + + public void zcard(final byte[] key) { + sendCommand(ZCARD, key); + } + + public void zscore(final byte[] key, final byte[] member) { + sendCommand(ZSCORE, key, member); + } + + public void multi() { + sendCommand(MULTI); + isInMulti = true; + } + + public void discard() { + sendCommand(DISCARD); + isInMulti = false; + isInWatch = false; + } + + public void exec() { + sendCommand(EXEC); + isInMulti = false; + isInWatch = false; + } + + public void watch(final byte[]... keys) { + sendCommand(WATCH, keys); + isInWatch = true; + } + + public void unwatch() { + sendCommand(UNWATCH); + isInWatch = false; + } + + public void sort(final byte[] key) { + sendCommand(SORT, key); + } + + public void sort(final byte[] key, final SortingParams sortingParameters) { + final List args = new ArrayList(); + args.add(key); + args.addAll(sortingParameters.getParams()); + sendCommand(SORT, args.toArray(new byte[args.size()][])); + } + + public void blpop(final byte[][] args) { + sendCommand(BLPOP, args); + } + + public void blpop(final int timeout, final byte[]... keys) { + final List args = new ArrayList(); + for (final byte[] arg : keys) { + args.add(arg); + } + args.add(Protocol.toByteArray(timeout)); + blpop(args.toArray(new byte[args.size()][])); + } + + public void sort(final byte[] key, final SortingParams sortingParameters, final byte[] dstkey) { + final List args = new ArrayList(); + args.add(key); + args.addAll(sortingParameters.getParams()); + args.add(STORE.raw); + args.add(dstkey); + sendCommand(SORT, args.toArray(new byte[args.size()][])); + } + + public void sort(final byte[] key, final byte[] dstkey) { + sendCommand(SORT, key, STORE.raw, dstkey); + } + + public void brpop(final byte[][] args) { + sendCommand(BRPOP, args); + } + + public void brpop(final int timeout, final byte[]... keys) { + final List args = new ArrayList(); + for (final byte[] arg : keys) { + args.add(arg); + } + args.add(Protocol.toByteArray(timeout)); + brpop(args.toArray(new byte[args.size()][])); + } + + public void auth(final String password) { + setPassword(password); + sendCommand(AUTH, password); + } + + public void subscribe(final byte[]... channels) { + sendCommand(SUBSCRIBE, channels); + } + + public void publish(final byte[] channel, final byte[] message) { + sendCommand(PUBLISH, channel, message); + } + + public void unsubscribe() { + sendCommand(UNSUBSCRIBE); + } + + public void unsubscribe(final byte[]... channels) { + sendCommand(UNSUBSCRIBE, channels); + } + + public void psubscribe(final byte[]... patterns) { + sendCommand(PSUBSCRIBE, patterns); + } + + public void punsubscribe() { + sendCommand(PUNSUBSCRIBE); + } + + public void punsubscribe(final byte[]... patterns) { + sendCommand(PUNSUBSCRIBE, patterns); + } + + public void pubsub(final byte[]... args) { + sendCommand(PUBSUB, args); + } + + public void zcount(final byte[] key, final double min, final double max) { + sendCommand(ZCOUNT, key, toByteArray(min), toByteArray(max)); + } + + public void zcount(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZCOUNT, key, min, max); + } + + public void zrangeByScore(final byte[] key, final double min, final double max) { + sendCommand(ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max)); + } + + public void zrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZRANGEBYSCORE, key, min, max); + } + + public void zrevrangeByScore(final byte[] key, final double max, final double min) { + sendCommand(ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min)); + } + + public void zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min) { + sendCommand(ZREVRANGEBYSCORE, key, max, min); + } + + public void zrangeByScore(final byte[] key, final double min, final double max, final int offset, + final int count) { + sendCommand(ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max), LIMIT.raw, toByteArray(offset), + toByteArray(count)); + } + + public void zrevrangeByScore(final byte[] key, final double max, final double min, + final int offset, final int count) { + sendCommand(ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min), LIMIT.raw, toByteArray(offset), + toByteArray(count)); + } + + public void zrangeByScoreWithScores(final byte[] key, final double min, final double max) { + sendCommand(ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max), WITHSCORES.raw); + } + + public void zrevrangeByScoreWithScores(final byte[] key, final double max, final double min) { + sendCommand(ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min), WITHSCORES.raw); + } + + public void zrangeByScoreWithScores(final byte[] key, final double min, final double max, + final int offset, final int count) { + sendCommand(ZRANGEBYSCORE, key, toByteArray(min), toByteArray(max), LIMIT.raw, toByteArray(offset), + toByteArray(count), WITHSCORES.raw); + } + + public void zrevrangeByScoreWithScores(final byte[] key, final double max, final double min, + final int offset, final int count) { + sendCommand(ZREVRANGEBYSCORE, key, toByteArray(max), toByteArray(min), LIMIT.raw, toByteArray(offset), + toByteArray(count), WITHSCORES.raw); + } + + public void zrangeByScore(final byte[] key, final byte[] min, final byte[] max, final int offset, + final int count) { + sendCommand(ZRANGEBYSCORE, key, min, max, LIMIT.raw, toByteArray(offset), toByteArray(count)); + } + + public void zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + sendCommand(ZREVRANGEBYSCORE, key, max, min, LIMIT.raw, toByteArray(offset), toByteArray(count)); + } + + public void zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZRANGEBYSCORE, key, min, max, WITHSCORES.raw); + } + + public void zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min) { + sendCommand(ZREVRANGEBYSCORE, key, max, min, WITHSCORES.raw); + } + + public void zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + sendCommand(ZRANGEBYSCORE, key, min, max, LIMIT.raw, toByteArray(offset), toByteArray(count), + WITHSCORES.raw); + } + + public void zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + sendCommand(ZREVRANGEBYSCORE, key, max, min, LIMIT.raw, toByteArray(offset), + toByteArray(count), WITHSCORES.raw); + } + + public void zremrangeByRank(final byte[] key, final long start, final long stop) { + sendCommand(ZREMRANGEBYRANK, key, toByteArray(start), toByteArray(stop)); + } + + public void zremrangeByScore(final byte[] key, final double min, final double max) { + sendCommand(ZREMRANGEBYSCORE, key, toByteArray(min), toByteArray(max)); + } + + public void zremrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZREMRANGEBYSCORE, key, min, max); + } + + public void zunionstore(final byte[] dstkey, final byte[]... sets) { + sendCommand(ZUNIONSTORE, joinParameters(dstkey, toByteArray(sets.length), sets)); + } + + public void zunionstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { + final List args = new ArrayList(); + args.add(dstkey); + args.add(Protocol.toByteArray(sets.length)); + for (final byte[] set : sets) { + args.add(set); + } + args.addAll(params.getParams()); + sendCommand(ZUNIONSTORE, args.toArray(new byte[args.size()][])); + } + + public void zinterstore(final byte[] dstkey, final byte[]... sets) { + sendCommand(ZINTERSTORE, joinParameters(dstkey, Protocol.toByteArray(sets.length), sets)); + } + + public void zinterstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { + final List args = new ArrayList(); + args.add(dstkey); + args.add(Protocol.toByteArray(sets.length)); + for (final byte[] set : sets) { + args.add(set); + } + args.addAll(params.getParams()); + sendCommand(ZINTERSTORE, args.toArray(new byte[args.size()][])); + } + + public void zlexcount(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZLEXCOUNT, key, min, max); + } + + public void zrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZRANGEBYLEX, key, min, max); + } + + public void zrangeByLex(final byte[] key, final byte[] min, final byte[] max, final int offset, + final int count) { + sendCommand(ZRANGEBYLEX, key, min, max, LIMIT.raw, toByteArray(offset), toByteArray(count)); + } + + public void zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min) { + sendCommand(ZREVRANGEBYLEX, key, max, min); + } + + public void zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + sendCommand(ZREVRANGEBYLEX, key, max, min, LIMIT.raw, toByteArray(offset), toByteArray(count)); + } + + public void zremrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + sendCommand(ZREMRANGEBYLEX, key, min, max); + } + + public void save() { + sendCommand(SAVE); + } + + public void bgsave() { + sendCommand(BGSAVE); + } + + public void bgrewriteaof() { + sendCommand(BGREWRITEAOF); + } + + public void lastsave() { + sendCommand(LASTSAVE); + } + + public void shutdown() { + sendCommand(SHUTDOWN); + } + + public void info() { + sendCommand(INFO); + } + + public void info(final String section) { + sendCommand(INFO, section); + } + + public void monitor() { + sendCommand(MONITOR); + } + + public void slaveof(final String host, final int port) { + sendCommand(SLAVEOF, host, String.valueOf(port)); + } + + public void slaveofNoOne() { + sendCommand(SLAVEOF, NO.raw, ONE.raw); + } + + public void configGet(final byte[] pattern) { + sendCommand(CONFIG, Keyword.GET.raw, pattern); + } + + public void configSet(final byte[] parameter, final byte[] value) { + sendCommand(CONFIG, Keyword.SET.raw, parameter, value); + } + + public void strlen(final byte[] key) { + sendCommand(STRLEN, key); + } + + public void sync() { + sendCommand(SYNC); + } + + public void lpushx(final byte[] key, final byte[]... string) { + sendCommand(LPUSHX, joinParameters(key, string)); + } + + public void persist(final byte[] key) { + sendCommand(PERSIST, key); + } + + public void rpushx(final byte[] key, final byte[]... string) { + sendCommand(RPUSHX, joinParameters(key, string)); + } + + public void echo(final byte[] string) { + sendCommand(ECHO, string); + } + + public void linsert(final byte[] key, final ListPosition where, final byte[] pivot, + final byte[] value) { + sendCommand(LINSERT, key, where.raw, pivot, value); + } + + public void debug(final DebugParams params) { + sendCommand(DEBUG, params.getCommand()); + } + + public void brpoplpush(final byte[] source, final byte[] destination, final int timeout) { + sendCommand(BRPOPLPUSH, source, destination, toByteArray(timeout)); + } + + public void configResetStat() { + sendCommand(CONFIG, Keyword.RESETSTAT.raw); + } + + public void configRewrite() { + sendCommand(CONFIG, Keyword.REWRITE.raw); + } + + public void setbit(final byte[] key, final long offset, final byte[] value) { + sendCommand(SETBIT, key, toByteArray(offset), value); + } + + public void setbit(final byte[] key, final long offset, final boolean value) { + sendCommand(SETBIT, key, toByteArray(offset), toByteArray(value)); + } + + public void getbit(final byte[] key, final long offset) { + sendCommand(GETBIT, key, toByteArray(offset)); + } + + public void bitpos(final byte[] key, final boolean value, final BitPosParams params) { + final List args = new ArrayList(); + args.add(key); + args.add(toByteArray(value)); + args.addAll(params.getParams()); + sendCommand(BITPOS, args.toArray(new byte[args.size()][])); + } + + public void setrange(final byte[] key, final long offset, final byte[] value) { + sendCommand(SETRANGE, key, toByteArray(offset), value); + } + + public void getrange(final byte[] key, final long startOffset, final long endOffset) { + sendCommand(GETRANGE, key, toByteArray(startOffset), toByteArray(endOffset)); + } + + public int getDB() { + return db; + } + + @Override + public void disconnect() { + db = 0; + super.disconnect(); + } + + @Override + public void close() { + db = 0; + super.close(); + } + + public void resetState() { + if (isInWatch()) unwatch(); + } + + public void eval(final byte[] script, final byte[] keyCount, final byte[][] params) { + sendCommand(EVAL, joinParameters(script, keyCount, params)); + } + + public void eval(final byte[] script, final int keyCount, final byte[]... params) { + sendCommand(EVAL, joinParameters(script, toByteArray(keyCount), params)); + } + + public void evalsha(final byte[] sha1, final byte[] keyCount, final byte[]... params) { + sendCommand(EVALSHA, joinParameters(sha1, keyCount, params)); + } + + public void evalsha(final byte[] sha1, final int keyCount, final byte[]... params) { + sendCommand(EVALSHA, joinParameters(sha1, toByteArray(keyCount), params)); + } + + public void scriptFlush() { + sendCommand(SCRIPT, Keyword.FLUSH.raw); + } + + public void scriptExists(final byte[]... sha1) { + sendCommand(SCRIPT, joinParameters(Keyword.EXISTS.raw, sha1)); + } + + public void scriptLoad(final byte[] script) { + sendCommand(SCRIPT, Keyword.LOAD.raw, script); + } + + public void scriptKill() { + sendCommand(SCRIPT, Keyword.KILL.raw); + } + + public void slowlogGet() { + sendCommand(SLOWLOG, Keyword.GET.raw); + } + + public void slowlogGet(final long entries) { + sendCommand(SLOWLOG, Keyword.GET.raw, toByteArray(entries)); + } + + public void slowlogReset() { + sendCommand(SLOWLOG, RESET.raw); + } + + public void slowlogLen() { + sendCommand(SLOWLOG, LEN.raw); + } + + public void objectRefcount(final byte[] key) { + sendCommand(OBJECT, REFCOUNT.raw, key); + } + + public void objectIdletime(final byte[] key) { + sendCommand(OBJECT, IDLETIME.raw, key); + } + + public void objectEncoding(final byte[] key) { + sendCommand(OBJECT, ENCODING.raw, key); + } + + public void bitcount(final byte[] key) { + sendCommand(BITCOUNT, key); + } + + public void bitcount(final byte[] key, final long start, final long end) { + sendCommand(BITCOUNT, key, toByteArray(start), toByteArray(end)); + } + + public void bitop(final BitOP op, final byte[] destKey, final byte[]... srcKeys) { + sendCommand(BITOP, joinParameters(op.raw, destKey, srcKeys)); + } + + public void sentinel(final byte[]... args) { + sendCommand(SENTINEL, args); + } + + public void dump(final byte[] key) { + sendCommand(DUMP, key); + } + + public void restore(final byte[] key, final int ttl, final byte[] serializedValue) { + sendCommand(RESTORE, key, toByteArray(ttl), serializedValue); + } + + public void pexpire(final byte[] key, final long milliseconds) { + sendCommand(PEXPIRE, key, toByteArray(milliseconds)); + } + + public void pexpireAt(final byte[] key, final long millisecondsTimestamp) { + sendCommand(PEXPIREAT, key, toByteArray(millisecondsTimestamp)); + } + + public void pttl(final byte[] key) { + sendCommand(PTTL, key); + } + + public void psetex(final byte[] key, final long milliseconds, final byte[] value) { + sendCommand(PSETEX, key, toByteArray(milliseconds), value); + } + + public void srandmember(final byte[] key, final int count) { + sendCommand(SRANDMEMBER, key, toByteArray(count)); + } + + public void clientKill(final byte[] client) { + sendCommand(CLIENT, Keyword.KILL.raw, client); + } + + public void clientGetname() { + sendCommand(CLIENT, Keyword.GETNAME.raw); + } + + public void clientList() { + sendCommand(CLIENT, Keyword.LIST.raw); + } + + public void clientSetname(final byte[] name) { + sendCommand(CLIENT, Keyword.SETNAME.raw, name); + } + + public void time() { + sendCommand(TIME); + } + + public void migrate(final byte[] host, final int port, final byte[] key, final int destinationDb, + final int timeout) { + sendCommand(MIGRATE, host, toByteArray(port), key, toByteArray(destinationDb), + toByteArray(timeout)); + } + + public void hincrByFloat(final byte[] key, final byte[] field, final double increment) { + sendCommand(HINCRBYFLOAT, key, field, toByteArray(increment)); + } + + public void scan(final byte[] cursor, final ScanParams params) { + final List args = new ArrayList(); + args.add(cursor); + args.addAll(params.getParams()); + sendCommand(SCAN, args.toArray(new byte[args.size()][])); + } + + public void hscan(final byte[] key, final byte[] cursor, final ScanParams params) { + final List args = new ArrayList(); + args.add(key); + args.add(cursor); + args.addAll(params.getParams()); + sendCommand(HSCAN, args.toArray(new byte[args.size()][])); + } + + public void sscan(final byte[] key, final byte[] cursor, final ScanParams params) { + final List args = new ArrayList(); + args.add(key); + args.add(cursor); + args.addAll(params.getParams()); + sendCommand(SSCAN, args.toArray(new byte[args.size()][])); + } + + public void zscan(final byte[] key, final byte[] cursor, final ScanParams params) { + final List args = new ArrayList(); + args.add(key); + args.add(cursor); + args.addAll(params.getParams()); + sendCommand(ZSCAN, args.toArray(new byte[args.size()][])); + } + + public void waitReplicas(final int replicas, final long timeout) { + sendCommand(WAIT, toByteArray(replicas), toByteArray(timeout)); + } + + public void cluster(final byte[]... args) { + sendCommand(CLUSTER, args); + } + + public void asking() { + sendCommand(ASKING); + } + + public void pfadd(final byte[] key, final byte[]... elements) { + sendCommand(PFADD, joinParameters(key, elements)); + } + + public void pfcount(final byte[] key) { + sendCommand(PFCOUNT, key); + } + + public void pfcount(final byte[]... keys) { + sendCommand(PFCOUNT, keys); + } + + public void pfmerge(final byte[] destkey, final byte[]... sourcekeys) { + sendCommand(PFMERGE, joinParameters(destkey, sourcekeys)); + } + + public void readonly() { + sendCommand(READONLY); + } + + public void geoadd(final byte[] key, final double longitude, final double latitude, final byte[] member) { + sendCommand(GEOADD, key, toByteArray(longitude), toByteArray(latitude), member); + } + + public void geoadd(final byte[] key, final Map memberCoordinateMap) { + List args = new ArrayList(memberCoordinateMap.size() * 3 + 1); + args.add(key); + args.addAll(convertGeoCoordinateMapToByteArrays(memberCoordinateMap)); + + byte[][] argsArray = new byte[args.size()][]; + args.toArray(argsArray); + + sendCommand(GEOADD, argsArray); + } + + public void geodist(final byte[] key, final byte[] member1, final byte[] member2) { + sendCommand(GEODIST, key, member1, member2); + } + + public void geodist(final byte[] key, final byte[] member1, final byte[] member2, final GeoUnit unit) { + sendCommand(GEODIST, key, member1, member2, unit.raw); + } + + public void geohash(final byte[] key, final byte[]... members) { + sendCommand(GEOHASH, joinParameters(key, members)); + } + + public void geopos(final byte[] key, final byte[][] members) { + sendCommand(GEOPOS, joinParameters(key, members)); + } + + public void georadius(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit) { + sendCommand(GEORADIUS, key, toByteArray(longitude), toByteArray(latitude), toByteArray(radius), + unit.raw); + } + + public void georadius(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit, + final GeoRadiusParam param) { + sendCommand(GEORADIUS, param.getByteParams(key, toByteArray(longitude), toByteArray(latitude), + toByteArray(radius), unit.raw)); + } + + public void georadiusByMember(final byte[] key, final byte[] member, final double radius, final GeoUnit unit) { + sendCommand(GEORADIUSBYMEMBER, key, member, toByteArray(radius), unit.raw); + } + + public void georadiusByMember(final byte[] key, final byte[] member, final double radius, final GeoUnit unit, + final GeoRadiusParam param) { + sendCommand(GEORADIUSBYMEMBER, param.getByteParams(key, member, toByteArray(radius), unit.raw)); + } + + public void moduleLoad(final byte[] path) { + sendCommand(MODULE, Keyword.LOAD.raw, path); + } + + public void moduleList() { + sendCommand(MODULE, Keyword.LIST.raw); + } + + public void moduleUnload(final byte[] name) { + sendCommand(MODULE, Keyword.UNLOAD.raw, name); + } + + private ArrayList convertScoreMembersToByteArrays(final Map scoreMembers) { + ArrayList args = new ArrayList(scoreMembers.size() * 2); + + for (Entry entry : scoreMembers.entrySet()) { + args.add(toByteArray(entry.getValue())); + args.add(entry.getKey()); + } + + return args; + } + + private List convertGeoCoordinateMapToByteArrays( + final Map memberCoordinateMap) { + List args = new ArrayList(memberCoordinateMap.size() * 3); + + for (Entry entry : memberCoordinateMap.entrySet()) { + GeoCoordinate coordinate = entry.getValue(); + args.add(toByteArray(coordinate.getLongitude())); + args.add(toByteArray(coordinate.getLatitude())); + args.add(entry.getKey()); + } + + return args; + } + + public void bitfield(final byte[] key, final byte[]... value) { + sendCommand(BITFIELD, joinParameters(key, value)); + } + + public void hstrlen(final byte[] key, final byte[] field) { + sendCommand(HSTRLEN, key, field); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedis.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedis.java new file mode 100755 index 000000000..68133ac48 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedis.java @@ -0,0 +1,3850 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.toByteArray; + +import java.io.Closeable; +import java.io.Serializable; +import java.net.URI; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.redis.clients.jedis.commands.*; +import com.fr.third.redis.clients.jedis.exceptions.InvalidURIException; +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.util.JedisByteHashMap; +import com.fr.third.redis.clients.jedis.util.JedisURIHelper; + +public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKeyBinaryCommands, + AdvancedBinaryJedisCommands, BinaryScriptingCommands, Closeable { + protected Client client = null; + protected Transaction transaction = null; + protected Pipeline pipeline = null; + + public BinaryJedis() { + client = new Client(); + } + + public BinaryJedis(final String host) { + URI uri = URI.create(host); + if (JedisURIHelper.isValid(uri)) { + initializeClientFromURI(uri); + } else { + client = new Client(host); + } + } + + public BinaryJedis(final HostAndPort hp) { + this(hp.getHost(), hp.getPort()); + } + + public BinaryJedis(final String host, final int port) { + client = new Client(host, port); + } + + public BinaryJedis(final String host, final int port, final boolean ssl) { + client = new Client(host, port, ssl); + } + + public BinaryJedis(final String host, final int port, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + client = new Client(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public BinaryJedis(final String host, final int port, final int timeout) { + this(host, port, timeout, timeout); + } + + public BinaryJedis(final String host, final int port, final int timeout, final boolean ssl) { + this(host, port, timeout, timeout, ssl); + } + + public BinaryJedis(final String host, final int port, final int timeout, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(host, port, timeout, timeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public BinaryJedis(final String host, final int port, final int connectionTimeout, + final int soTimeout) { + client = new Client(host, port); + client.setConnectionTimeout(connectionTimeout); + client.setSoTimeout(soTimeout); + } + + public BinaryJedis(final String host, final int port, final int connectionTimeout, + final int soTimeout, final boolean ssl) { + client = new Client(host, port, ssl); + client.setConnectionTimeout(connectionTimeout); + client.setSoTimeout(soTimeout); + } + + public BinaryJedis(final String host, final int port, final int connectionTimeout, + final int soTimeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + client = new Client(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + client.setConnectionTimeout(connectionTimeout); + client.setSoTimeout(soTimeout); + } + + public BinaryJedis(final JedisShardInfo shardInfo) { + client = new Client(shardInfo.getHost(), shardInfo.getPort(), shardInfo.getSsl(), + shardInfo.getSslSocketFactory(), shardInfo.getSslParameters(), + shardInfo.getHostnameVerifier()); + client.setConnectionTimeout(shardInfo.getConnectionTimeout()); + client.setSoTimeout(shardInfo.getSoTimeout()); + client.setPassword(shardInfo.getPassword()); + client.setDb(shardInfo.getDb()); + } + + public BinaryJedis(URI uri) { + initializeClientFromURI(uri); + } + + public BinaryJedis(URI uri, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + initializeClientFromURI(uri, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public BinaryJedis(final URI uri, final int timeout) { + this(uri, timeout, timeout); + } + + public BinaryJedis(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + this(uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public BinaryJedis(final URI uri, final int connectionTimeout, final int soTimeout) { + initializeClientFromURI(uri); + client.setConnectionTimeout(connectionTimeout); + client.setSoTimeout(soTimeout); + } + + public BinaryJedis(final URI uri, final int connectionTimeout, final int soTimeout, + final SSLSocketFactory sslSocketFactory,final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + initializeClientFromURI(uri, sslSocketFactory, sslParameters, hostnameVerifier); + client.setConnectionTimeout(connectionTimeout); + client.setSoTimeout(soTimeout); + } + + private void initializeClientFromURI(URI uri) { + initializeClientFromURI(uri, null, null, null); + } + + private void initializeClientFromURI(URI uri, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + if (!JedisURIHelper.isValid(uri)) { + throw new InvalidURIException(String.format( + "Cannot open Redis connection due invalid URI. %s", uri.toString())); + } + + client = new Client(uri.getHost(), uri.getPort(), JedisURIHelper.isRedisSSLScheme(uri), + sslSocketFactory, sslParameters, hostnameVerifier); + + String password = JedisURIHelper.getPassword(uri); + if (password != null) { + client.auth(password); + client.getStatusCodeReply(); + } + + int dbIndex = JedisURIHelper.getDBIndex(uri); + if (dbIndex > 0) { + client.select(dbIndex); + client.getStatusCodeReply(); + client.setDb(dbIndex); + } + } + + @Override + public String ping() { + checkIsInMultiOrPipeline(); + client.ping(); + return client.getStatusCodeReply(); + } + + /** + * Works same as ping() but returns argument message instead of PONG. + * @param message + * @return message + */ + public byte[] ping(final byte[] message) { + checkIsInMultiOrPipeline(); + client.ping(message); + return client.getBinaryBulkReply(); + } + + /** + * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 + * GB). + *

+ * Time complexity: O(1) + * @param key + * @param value + * @return Status code reply + */ + @Override + public String set(final byte[] key, final byte[] value) { + checkIsInMultiOrPipeline(); + client.set(key, value); + return client.getStatusCodeReply(); + } + + /** + * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 + * GB). + * @param key + * @param value + * @param params + * @return Status code reply + */ + @Override + public String set(final byte[] key, final byte[] value, final SetParams params) { + checkIsInMultiOrPipeline(); + client.set(key, value, params); + return client.getStatusCodeReply(); + } + + /** + * Get the value of the specified key. If the key does not exist the special value 'nil' is + * returned. If the value stored at key is not a string an error is returned because GET can only + * handle string values. + *

+ * Time complexity: O(1) + * @param key + * @return Bulk reply + */ + @Override + public byte[] get(final byte[] key) { + checkIsInMultiOrPipeline(); + client.get(key); + return client.getBinaryBulkReply(); + } + + /** + * Ask the server to silently close the connection. + */ + @Override + public String quit() { + checkIsInMultiOrPipeline(); + client.quit(); + String quitReturn = client.getStatusCodeReply(); + client.disconnect(); + return quitReturn; + } + + /** + * Test if the specified keys exist. The command returns the number of keys exist. + * Time complexity: O(N) + * @param keys + * @return Integer reply, specifically: an integer greater than 0 if one or more keys exist, + * 0 if none of the specified keys exist. + */ + @Override + public Long exists(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.exists(keys); + return client.getIntegerReply(); + } + + /** + * Test if the specified key exists. The command returns "1" if the key exists, otherwise "0" is + * returned. Note that even keys set with an empty string as value will return "1". Time + * complexity: O(1) + * @param key + * @return Boolean reply, true if the key exists, otherwise false + */ + @Override + public Boolean exists(final byte[] key) { + checkIsInMultiOrPipeline(); + client.exists(key); + return client.getIntegerReply() == 1; + } + + /** + * Remove the specified keys. If a given key does not exist no operation is performed for this + * key. The command returns the number of keys removed. Time complexity: O(1) + * @param keys + * @return Integer reply, specifically: an integer greater than 0 if one or more keys were removed + * 0 if none of the specified key existed + */ + @Override + public Long del(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.del(keys); + return client.getIntegerReply(); + } + + @Override + public Long del(final byte[] key) { + checkIsInMultiOrPipeline(); + client.del(key); + return client.getIntegerReply(); + } + + /** + * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is + * ignored if it does not exist. However the command performs the actual memory reclaiming in a + * different thread, so it is not blocking, while DEL is. This is where the command name comes + * from: the command just unlinks the keys from the keyspace. The actual removal will happen later + * asynchronously. + *

+ * Time complexity: O(1) for each key removed regardless of its size. Then the command does O(N) + * work in a different thread in order to reclaim memory, where N is the number of allocations the + * deleted objects where composed of. + * @param keys + * @return Integer reply: The number of keys that were unlinked + */ + @Override + public Long unlink(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.unlink(keys); + return client.getIntegerReply(); + } + + @Override + public Long unlink(final byte[] key) { + checkIsInMultiOrPipeline(); + client.unlink(key); + return client.getIntegerReply(); + } + + /** + * Return the type of the value stored at key in form of a string. The type can be one of "none", + * "string", "list", "set". "none" is returned if the key does not exist. Time complexity: O(1) + * @param key + * @return Status code reply, specifically: "none" if the key does not exist "string" if the key + * contains a String value "list" if the key contains a List value "set" if the key + * contains a Set value "zset" if the key contains a Sorted Set value "hash" if the key + * contains a Hash value + */ + @Override + public String type(final byte[] key) { + checkIsInMultiOrPipeline(); + client.type(key); + return client.getStatusCodeReply(); + } + + /** + * Delete all the keys of the currently selected DB. This command never fails. + * @return Status code reply + */ + @Override + public String flushDB() { + checkIsInMultiOrPipeline(); + client.flushDB(); + return client.getStatusCodeReply(); + } + + /** + * Returns all the keys matching the glob-style pattern as space separated strings. For example if + * you have in the database the keys "foo" and "foobar" the command "KEYS foo*" will return + * "foo foobar". + *

+ * Note that while the time complexity for this operation is O(n) the constant times are pretty + * low. For example Redis running on an entry level laptop can scan a 1 million keys database in + * 40 milliseconds. Still it's better to consider this one of the slow commands that may ruin + * the DB performance if not used with care. + *

+ * In other words this command is intended only for debugging and special operations like creating + * a script to change the DB schema. Don't use it in your normal code. Use Redis Sets in order to + * group together a subset of objects. + *

+ * Glob style patterns examples: + *

    + *
  • h?llo will match hello hallo hhllo + *
  • h*llo will match hllo heeeello + *
  • h[ae]llo will match hello and hallo, but not hillo + *
+ *

+ * Use \ to escape special chars if you want to match them verbatim. + *

+ * Time complexity: O(n) (with n being the number of keys in the DB, and assuming keys and pattern + * of limited length) + * @param pattern + * @return Multi bulk reply + */ + @Override + public Set keys(final byte[] pattern) { + checkIsInMultiOrPipeline(); + client.keys(pattern); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Return a randomly selected key from the currently selected DB. + *

+ * Time complexity: O(1) + * @return Singe line reply, specifically the randomly selected key or an empty string is the + * database is empty + */ + @Override + public byte[] randomBinaryKey() { + checkIsInMultiOrPipeline(); + client.randomKey(); + return client.getBinaryBulkReply(); + } + + /** + * Atomically renames the key oldkey to newkey. If the source and destination name are the same an + * error is returned. If newkey already exists it is overwritten. + *

+ * Time complexity: O(1) + * @param oldkey + * @param newkey + * @return Status code repy + */ + @Override + public String rename(final byte[] oldkey, final byte[] newkey) { + checkIsInMultiOrPipeline(); + client.rename(oldkey, newkey); + return client.getStatusCodeReply(); + } + + /** + * Rename oldkey into newkey but fails if the destination key newkey already exists. + *

+ * Time complexity: O(1) + * @param oldkey + * @param newkey + * @return Integer reply, specifically: 1 if the key was renamed 0 if the target key already exist + */ + @Override + public Long renamenx(final byte[] oldkey, final byte[] newkey) { + checkIsInMultiOrPipeline(); + client.renamenx(oldkey, newkey); + return client.getIntegerReply(); + } + + /** + * Return the number of keys in the currently selected database. + * @return Integer reply + */ + @Override + public Long dbSize() { + checkIsInMultiOrPipeline(); + client.dbSize(); + return client.getIntegerReply(); + } + + /** + * Set a timeout on the specified key. After the timeout the key will be automatically deleted by + * the server. A key with an associated timeout is said to be volatile in Redis terminology. + *

+ * Voltile keys are stored on disk like the other keys, the timeout is persistent too like all the + * other aspects of the dataset. Saving a dataset containing expires and stopping the server does + * not stop the flow of time as Redis stores on disk the time when the key will no longer be + * available as Unix time, and not the remaining seconds. + *

+ * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire + * set. It is also possible to undo the expire at all turning the key into a normal key using the + * {@link #persist(byte[]) PERSIST} command. + *

+ * Time complexity: O(1) + * @see Expire Command + * @param key + * @param seconds + * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since + * the key already has an associated timeout (this may happen only in Redis versions < + * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. + */ + @Override + public Long expire(final byte[] key, final int seconds) { + checkIsInMultiOrPipeline(); + client.expire(key, seconds); + return client.getIntegerReply(); + } + + /** + * EXPIREAT works exctly like {@link #expire(byte[], int) EXPIRE} but instead to get the number of + * seconds representing the Time To Live of the key as a second argument (that is a relative way + * of specifing the TTL), it takes an absolute one in the form of a UNIX timestamp (Number of + * seconds elapsed since 1 Gen 1970). + *

+ * EXPIREAT was introduced in order to implement the Append Only File persistence mode so that + * EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. + * Of course EXPIREAT can also used by programmers that need a way to simply specify that a given + * key should expire at a given time in the future. + *

+ * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire + * set. It is also possible to undo the expire at all turning the key into a normal key using the + * {@link #persist(byte[]) PERSIST} command. + *

+ * Time complexity: O(1) + * @see Expire Command + * @param key + * @param unixTime + * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since + * the key already has an associated timeout (this may happen only in Redis versions < + * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. + */ + @Override + public Long expireAt(final byte[] key, final long unixTime) { + checkIsInMultiOrPipeline(); + client.expireAt(key, unixTime); + return client.getIntegerReply(); + } + + /** + * The TTL command returns the remaining time to live in seconds of a key that has an + * {@link #expire(byte[], int) EXPIRE} set. This introspection capability allows a Redis client to + * check how many seconds a given key will continue to be part of the dataset. + * @param key + * @return Integer reply, returns the remaining time to live in seconds of a key that has an + * EXPIRE. If the Key does not exists or does not have an associated expire, -1 is + * returned. + */ + @Override + public Long ttl(final byte[] key) { + checkIsInMultiOrPipeline(); + client.ttl(key); + return client.getIntegerReply(); + } + + /** + * Alters the last access time of a key(s). A key is ignored if it does not exist. + * Time complexity: O(N) where N is the number of keys that will be touched. + * @param keys + * @return Integer reply: The number of keys that were touched. + */ + @Override + public Long touch(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.touch(keys); + return client.getIntegerReply(); + } + + @Override + public Long touch(final byte[] key) { + checkIsInMultiOrPipeline(); + client.touch(key); + return client.getIntegerReply(); + } + + /** + * Select the DB with having the specified zero-based numeric index. For default every new client + * connection is automatically selected to DB 0. + * @param index + * @return Status code reply + */ + @Override + public String select(final int index) { + checkIsInMultiOrPipeline(); + client.select(index); + String statusCodeReply = client.getStatusCodeReply(); + client.setDb(index); + + return statusCodeReply; + } + + @Override + public String swapDB(final int index1, final int index2) { + checkIsInMultiOrPipeline(); + client.swapDB(index1, index2); + return client.getStatusCodeReply(); + } + + /** + * Move the specified key from the currently selected DB to the specified destination DB. Note + * that this command returns 1 only if the key was successfully moved, and 0 if the target key was + * already there or if the source key was not found at all, so it is possible to use MOVE as a + * locking primitive. + * @param key + * @param dbIndex + * @return Integer reply, specifically: 1 if the key was moved 0 if the key was not moved because + * already present on the target DB or was not found in the current DB. + */ + @Override + public Long move(final byte[] key, final int dbIndex) { + checkIsInMultiOrPipeline(); + client.move(key, dbIndex); + return client.getIntegerReply(); + } + + /** + * Delete all the keys of all the existing databases, not just the currently selected one. This + * command never fails. + * @return Status code reply + */ + @Override + public String flushAll() { + checkIsInMultiOrPipeline(); + client.flushAll(); + return client.getStatusCodeReply(); + } + + /** + * GETSET is an atomic set this value and return the old value command. Set key to the string + * value and return the old value stored at key. The string can't be longer than 1073741824 bytes + * (1 GB). + *

+ * Time complexity: O(1) + * @param key + * @param value + * @return Bulk reply + */ + @Override + public byte[] getSet(final byte[] key, final byte[] value) { + checkIsInMultiOrPipeline(); + client.getSet(key, value); + return client.getBinaryBulkReply(); + } + + /** + * Get the values of all the specified keys. If one or more keys dont exist or is not of type + * String, a 'nil' value is returned instead of the value of the specified key, but the operation + * never fails. + *

+ * Time complexity: O(1) for every key + * @param keys + * @return Multi bulk reply + */ + @Override + public List mget(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.mget(keys); + return client.getBinaryMultiBulkReply(); + } + + /** + * SETNX works exactly like {@link #set(byte[], byte[]) SET} with the only difference that if the + * key already exists no operation is performed. SETNX actually means "SET if Not eXists". + *

+ * Time complexity: O(1) + * @param key + * @param value + * @return Integer reply, specifically: 1 if the key was set 0 if the key was not set + */ + @Override + public Long setnx(final byte[] key, final byte[] value) { + checkIsInMultiOrPipeline(); + client.setnx(key, value); + return client.getIntegerReply(); + } + + /** + * The command is exactly equivalent to the following group of commands: + * {@link #set(byte[], byte[]) SET} + {@link #expire(byte[], int) EXPIRE}. The operation is + * atomic. + *

+ * Time complexity: O(1) + * @param key + * @param seconds + * @param value + * @return Status code reply + */ + @Override + public String setex(final byte[] key, final int seconds, final byte[] value) { + checkIsInMultiOrPipeline(); + client.setex(key, seconds, value); + return client.getStatusCodeReply(); + } + + /** + * Set the the respective keys to the respective values. MSET will replace old values with new + * values, while {@link #msetnx(byte[]...) MSETNX} will not perform any operation at all even if + * just a single key already exists. + *

+ * Because of this semantic MSETNX can be used in order to set different keys representing + * different fields of an unique logic object in a way that ensures that either all the fields or + * none at all are set. + *

+ * Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B + * are modified, another client talking to Redis can either see the changes to both A and B at + * once, or no modification at all. + * @see #msetnx(byte[]...) + * @param keysvalues + * @return Status code reply Basically +OK as MSET can't fail + */ + @Override + public String mset(final byte[]... keysvalues) { + checkIsInMultiOrPipeline(); + client.mset(keysvalues); + return client.getStatusCodeReply(); + } + + /** + * Set the the respective keys to the respective values. {@link #mset(byte[]...) MSET} will + * replace old values with new values, while MSETNX will not perform any operation at all even if + * just a single key already exists. + *

+ * Because of this semantic MSETNX can be used in order to set different keys representing + * different fields of an unique logic object in a way that ensures that either all the fields or + * none at all are set. + *

+ * Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B + * are modified, another client talking to Redis can either see the changes to both A and B at + * once, or no modification at all. + * @see #mset(byte[]...) + * @param keysvalues + * @return Integer reply, specifically: 1 if the all the keys were set 0 if no key was set (at + * least one key already existed) + */ + @Override + public Long msetnx(final byte[]... keysvalues) { + checkIsInMultiOrPipeline(); + client.msetnx(keysvalues); + return client.getIntegerReply(); + } + + /** + * DECRBY work just like {@link #decr(byte[]) INCR} but instead to decrement by 1 the decrement is + * integer. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incr(byte[]) + * @see #decr(byte[]) + * @see #incrBy(byte[], long) + * @param key + * @param decrement + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long decrBy(final byte[] key, final long decrement) { + checkIsInMultiOrPipeline(); + client.decrBy(key, decrement); + return client.getIntegerReply(); + } + + /** + * Decrement the number stored at key by one. If the key does not exist or contains a value of a + * wrong type, set the key to the value of "0" before to perform the decrement operation. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incr(byte[]) + * @see #incrBy(byte[], long) + * @see #decrBy(byte[], long) + * @param key + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long decr(final byte[] key) { + checkIsInMultiOrPipeline(); + client.decr(key); + return client.getIntegerReply(); + } + + /** + * INCRBY work just like {@link #incr(byte[]) INCR} but instead to increment by 1 the increment is + * integer. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incr(byte[]) + * @see #decr(byte[]) + * @see #decrBy(byte[], long) + * @param key + * @param increment + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long incrBy(final byte[] key, final long increment) { + checkIsInMultiOrPipeline(); + client.incrBy(key, increment); + return client.getIntegerReply(); + } + + /** + * INCRBYFLOAT work just like {@link #incrBy(byte[], long)} INCRBY} but increments by floats + * instead of integers. + *

+ * INCRBYFLOAT commands are limited to double precision floating point values. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "double" types. + * Simply the string stored at the key is parsed as a base double precision floating point value, + * incremented, and then converted back as a string. There is no DECRYBYFLOAT but providing a + * negative value will work as expected. + *

+ * Time complexity: O(1) + * @see #incr(byte[]) + * @see #decr(byte[]) + * @see #decrBy(byte[], long) + * @param key the key to increment + * @param increment the value to increment by + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Double incrByFloat(final byte[] key, final double increment) { + checkIsInMultiOrPipeline(); + client.incrByFloat(key, increment); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + /** + * Increment the number stored at key by one. If the key does not exist or contains a value of a + * wrong type, set the key to the value of "0" before to perform the increment operation. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incrBy(byte[], long) + * @see #decr(byte[]) + * @see #decrBy(byte[], long) + * @param key + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long incr(final byte[] key) { + checkIsInMultiOrPipeline(); + client.incr(key); + return client.getIntegerReply(); + } + + /** + * If the key already exists and is a string, this command appends the provided value at the end + * of the string. If the key does not exist it is created and set as an empty string, so APPEND + * will be very similar to SET in this special case. + *

+ * Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is + * small and the already present value is of any size, since the dynamic string library used by + * Redis will double the free space available on every reallocation. + * @param key + * @param value + * @return Integer reply, specifically the total length of the string after the append operation. + */ + @Override + public Long append(final byte[] key, final byte[] value) { + checkIsInMultiOrPipeline(); + client.append(key, value); + return client.getIntegerReply(); + } + + /** + * Return a subset of the string from offset start to offset end (both offsets are inclusive). + * Negative offsets can be used in order to provide an offset starting from the end of the string. + * So -1 means the last char, -2 the penultimate and so forth. + *

+ * The function handles out of range requests without raising an error, but just limiting the + * resulting range to the actual length of the string. + *

+ * Time complexity: O(start+n) (with start being the start index and n the total length of the + * requested range). Note that the lookup part of this command is O(1) so for small strings this + * is actually an O(1) command. + * @param key + * @param start + * @param end + * @return Bulk reply + */ + @Override + public byte[] substr(final byte[] key, final int start, final int end) { + checkIsInMultiOrPipeline(); + client.substr(key, start, end); + return client.getBinaryBulkReply(); + } + + /** + * Set the specified hash field to the specified value. + *

+ * If key does not exist, a new key holding a hash is created. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @param value + * @return If the field already exists, and the HSET just produced an update of the value, 0 is + * returned, otherwise if a new field is created 1 is returned. + */ + @Override + public Long hset(final byte[] key, final byte[] field, final byte[] value) { + checkIsInMultiOrPipeline(); + client.hset(key, field, value); + return client.getIntegerReply(); + } + + @Override + public Long hset(final byte[] key, final Map hash) { + checkIsInMultiOrPipeline(); + client.hset(key, hash); + return client.getIntegerReply(); + } + + /** + * If key holds a hash, retrieve the value associated to the specified field. + *

+ * If the field is not found or the key does not exist, a special 'nil' value is returned. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @return Bulk reply + */ + @Override + public byte[] hget(final byte[] key, final byte[] field) { + checkIsInMultiOrPipeline(); + client.hget(key, field); + return client.getBinaryBulkReply(); + } + + /** + * Set the specified hash field to the specified value if the field not exists. Time + * complexity: O(1) + * @param key + * @param field + * @param value + * @return If the field already exists, 0 is returned, otherwise if a new field is created 1 is + * returned. + */ + @Override + public Long hsetnx(final byte[] key, final byte[] field, final byte[] value) { + checkIsInMultiOrPipeline(); + client.hsetnx(key, field, value); + return client.getIntegerReply(); + } + + /** + * Set the respective fields to the respective values. HMSET replaces old values with new values. + *

+ * If key does not exist, a new key holding a hash is created. + *

+ * Time complexity: O(N) (with N being the number of fields) + * @param key + * @param hash + * @return Always OK because HMSET can't fail + */ + @Override + public String hmset(final byte[] key, final Map hash) { + checkIsInMultiOrPipeline(); + client.hmset(key, hash); + return client.getStatusCodeReply(); + } + + /** + * Retrieve the values associated to the specified fields. + *

+ * If some of the specified fields do not exist, nil values are returned. Non existing keys are + * considered like empty hashes. + *

+ * Time complexity: O(N) (with N being the number of fields) + * @param key + * @param fields + * @return Multi Bulk Reply specifically a list of all the values associated with the specified + * fields, in the same order of the request. + */ + @Override + public List hmget(final byte[] key, final byte[]... fields) { + checkIsInMultiOrPipeline(); + client.hmget(key, fields); + return client.getBinaryMultiBulkReply(); + } + + /** + * Increment the number stored at field in the hash at key by value. If key does not exist, a new + * key holding a hash is created. If field does not exist or holds a string, the value is set to 0 + * before applying the operation. Since the value argument is signed you can use this command to + * perform both increments and decrements. + *

+ * The range of values supported by HINCRBY is limited to 64 bit signed integers. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @param value + * @return Integer reply The new value at field after the increment operation. + */ + @Override + public Long hincrBy(final byte[] key, final byte[] field, final long value) { + checkIsInMultiOrPipeline(); + client.hincrBy(key, field, value); + return client.getIntegerReply(); + } + + /** + * Increment the number stored at field in the hash at key by a double precision floating point + * value. If key does not exist, a new key holding a hash is created. If field does not exist or + * holds a string, the value is set to 0 before applying the operation. Since the value argument + * is signed you can use this command to perform both increments and decrements. + *

+ * The range of values supported by HINCRBYFLOAT is limited to double precision floating point + * values. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @param value + * @return Double precision floating point reply The new value at field after the increment + * operation. + */ + @Override + public Double hincrByFloat(final byte[] key, final byte[] field, final double value) { + checkIsInMultiOrPipeline(); + client.hincrByFloat(key, field, value); + final String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + /** + * Test for existence of a specified field in a hash. Time complexity: O(1) + * @param key + * @param field + * @return Return 1 if the hash stored at key contains the specified field. Return 0 if the key is + * not found or the field is not present. + */ + @Override + public Boolean hexists(final byte[] key, final byte[] field) { + checkIsInMultiOrPipeline(); + client.hexists(key, field); + return client.getIntegerReply() == 1; + } + + /** + * Remove the specified field from an hash stored at key. + *

+ * Time complexity: O(1) + * @param key + * @param fields + * @return If the field was present in the hash it is deleted and 1 is returned, otherwise 0 is + * returned and no operation is performed. + */ + @Override + public Long hdel(final byte[] key, final byte[]... fields) { + checkIsInMultiOrPipeline(); + client.hdel(key, fields); + return client.getIntegerReply(); + } + + /** + * Return the number of items in a hash. + *

+ * Time complexity: O(1) + * @param key + * @return The number of entries (fields) contained in the hash stored at key. If the specified + * key does not exist, 0 is returned assuming an empty hash. + */ + @Override + public Long hlen(final byte[] key) { + checkIsInMultiOrPipeline(); + client.hlen(key); + return client.getIntegerReply(); + } + + /** + * Return all the fields in a hash. + *

+ * Time complexity: O(N), where N is the total number of entries + * @param key + * @return All the fields names contained into a hash. + */ + @Override + public Set hkeys(final byte[] key) { + checkIsInMultiOrPipeline(); + client.hkeys(key); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Return all the values in a hash. + *

+ * Time complexity: O(N), where N is the total number of entries + * @param key + * @return All the fields values contained into a hash. + */ + @Override + public List hvals(final byte[] key) { + checkIsInMultiOrPipeline(); + client.hvals(key); + return client.getBinaryMultiBulkReply(); + } + + /** + * Return all the fields and associated values in a hash. + *

+ * Time complexity: O(N), where N is the total number of entries + * @param key + * @return All the fields and values contained into a hash. + */ + @Override + public Map hgetAll(final byte[] key) { + checkIsInMultiOrPipeline(); + client.hgetAll(key); + final List flatHash = client.getBinaryMultiBulkReply(); + final Map hash = new JedisByteHashMap(); + final Iterator iterator = flatHash.iterator(); + while (iterator.hasNext()) { + hash.put(iterator.next(), iterator.next()); + } + + return hash; + } + + /** + * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key + * does not exist an empty list is created just before the append operation. If the key exists but + * is not a List an error is returned. + *

+ * Time complexity: O(1) + * @see BinaryJedis#rpush(byte[], byte[]...) + * @param key + * @param strings + * @return Integer reply, specifically, the number of elements inside the list after the push + * operation. + */ + @Override + public Long rpush(final byte[] key, final byte[]... strings) { + checkIsInMultiOrPipeline(); + client.rpush(key, strings); + return client.getIntegerReply(); + } + + /** + * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key + * does not exist an empty list is created just before the append operation. If the key exists but + * is not a List an error is returned. + *

+ * Time complexity: O(1) + * @see BinaryJedis#rpush(byte[], byte[]...) + * @param key + * @param strings + * @return Integer reply, specifically, the number of elements inside the list after the push + * operation. + */ + @Override + public Long lpush(final byte[] key, final byte[]... strings) { + checkIsInMultiOrPipeline(); + client.lpush(key, strings); + return client.getIntegerReply(); + } + + /** + * Return the length of the list stored at the specified key. If the key does not exist zero is + * returned (the same behaviour as for empty lists). If the value stored at key is not a list an + * error is returned. + *

+ * Time complexity: O(1) + * @param key + * @return The length of the list. + */ + @Override + public Long llen(final byte[] key) { + checkIsInMultiOrPipeline(); + client.llen(key); + return client.getIntegerReply(); + } + + /** + * Return the specified elements of the list stored at the specified key. Start and end are + * zero-based indexes. 0 is the first element of the list (the list head), 1 the next element and + * so on. + *

+ * For example LRANGE foobar 0 2 will return the first three elements of the list. + *

+ * start and end can also be negative numbers indicating offsets from the end of the list. For + * example -1 is the last element of the list, -2 the penultimate element and so on. + *

+ * Consistency with range functions in various programming languages + *

+ * Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return 11 elements, + * that is, rightmost item is included. This may or may not be consistent with behavior of + * range-related functions in your programming language of choice (think Ruby's Range.new, + * Array#slice or Python's range() function). + *

+ * LRANGE behavior is consistent with one of Tcl. + *

+ * Out-of-range indexes + *

+ * Indexes out of range will not produce an error: if start is over the end of the list, or start + * > end, an empty list is returned. If end is over the end of the list Redis will threat it + * just like the last element of the list. + *

+ * Time complexity: O(start+n) (with n being the length of the range and start being the start + * offset) + * @param key + * @param start + * @param stop + * @return Multi bulk reply, specifically a list of elements in the specified range. + */ + @Override + public List lrange(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.lrange(key, start, stop); + return client.getBinaryMultiBulkReply(); + } + + /** + * Trim an existing list so that it will contain only the specified range of elements specified. + * Start and end are zero-based indexes. 0 is the first element of the list (the list head), 1 the + * next element and so on. + *

+ * For example LTRIM foobar 0 2 will modify the list stored at foobar key so that only the first + * three elements of the list will remain. + *

+ * start and end can also be negative numbers indicating offsets from the end of the list. For + * example -1 is the last element of the list, -2 the penultimate element and so on. + *

+ * Indexes out of range will not produce an error: if start is over the end of the list, or start + * > end, an empty list is left as value. If end over the end of the list Redis will threat it + * just like the last element of the list. + *

+ * Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example: + *

+ * {@code lpush("mylist", "someelement"); ltrim("mylist", 0, 99); * } + *

+ * The above two commands will push elements in the list taking care that the list will not grow + * without limits. This is very useful when using Redis to store logs for example. It is important + * to note that when used in this way LTRIM is an O(1) operation because in the average case just + * one element is removed from the tail of the list. + *

+ * Time complexity: O(n) (with n being len of list - len of range) + * @param key + * @param start + * @param stop + * @return Status code reply + */ + @Override + public String ltrim(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.ltrim(key, start, stop); + return client.getStatusCodeReply(); + } + + /** + * Return the specified element of the list stored at the specified key. 0 is the first element, 1 + * the second and so on. Negative indexes are supported, for example -1 is the last element, -2 + * the penultimate and so on. + *

+ * If the value stored at key is not of list type an error is returned. If the index is out of + * range a 'nil' reply is returned. + *

+ * Note that even if the average time complexity is O(n) asking for the first or the last element + * of the list is O(1). + *

+ * Time complexity: O(n) (with n being the length of the list) + * @param key + * @param index + * @return Bulk reply, specifically the requested element + */ + @Override + public byte[] lindex(final byte[] key, final long index) { + checkIsInMultiOrPipeline(); + client.lindex(key, index); + return client.getBinaryBulkReply(); + } + + /** + * Set a new value as the element at index position of the List at key. + *

+ * Out of range indexes will generate an error. + *

+ * Similarly to other list commands accepting indexes, the index can be negative to access + * elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, + * and so forth. + *

+ * Time complexity: + *

+ * O(N) (with N being the length of the list), setting the first or last elements of the list is + * O(1). + * @see #lindex(byte[], long) + * @param key + * @param index + * @param value + * @return Status code reply + */ + @Override + public String lset(final byte[] key, final long index, final byte[] value) { + checkIsInMultiOrPipeline(); + client.lset(key, index, value); + return client.getStatusCodeReply(); + } + + /** + * Remove the first count occurrences of the value element from the list. If count is zero all the + * elements are removed. If count is negative elements are removed from tail to head, instead to + * go from head to tail that is the normal behaviour. So for example LREM with count -2 and hello + * as value to remove against the list (a,b,c,hello,x,hello,hello) will have the list + * (a,b,c,hello,x). The number of removed elements is returned as an integer, see below for more + * information about the returned value. Note that non existing keys are considered like empty + * lists by LREM, so LREM against non existing keys will always return 0. + *

+ * Time complexity: O(N) (with N being the length of the list) + * @param key + * @param count + * @param value + * @return Integer Reply, specifically: The number of removed elements if the operation succeeded + */ + @Override + public Long lrem(final byte[] key, final long count, final byte[] value) { + checkIsInMultiOrPipeline(); + client.lrem(key, count, value); + return client.getIntegerReply(); + } + + /** + * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example + * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become + * "b","c". + *

+ * If the key does not exist or the list is already empty the special value 'nil' is returned. + * @see #rpop(byte[]) + * @param key + * @return Bulk reply + */ + @Override + public byte[] lpop(final byte[] key) { + checkIsInMultiOrPipeline(); + client.lpop(key); + return client.getBinaryBulkReply(); + } + + /** + * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example + * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become + * "b","c". + *

+ * If the key does not exist or the list is already empty the special value 'nil' is returned. + * @see #lpop(byte[]) + * @param key + * @return Bulk reply + */ + @Override + public byte[] rpop(final byte[] key) { + checkIsInMultiOrPipeline(); + client.rpop(key); + return client.getBinaryBulkReply(); + } + + /** + * Atomically return and remove the last (tail) element of the srckey list, and push the element + * as the first (head) element of the dstkey list. For example if the source list contains the + * elements "a","b","c" and the destination list contains the elements "foo","bar" after an + * RPOPLPUSH command the content of the two lists will be "a","b" and "c","foo","bar". + *

+ * If the key does not exist or the list is already empty the special value 'nil' is returned. If + * the srckey and dstkey are the same the operation is equivalent to removing the last element + * from the list and pusing it as first element of the list, so it's a "list rotation" command. + *

+ * Time complexity: O(1) + * @param srckey + * @param dstkey + * @return Bulk reply + */ + @Override + public byte[] rpoplpush(final byte[] srckey, final byte[] dstkey) { + checkIsInMultiOrPipeline(); + client.rpoplpush(srckey, dstkey); + return client.getBinaryBulkReply(); + } + + /** + * Add the specified member to the set value stored at key. If member is already a member of the + * set no operation is performed. If key does not exist a new set with the specified member as + * sole member is created. If the key exists but does not hold a set value an error is returned. + *

+ * Time complexity O(1) + * @param key + * @param members + * @return Integer reply, specifically: 1 if the new element was added 0 if the element was + * already a member of the set + */ + @Override + public Long sadd(final byte[] key, final byte[]... members) { + checkIsInMultiOrPipeline(); + client.sadd(key, members); + return client.getIntegerReply(); + } + + /** + * Return all the members (elements) of the set value stored at key. This is just syntax glue for + * {@link #sinter(byte[]...)} SINTER}. + *

+ * Time complexity O(N) + * @param key the key of the set + * @return Multi bulk reply + */ + @Override + public Set smembers(final byte[] key) { + checkIsInMultiOrPipeline(); + client.smembers(key); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Remove the specified member from the set value stored at key. If member was not a member of the + * set no operation is performed. If key does not hold a set value an error is returned. + *

+ * Time complexity O(1) + * @param key the key of the set + * @param member the set member to remove + * @return Integer reply, specifically: 1 if the new element was removed 0 if the new element was + * not a member of the set + */ + @Override + public Long srem(final byte[] key, final byte[]... member) { + checkIsInMultiOrPipeline(); + client.srem(key, member); + return client.getIntegerReply(); + } + + /** + * Remove a random element from a Set returning it as return value. If the Set is empty or the key + * does not exist, a nil object is returned. + *

+ * The {@link #srandmember(byte[])} command does a similar work but the returned element is not + * removed from the Set. + *

+ * Time complexity O(1) + * @param key + * @return Bulk reply + */ + @Override + public byte[] spop(final byte[] key) { + checkIsInMultiOrPipeline(); + client.spop(key); + return client.getBinaryBulkReply(); + } + + @Override + public Set spop(final byte[] key, final long count) { + checkIsInMultiOrPipeline(); + client.spop(key, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Move the specified member from the set at srckey to the set at dstkey. This operation is + * atomic, in every given moment the element will appear to be in the source or destination set + * for accessing clients. + *

+ * If the source set does not exist or does not contain the specified element no operation is + * performed and zero is returned, otherwise the element is removed from the source set and added + * to the destination set. On success one is returned, even if the element was already present in + * the destination set. + *

+ * An error is raised if the source or destination keys contain a non Set value. + *

+ * Time complexity O(1) + * @param srckey + * @param dstkey + * @param member + * @return Integer reply, specifically: 1 if the element was moved 0 if the element was not found + * on the first set and no operation was performed + */ + @Override + public Long smove(final byte[] srckey, final byte[] dstkey, final byte[] member) { + checkIsInMultiOrPipeline(); + client.smove(srckey, dstkey, member); + return client.getIntegerReply(); + } + + /** + * Return the set cardinality (number of elements). If the key does not exist 0 is returned, like + * for empty sets. + * @param key + * @return Integer reply, specifically: the cardinality (number of elements) of the set as an + * integer. + */ + @Override + public Long scard(final byte[] key) { + checkIsInMultiOrPipeline(); + client.scard(key); + return client.getIntegerReply(); + } + + /** + * Return 1 if member is a member of the set stored at key, otherwise 0 is returned. + *

+ * Time complexity O(1) + * @param key + * @param member + * @return Integer reply, specifically: 1 if the element is a member of the set 0 if the element + * is not a member of the set OR if the key does not exist + */ + @Override + public Boolean sismember(final byte[] key, final byte[] member) { + checkIsInMultiOrPipeline(); + client.sismember(key, member); + return client.getIntegerReply() == 1; + } + + /** + * Return the members of a set resulting from the intersection of all the sets hold at the + * specified keys. Like in {@link #lrange(byte[], long, long)} LRANGE} the result is sent to the + * client as a multi-bulk reply (see the protocol specification for more information). If just a + * single key is specified, then this command produces the same result as + * {@link #smembers(byte[]) SMEMBERS}. Actually SMEMBERS is just syntax sugar for SINTER. + *

+ * Non existing keys are considered like empty sets, so if one of the keys is missing an empty set + * is returned (since the intersection with an empty set always is an empty set). + *

+ * Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the + * number of sets + * @param keys + * @return Multi bulk reply, specifically the list of common elements. + */ + @Override + public Set sinter(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.sinter(keys); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * This commnad works exactly like {@link #sinter(byte[]...) SINTER} but instead of being returned + * the resulting set is sotred as dstkey. + *

+ * Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the + * number of sets + * @param dstkey + * @param keys + * @return Status code reply + */ + @Override + public Long sinterstore(final byte[] dstkey, final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.sinterstore(dstkey, keys); + return client.getIntegerReply(); + } + + /** + * Return the members of a set resulting from the union of all the sets hold at the specified + * keys. Like in {@link #lrange(byte[], long, long)} LRANGE} the result is sent to the client as a + * multi-bulk reply (see the protocol specification for more information). If just a single key is + * specified, then this command produces the same result as {@link #smembers(byte[]) SMEMBERS}. + *

+ * Non existing keys are considered like empty sets. + *

+ * Time complexity O(N) where N is the total number of elements in all the provided sets + * @param keys + * @return Multi bulk reply, specifically the list of common elements. + */ + @Override + public Set sunion(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.sunion(keys); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * This command works exactly like {@link #sunion(byte[]...) SUNION} but instead of being returned + * the resulting set is stored as dstkey. Any existing value in dstkey will be over-written. + *

+ * Time complexity O(N) where N is the total number of elements in all the provided sets + * @param dstkey + * @param keys + * @return Status code reply + */ + @Override + public Long sunionstore(final byte[] dstkey, final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.sunionstore(dstkey, keys); + return client.getIntegerReply(); + } + + /** + * Return the difference between the Set stored at key1 and all the Sets key2, ..., keyN + *

+ * Example: + * + *

+   * key1 = [x, a, b, c]
+   * key2 = [c]
+   * key3 = [a, d]
+   * SDIFF key1,key2,key3 => [x, b]
+   * 
+ * + * Non existing keys are considered like empty sets. + *

+ * Time complexity: + *

+ * O(N) with N being the total number of elements of all the sets + * @param keys + * @return Return the members of a set resulting from the difference between the first set + * provided and all the successive sets. + */ + @Override + public Set sdiff(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.sdiff(keys); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * This command works exactly like {@link #sdiff(byte[]...) SDIFF} but instead of being returned + * the resulting set is stored in dstkey. + * @param dstkey + * @param keys + * @return Status code reply + */ + @Override + public Long sdiffstore(final byte[] dstkey, final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.sdiffstore(dstkey, keys); + return client.getIntegerReply(); + } + + /** + * Return a random element from a Set, without removing the element. If the Set is empty or the + * key does not exist, a nil object is returned. + *

+ * The SPOP command does a similar work but the returned element is popped (removed) from the Set. + *

+ * Time complexity O(1) + * @param key + * @return Bulk reply + */ + @Override + public byte[] srandmember(final byte[] key) { + checkIsInMultiOrPipeline(); + client.srandmember(key); + return client.getBinaryBulkReply(); + } + + @Override + public List srandmember(final byte[] key, final int count) { + checkIsInMultiOrPipeline(); + client.srandmember(key, count); + return client.getBinaryMultiBulkReply(); + } + + /** + * Add the specified member having the specifeid score to the sorted set stored at key. If member + * is already a member of the sorted set the score is updated, and the element reinserted in the + * right position to ensure sorting. If key does not exist a new sorted set with the specified + * member as sole member is crated. If the key exists but does not hold a sorted set value an + * error is returned. + *

+ * The score value can be the string representation of a double precision floating point number. + *

+ * Time complexity O(log(N)) with N being the number of elements in the sorted set + * @param key + * @param score + * @param member + * @return Integer reply, specifically: 1 if the new element was added 0 if the element was + * already a member of the sorted set and the score was updated + */ + @Override + public Long zadd(final byte[] key, final double score, final byte[] member) { + checkIsInMultiOrPipeline(); + client.zadd(key, score, member); + return client.getIntegerReply(); + } + + @Override + public Long zadd(final byte[] key, final double score, final byte[] member, final ZAddParams params) { + checkIsInMultiOrPipeline(); + client.zadd(key, score, member, params); + return client.getIntegerReply(); + } + + @Override + public Long zadd(final byte[] key, final Map scoreMembers) { + checkIsInMultiOrPipeline(); + client.zadd(key, scoreMembers); + return client.getIntegerReply(); + } + + @Override + public Long zadd(final byte[] key, final Map scoreMembers, final ZAddParams params) { + checkIsInMultiOrPipeline(); + client.zadd(key, scoreMembers, params); + return client.getIntegerReply(); + } + + @Override + public Set zrange(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrange(key, start, stop); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Remove the specified member from the sorted set value stored at key. If member was not a member + * of the set no operation is performed. If key does not not hold a set value an error is + * returned. + *

+ * Time complexity O(log(N)) with N being the number of elements in the sorted set + * @param key + * @param members + * @return Integer reply, specifically: 1 if the new element was removed 0 if the new element was + * not a member of the set + */ + @Override + public Long zrem(final byte[] key, final byte[]... members) { + checkIsInMultiOrPipeline(); + client.zrem(key, members); + return client.getIntegerReply(); + } + + /** + * If member already exists in the sorted set adds the increment to its score and updates the + * position of the element in the sorted set accordingly. If member does not already exist in the + * sorted set it is added with increment as score (that is, like if the previous score was + * virtually zero). If key does not exist a new sorted set with the specified member as sole + * member is crated. If the key exists but does not hold a sorted set value an error is returned. + *

+ * The score value can be the string representation of a double precision floating point number. + * It's possible to provide a negative value to perform a decrement. + *

+ * For an introduction to sorted sets check the Introduction to Redis data types page. + *

+ * Time complexity O(log(N)) with N being the number of elements in the sorted set + * @param key + * @param increment + * @param member + * @return The new score + */ + @Override + public Double zincrby(final byte[] key, final double increment, final byte[] member) { + checkIsInMultiOrPipeline(); + client.zincrby(key, increment, member); + return BuilderFactory.DOUBLE.build(client.getOne()); + } + + @Override + public Double zincrby(final byte[] key, final double increment, final byte[] member, final ZIncrByParams params) { + checkIsInMultiOrPipeline(); + client.zincrby(key, increment, member, params); + return BuilderFactory.DOUBLE.build(client.getOne()); + } + + /** + * Return the rank (or index) or member in the sorted set at key, with scores being ordered from + * low to high. + *

+ * When the given member does not exist in the sorted set, the special value 'nil' is returned. + * The returned rank (or index) of the member is 0-based for both commands. + *

+ * Time complexity: + *

+ * O(log(N)) + * @see #zrevrank(byte[], byte[]) + * @param key + * @param member + * @return Integer reply or a nil bulk reply, specifically: the rank of the element as an integer + * reply if the element exists. A nil bulk reply if there is no such element. + */ + @Override + public Long zrank(final byte[] key, final byte[] member) { + checkIsInMultiOrPipeline(); + client.zrank(key, member); + return client.getIntegerReply(); + } + + /** + * Return the rank (or index) or member in the sorted set at key, with scores being ordered from + * high to low. + *

+ * When the given member does not exist in the sorted set, the special value 'nil' is returned. + * The returned rank (or index) of the member is 0-based for both commands. + *

+ * Time complexity: + *

+ * O(log(N)) + * @see #zrank(byte[], byte[]) + * @param key + * @param member + * @return Integer reply or a nil bulk reply, specifically: the rank of the element as an integer + * reply if the element exists. A nil bulk reply if there is no such element. + */ + @Override + public Long zrevrank(final byte[] key, final byte[] member) { + checkIsInMultiOrPipeline(); + client.zrevrank(key, member); + return client.getIntegerReply(); + } + + @Override + public Set zrevrange(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrevrange(key, start, stop); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrangeWithScores(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrangeWithScores(key, start, stop); + return getTupledSet(); + } + + @Override + public Set zrevrangeWithScores(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrevrangeWithScores(key, start, stop); + return getTupledSet(); + } + + /** + * Return the sorted set cardinality (number of elements). If the key does not exist 0 is + * returned, like for empty sorted sets. + *

+ * Time complexity O(1) + * @param key + * @return the cardinality (number of elements) of the set as an integer. + */ + @Override + public Long zcard(final byte[] key) { + checkIsInMultiOrPipeline(); + client.zcard(key); + return client.getIntegerReply(); + } + + /** + * Return the score of the specified element of the sorted set at key. If the specified element + * does not exist in the sorted set, or the key does not exist at all, a special 'nil' value is + * returned. + *

+ * Time complexity: O(1) + * @param key + * @param member + * @return the score + */ + @Override + public Double zscore(final byte[] key, final byte[] member) { + checkIsInMultiOrPipeline(); + client.zscore(key, member); + final String score = client.getBulkReply(); + return (score != null ? new Double(score) : null); + } + + public Transaction multi() { + client.multi(); + client.getOne(); // expected OK + transaction = new Transaction(client); + return transaction; + } + + protected void checkIsInMultiOrPipeline() { + if (client.isInMulti()) { + throw new JedisDataException( + "Cannot use Jedis when in Multi. Please use Transaction or reset jedis state."); + } else if (pipeline != null && pipeline.hasPipelinedResponse()) { + throw new JedisDataException( + "Cannot use Jedis when in Pipeline. Please use Pipeline or reset jedis state ."); + } + } + + public void connect() { + client.connect(); + } + + public void disconnect() { + client.disconnect(); + } + + public void resetState() { + if (client.isConnected()) { + if (transaction != null) { + transaction.clear(); + } + + if (pipeline != null) { + pipeline.clear(); + } + + if (client.isInWatch()) { + unwatch(); + } + + client.resetState(); + } + + transaction = null; + pipeline = null; + } + + @Override + public String watch(final byte[]... keys) { + client.watch(keys); + return client.getStatusCodeReply(); + } + + @Override + public String unwatch() { + client.unwatch(); + return client.getStatusCodeReply(); + } + + @Override + public void close() { + client.close(); + } + + /** + * Sort a Set or a List. + *

+ * Sort the elements contained in the List, Set, or Sorted Set value at key. By default sorting is + * numeric with elements being compared as double precision floating point numbers. This is the + * simplest form of SORT. + * @see #sort(byte[], byte[]) + * @see #sort(byte[], SortingParams) + * @see #sort(byte[], SortingParams, byte[]) + * @param key + * @return Assuming the Set/List at key contains a list of numbers, the return value will be the + * list of numbers ordered from the smallest to the biggest number. + */ + @Override + public List sort(final byte[] key) { + checkIsInMultiOrPipeline(); + client.sort(key); + return client.getBinaryMultiBulkReply(); + } + + /** + * Sort a Set or a List accordingly to the specified parameters. + *

+ * examples: + *

+ * Given are the following sets and key/values: + * + *

+   * x = [1, 2, 3]
+   * y = [a, b, c]
+   * 
+   * k1 = z
+   * k2 = y
+   * k3 = x
+   * 
+   * w1 = 9
+   * w2 = 8
+   * w3 = 7
+   * 
+ * + * Sort Order: + * + *
+   * sort(x) or sort(x, sp.asc())
+   * -> [1, 2, 3]
+   * 
+   * sort(x, sp.desc())
+   * -> [3, 2, 1]
+   * 
+   * sort(y)
+   * -> [c, a, b]
+   * 
+   * sort(y, sp.alpha())
+   * -> [a, b, c]
+   * 
+   * sort(y, sp.alpha().desc())
+   * -> [c, a, b]
+   * 
+ * + * Limit (e.g. for Pagination): + * + *
+   * sort(x, sp.limit(0, 2))
+   * -> [1, 2]
+   * 
+   * sort(y, sp.alpha().desc().limit(1, 2))
+   * -> [b, a]
+   * 
+ * + * Sorting by external keys: + * + *
+   * sort(x, sb.by(w*))
+   * -> [3, 2, 1]
+   * 
+   * sort(x, sb.by(w*).desc())
+   * -> [1, 2, 3]
+   * 
+ * + * Getting external keys: + * + *
+   * sort(x, sp.by(w*).get(k*))
+   * -> [x, y, z]
+   * 
+   * sort(x, sp.by(w*).get(#).get(k*))
+   * -> [3, x, 2, y, 1, z]
+   * 
+ * @see #sort(byte[]) + * @see #sort(byte[], SortingParams, byte[]) + * @param key + * @param sortingParameters + * @return a list of sorted elements. + */ + @Override + public List sort(final byte[] key, final SortingParams sortingParameters) { + checkIsInMultiOrPipeline(); + client.sort(key, sortingParameters); + return client.getBinaryMultiBulkReply(); + } + + /** + * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking + * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty + * lists. + *

+ * The following is a description of the exact semantic. We describe BLPOP but the two commands + * are identical, the only difference is that BLPOP pops the element from the left (head) of the + * list, and BRPOP pops from the right (tail). + *

+ * Non blocking behavior + *

+ * When BLPOP is called, if at least one of the specified keys contain a non empty list, an + * element is popped from the head of the list and returned to the caller together with the name + * of the key (BLPOP returns a two elements array, the first element is the key, the second the + * popped value). + *

+ * Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 + * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP + * guarantees to return an element from the list stored at list2 (since it is the first non empty + * list starting from the left). + *

+ * Blocking behavior + *

+ * If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other + * client performs a LPUSH or an RPUSH operation against one of the lists. + *

+ * Once new data is present on one of the lists, the client finally returns with the name of the + * key unblocking it and the popped value. + *

+ * When blocking, if a non-zero timeout is specified, the client will unblock returning a nil + * special value if the specified amount of seconds passed without a push operation against at + * least one of the specified keys. + *

+ * The timeout argument is interpreted as an integer value. A timeout of zero means instead to + * block forever. + *

+ * Multiple clients blocking for the same keys + *

+ * Multiple clients can block for the same key. They are put into a queue, so the first to be + * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. + *

+ * blocking POP inside a MULTI/EXEC transaction + *

+ * BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies + * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis + * transaction). + *

+ * The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil + * reply, exactly what happens when the timeout is reached. If you like science fiction, think at + * it like if inside MULTI/EXEC the time will flow at infinite speed :) + *

+ * Time complexity: O(1) + * @see #brpop(int, byte[]...) + * @param timeout + * @param keys + * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the + * unblocking key and the popped value. + *

+ * When a non-zero timeout is specified, and the BLPOP operation timed out, the return + * value is a nil multi bulk reply. Most client values will return false or nil + * accordingly to the programming language used. + */ + @Override + public List blpop(final int timeout, final byte[]... keys) { + return blpop(getArgsAddTimeout(timeout, keys)); + } + + private byte[][] getArgsAddTimeout(int timeout, byte[][] keys) { + int size = keys.length; + final byte[][] args = new byte[size + 1][]; + for (int at = 0; at != size; ++at) { + args[at] = keys[at]; + } + args[size] = Protocol.toByteArray(timeout); + return args; + } + + /** + * Sort a Set or a List accordingly to the specified parameters and store the result at dstkey. + * @see #sort(byte[], SortingParams) + * @see #sort(byte[]) + * @see #sort(byte[], byte[]) + * @param key + * @param sortingParameters + * @param dstkey + * @return The number of elements of the list at dstkey. + */ + @Override + public Long sort(final byte[] key, final SortingParams sortingParameters, final byte[] dstkey) { + checkIsInMultiOrPipeline(); + client.sort(key, sortingParameters, dstkey); + return client.getIntegerReply(); + } + + /** + * Sort a Set or a List and Store the Result at dstkey. + *

+ * Sort the elements contained in the List, Set, or Sorted Set value at key and store the result + * at dstkey. By default sorting is numeric with elements being compared as double precision + * floating point numbers. This is the simplest form of SORT. + * @see #sort(byte[]) + * @see #sort(byte[], SortingParams) + * @see #sort(byte[], SortingParams, byte[]) + * @param key + * @param dstkey + * @return The number of elements of the list at dstkey. + */ + @Override + public Long sort(final byte[] key, final byte[] dstkey) { + checkIsInMultiOrPipeline(); + client.sort(key, dstkey); + return client.getIntegerReply(); + } + + /** + * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking + * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty + * lists. + *

+ * The following is a description of the exact semantic. We describe BLPOP but the two commands + * are identical, the only difference is that BLPOP pops the element from the left (head) of the + * list, and BRPOP pops from the right (tail). + *

+ * Non blocking behavior + *

+ * When BLPOP is called, if at least one of the specified keys contain a non empty list, an + * element is popped from the head of the list and returned to the caller together with the name + * of the key (BLPOP returns a two elements array, the first element is the key, the second the + * popped value). + *

+ * Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 + * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP + * guarantees to return an element from the list stored at list2 (since it is the first non empty + * list starting from the left). + *

+ * Blocking behavior + *

+ * If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other + * client performs a LPUSH or an RPUSH operation against one of the lists. + *

+ * Once new data is present on one of the lists, the client finally returns with the name of the + * key unblocking it and the popped value. + *

+ * When blocking, if a non-zero timeout is specified, the client will unblock returning a nil + * special value if the specified amount of seconds passed without a push operation against at + * least one of the specified keys. + *

+ * The timeout argument is interpreted as an integer value. A timeout of zero means instead to + * block forever. + *

+ * Multiple clients blocking for the same keys + *

+ * Multiple clients can block for the same key. They are put into a queue, so the first to be + * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. + *

+ * blocking POP inside a MULTI/EXEC transaction + *

+ * BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies + * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis + * transaction). + *

+ * The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil + * reply, exactly what happens when the timeout is reached. If you like science fiction, think at + * it like if inside MULTI/EXEC the time will flow at infinite speed :) + *

+ * Time complexity: O(1) + * @see #blpop(int, byte[]...) + * @param timeout + * @param keys + * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the + * unblocking key and the popped value. + *

+ * When a non-zero timeout is specified, and the BLPOP operation timed out, the return + * value is a nil multi bulk reply. Most client values will return false or nil + * accordingly to the programming language used. + */ + @Override + public List brpop(final int timeout, final byte[]... keys) { + return brpop(getArgsAddTimeout(timeout, keys)); + } + + @Override + public List blpop(final byte[]... args) { + checkIsInMultiOrPipeline(); + client.blpop(args); + client.setTimeoutInfinite(); + try { + return client.getBinaryMultiBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public List brpop(final byte[]... args) { + checkIsInMultiOrPipeline(); + client.brpop(args); + client.setTimeoutInfinite(); + try { + return client.getBinaryMultiBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + + /** + * Request for authentication in a password protected Redis server. A Redis server can be + * instructed to require a password before to allow clients to issue commands. This is done using + * the requirepass directive in the Redis configuration file. If the password given by the client + * is correct the server replies with an OK status code reply and starts accepting commands from + * the client. Otherwise an error is returned and the clients needs to try a new password. Note + * that for the high performance nature of Redis it is possible to try a lot of passwords in + * parallel in very short time, so make sure to generate a strong and very long password so that + * this attack is infeasible. + * @param password + * @return Status code reply + */ + @Override + public String auth(final String password) { + checkIsInMultiOrPipeline(); + client.auth(password); + return client.getStatusCodeReply(); + } + + public Pipeline pipelined() { + pipeline = new Pipeline(); + pipeline.setClient(client); + return pipeline; + } + + @Override + public Long zcount(final byte[] key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zcount(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Long zcount(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zcount(key, min, max); + return client.getIntegerReply(); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(byte[], double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(byte[], double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(byte[], double, double) + * @see #zrangeByScore(byte[], double, double, int, int) + * @see #zrangeByScoreWithScores(byte[], double, double) + * @see #zrangeByScoreWithScores(byte[], double, double, int, int) + * @see #zcount(byte[], double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScore(final byte[] key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(byte[], double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(byte[], double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(byte[], double, double) + * @see #zrangeByScore(byte[], double, double, int, int) + * @see #zrangeByScoreWithScores(byte[], double, double) + * @see #zrangeByScoreWithScores(byte[], double, double, int, int) + * @see #zcount(byte[], double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScore(final byte[] key, final double min, final double max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max, offset, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrangeByScore(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max, offset, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(byte[], double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(byte[], double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(byte[], double, double) + * @see #zrangeByScore(byte[], double, double, int, int) + * @see #zrangeByScoreWithScores(byte[], double, double) + * @see #zrangeByScoreWithScores(byte[], double, double, int, int) + * @see #zcount(byte[], double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScoreWithScores(final byte[] key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max); + return getTupledSet(); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max); + return getTupledSet(); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(byte[], double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(byte[], double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(byte[], double, double) + * @see #zrangeByScore(byte[], double, double, int, int) + * @see #zrangeByScoreWithScores(byte[], double, double) + * @see #zrangeByScoreWithScores(byte[], double, double, int, int) + * @see #zcount(byte[], double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScoreWithScores(final byte[] key, final double min, final double max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max, offset, count); + return getTupledSet(); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max, offset, count); + return getTupledSet(); + } + + protected Set getTupledSet() { + List membersWithScores = client.getBinaryMultiBulkReply(); + if (membersWithScores.isEmpty()) { + return Collections.emptySet(); + } + Set set = new LinkedHashSet(membersWithScores.size() / 2, 1.0f); + Iterator iterator = membersWithScores.iterator(); + while (iterator.hasNext()) { + set.add(new Tuple(iterator.next(), BuilderFactory.DOUBLE.build(iterator.next()))); + } + return set; + } + + @Override + public Set zrevrangeByScore(final byte[] key, final double max, final double min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final double max, final double min, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min, offset, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min, offset, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final double max, final double min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final double max, + final double min, final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min, offset, count); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final byte[] max, + final byte[] min, final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min, offset, count); + return getTupledSet(); + } + + /** + * Remove all elements in the sorted set at key with rank between start and end. Start and end are + * 0-based with rank 0 being the element with the lowest score. Both start and end can be negative + * numbers, where they indicate offsets starting at the element with the highest rank. For + * example: -1 is the element with the highest score, -2 the element with the second highest score + * and so forth. + *

+ * Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set + * and M the number of elements removed by the operation + */ + @Override + public Long zremrangeByRank(final byte[] key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zremrangeByRank(key, start, stop); + return client.getIntegerReply(); + } + + /** + * Remove all the elements in the sorted set at key with a score between min and max (including + * elements with score equal to min or max). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements removed by the operation + * @param key + * @param min + * @param max + * @return Integer reply, specifically the number of elements removed. + */ + @Override + public Long zremrangeByScore(final byte[] key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zremrangeByScore(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Long zremrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zremrangeByScore(key, min, max); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(byte[], byte[]...)} ZINTERSTORE} command requires + * an element to be present in each of the given inputs to be inserted in the result. The {@link + * #zunionstore(byte[], byte[]...)} command inserts all elements across all inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(byte[], byte[]...) + * @see #zunionstore(byte[], ZParams, byte[]...) + * @see #zinterstore(byte[], byte[]...) + * @see #zinterstore(byte[], ZParams, byte[]...) + * @param dstkey + * @param sets + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zunionstore(final byte[] dstkey, final byte[]... sets) { + checkIsInMultiOrPipeline(); + client.zunionstore(dstkey, sets); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(byte[], byte[]...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The {@link + * #zunionstore(byte[], byte[]...) ZUNIONSTORE} command inserts all elements across all inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(byte[], byte[]...) + * @see #zunionstore(byte[], ZParams, byte[]...) + * @see #zinterstore(byte[], byte[]...) + * @see #zinterstore(byte[], ZParams, byte[]...) + * @param dstkey + * @param sets + * @param params + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zunionstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { + checkIsInMultiOrPipeline(); + client.zunionstore(dstkey, params, sets); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(byte[], byte[]...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The {@link + * #zunionstore(byte[], byte[]...) ZUNIONSTORE} command inserts all elements across all inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(byte[], byte[]...) + * @see #zunionstore(byte[], ZParams, byte[]...) + * @see #zinterstore(byte[], byte[]...) + * @see #zinterstore(byte[], ZParams, byte[]...) + * @param dstkey + * @param sets + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zinterstore(final byte[] dstkey, final byte[]... sets) { + checkIsInMultiOrPipeline(); + client.zinterstore(dstkey, sets); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(byte[], byte[]...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The {@link + * #zunionstore(byte[], byte[]...) ZUNIONSTORE} command inserts all elements across all inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(byte[], byte[]...) + * @see #zunionstore(byte[], ZParams, byte[]...) + * @see #zinterstore(byte[], byte[]...) + * @see #zinterstore(byte[], ZParams, byte[]...) + * @param dstkey + * @param sets + * @param params + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zinterstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { + checkIsInMultiOrPipeline(); + client.zinterstore(dstkey, params, sets); + return client.getIntegerReply(); + } + + @Override + public Long zlexcount(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zlexcount(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Set zrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zrangeByLex(key, min, max); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrangeByLex(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByLex(key, min, max, offset, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByLex(key, max, min); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Set zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min, final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByLex(key, max, min, offset, count); + return SetFromList.of(client.getBinaryMultiBulkReply()); + } + + @Override + public Long zremrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + checkIsInMultiOrPipeline(); + client.zremrangeByLex(key, min, max); + return client.getIntegerReply(); + } + + /** + * Synchronously save the DB on disk. + *

+ * Save the whole dataset on disk (this means that all the databases are saved, as well as keys + * with an EXPIRE set (the expire is preserved). The server hangs while the saving is not + * completed, no connection is served in the meanwhile. An OK code is returned when the DB was + * fully stored in disk. + *

+ * The background variant of this command is {@link #bgsave() BGSAVE} that is able to perform the + * saving in the background while the server continues serving other clients. + *

+ * @return Status code reply + */ + @Override + public String save() { + client.save(); + return client.getStatusCodeReply(); + } + + /** + * Asynchronously save the DB on disk. + *

+ * Save the DB in background. The OK code is immediately returned. Redis forks, the parent + * continues to server the clients, the child saves the DB on disk then exit. A client my be able + * to check if the operation succeeded using the LASTSAVE command. + * @return Status code reply + */ + @Override + public String bgsave() { + client.bgsave(); + return client.getStatusCodeReply(); + } + + /** + * Rewrite the append only file in background when it gets too big. Please for detailed + * information about the Redis Append Only File check the Append Only File Howto. + *

+ * BGREWRITEAOF rewrites the Append Only File in background when it gets too big. The Redis Append + * Only File is a Journal, so every operation modifying the dataset is logged in the Append Only + * File (and replayed at startup). This means that the Append Only File always grows. In order to + * rebuild its content the BGREWRITEAOF creates a new version of the append only file starting + * directly form the dataset in memory in order to guarantee the generation of the minimal number + * of commands needed to rebuild the database. + *

+ * @return Status code reply + */ + @Override + public String bgrewriteaof() { + client.bgrewriteaof(); + return client.getStatusCodeReply(); + } + + /** + * Return the UNIX time stamp of the last successfully saving of the dataset on disk. + *

+ * Return the UNIX TIME of the last DB save executed with success. A client may check if a + * {@link #bgsave() BGSAVE} command succeeded reading the LASTSAVE value, then issuing a BGSAVE + * command and checking at regular intervals every N seconds if LASTSAVE changed. + * @return Integer reply, specifically an UNIX time stamp. + */ + @Override + public Long lastsave() { + client.lastsave(); + return client.getIntegerReply(); + } + + /** + * Synchronously save the DB on disk, then shutdown the server. + *

+ * Stop all the clients, save the DB, then quit the server. This commands makes sure that the DB + * is switched off without the lost of any data. This is not guaranteed if the client uses simply + * {@link #save() SAVE} and then {@link #quit() QUIT} because other clients may alter the DB data + * between the two commands. + * @return Status code reply on error. On success nothing is returned since the server quits and + * the connection is closed. + */ + @Override + public String shutdown() { + client.shutdown(); + String status; + try { + status = client.getStatusCodeReply(); + } catch (JedisException ex) { + status = null; + } + return status; + } + + /** + * Provide information and statistics about the server. + *

+ * The info command returns different information and statistics about the server in an format + * that's simple to parse by computers and easy to read by humans. + *

+ * Format of the returned String: + *

+ * All the fields are in the form field:value + * + *

+   * edis_version:0.07
+   * connected_clients:1
+   * connected_slaves:0
+   * used_memory:3187
+   * changes_since_last_save:0
+   * last_save_time:1237655729
+   * total_connections_received:1
+   * total_commands_processed:1
+   * uptime_in_seconds:25
+   * uptime_in_days:0
+   * 
+ * + * Notes + *

+ * used_memory is returned in bytes, and is the total number of bytes allocated by the program + * using malloc. + *

+ * uptime_in_days is redundant since the uptime in seconds contains already the full uptime + * information, this field is only mainly present for humans. + *

+ * changes_since_last_save does not refer to the number of key changes, but to the number of + * operations that produced some kind of change in the dataset. + *

+ * @return Bulk reply + */ + @Override + public String info() { + client.info(); + return client.getBulkReply(); + } + + @Override + public String info(final String section) { + client.info(section); + return client.getBulkReply(); + } + + /** + * Dump all the received requests in real time. + *

+ * MONITOR is a debugging command that outputs the whole sequence of commands received by the + * Redis server. is very handy in order to understand what is happening into the database. This + * command is used directly via telnet. + * @param jedisMonitor + */ + public void monitor(final JedisMonitor jedisMonitor) { + client.monitor(); + client.getStatusCodeReply(); + jedisMonitor.proceed(client); + } + + /** + * Change the replication settings. + *

+ * The SLAVEOF command can change the replication settings of a slave on the fly. If a Redis + * server is arleady acting as slave, the command SLAVEOF NO ONE will turn off the replicaiton + * turning the Redis server into a MASTER. In the proper form SLAVEOF hostname port will make the + * server a slave of the specific server listening at the specified hostname and port. + *

+ * If a server is already a slave of some master, SLAVEOF hostname port will stop the replication + * against the old server and start the synchrnonization against the new one discarding the old + * dataset. + *

+ * The form SLAVEOF no one will stop replication turning the server into a MASTER but will not + * discard the replication. So if the old master stop working it is possible to turn the slave + * into a master and set the application to use the new master in read/write. Later when the other + * Redis server will be fixed it can be configured in order to work as slave. + *

+ * @param host + * @param port + * @return Status code reply + */ + @Override + public String slaveof(final String host, final int port) { + client.slaveof(host, port); + return client.getStatusCodeReply(); + } + + @Override + public String slaveofNoOne() { + client.slaveofNoOne(); + return client.getStatusCodeReply(); + } + + /** + * Retrieve the configuration of a running Redis server. Not all the configuration parameters are + * supported. + *

+ * CONFIG GET returns the current configuration parameters. This sub command only accepts a single + * argument, that is glob style pattern. All the configuration parameters matching this parameter + * are reported as a list of key-value pairs. + *

+ * Example: + * + *

+   * $ redis-cli config get '*'
+   * 1. "dbfilename"
+   * 2. "dump.rdb"
+   * 3. "requirepass"
+   * 4. (nil)
+   * 5. "masterauth"
+   * 6. (nil)
+   * 7. "maxmemory"
+   * 8. "0\n"
+   * 9. "appendfsync"
+   * 10. "everysec"
+   * 11. "save"
+   * 12. "3600 1 300 100 60 10000"
+   * 
+   * $ redis-cli config get 'm*'
+   * 1. "masterauth"
+   * 2. (nil)
+   * 3. "maxmemory"
+   * 4. "0\n"
+   * 
+ * @param pattern + * @return Bulk reply. + */ + @Override + public List configGet(final byte[] pattern) { + checkIsInMultiOrPipeline(); + client.configGet(pattern); + return client.getBinaryMultiBulkReply(); + } + + /** + * Reset the stats returned by INFO + * @return + */ + @Override + public String configResetStat() { + checkIsInMultiOrPipeline(); + client.configResetStat(); + return client.getStatusCodeReply(); + } + + /** + * The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying + * the minimal changes needed to make it reflect the configuration currently used by the server, + * which may be different compared to the original one because of the use of the CONFIG SET command. + * + * The rewrite is performed in a very conservative way: + *
    + *
  • Comments and the overall structure of the original redis.conf are preserved as much as possible.
  • + *
  • If an option already exists in the old redis.conf file, it will be rewritten at the same position (line number).
  • + *
  • If an option was not already present, but it is set to its default value, it is not added by the rewrite process.
  • + *
  • If an option was not already present, but it is set to a non-default value, it is appended at the end of the file.
  • + *
  • Non used lines are blanked. For instance if you used to have multiple save directives, but + * the current configuration has fewer or none as you disabled RDB persistence, all the lines will be blanked.
  • + *
+ * + * CONFIG REWRITE is also able to rewrite the configuration file from scratch if the original one + * no longer exists for some reason. However if the server was started without a configuration + * file at all, the CONFIG REWRITE will just return an error. + * @return OK when the configuration was rewritten properly. Otherwise an error is returned. + */ + @Override + public String configRewrite() { + checkIsInMultiOrPipeline(); + client.configRewrite(); + return client.getStatusCodeReply(); + } + + /** + * Alter the configuration of a running Redis server. Not all the configuration parameters are + * supported. + *

+ * The list of configuration parameters supported by CONFIG SET can be obtained issuing a + * {@link #configGet(byte[]) CONFIG GET *} command. + *

+ * The configuration set using CONFIG SET is immediately loaded by the Redis server that will + * start acting as specified starting from the next command. + *

+ * Parameters value format + *

+ * The value of the configuration parameter is the same as the one of the same parameter in the + * Redis configuration file, with the following exceptions: + *

+ *

    + *
  • The save paramter is a list of space-separated integers. Every pair of integers specify the + * time and number of changes limit to trigger a save. For instance the command CONFIG SET save + * "3600 10 60 10000" will configure the server to issue a background saving of the RDB file every + * 3600 seconds if there are at least 10 changes in the dataset, and every 60 seconds if there are + * at least 10000 changes. To completely disable automatic snapshots just set the parameter as an + * empty string. + *
  • All the integer parameters representing memory are returned and accepted only using bytes + * as unit. + *
+ * @param parameter + * @param value + * @return Status code reply + */ + @Override + public byte[] configSet(final byte[] parameter, final byte[] value) { + checkIsInMultiOrPipeline(); + client.configSet(parameter, value); + return client.getBinaryBulkReply(); + } + + public boolean isConnected() { + return client.isConnected(); + } + + @Override + public Long strlen(final byte[] key) { + checkIsInMultiOrPipeline(); + client.strlen(key); + return client.getIntegerReply(); + } + + public void sync() { + client.sync(); + } + + @Override + public Long lpushx(final byte[] key, final byte[]... string) { + checkIsInMultiOrPipeline(); + client.lpushx(key, string); + return client.getIntegerReply(); + } + + /** + * Undo a {@link #expire(byte[], int) expire} at turning the expire key into a normal key. + *

+ * Time complexity: O(1) + * @param key + * @return Integer reply, specifically: 1: the key is now persist. 0: the key is not persist (only + * happens when key not set). + */ + @Override + public Long persist(final byte[] key) { + client.persist(key); + return client.getIntegerReply(); + } + + @Override + public Long rpushx(final byte[] key, final byte[]... string) { + checkIsInMultiOrPipeline(); + client.rpushx(key, string); + return client.getIntegerReply(); + } + + @Override + public byte[] echo(final byte[] string) { + checkIsInMultiOrPipeline(); + client.echo(string); + return client.getBinaryBulkReply(); + } + + @Override + public Long linsert(final byte[] key, final ListPosition where, final byte[] pivot, + final byte[] value) { + checkIsInMultiOrPipeline(); + client.linsert(key, where, pivot, value); + return client.getIntegerReply(); + } + + @Override + public String debug(final DebugParams params) { + client.debug(params); + return client.getStatusCodeReply(); + } + + public Client getClient() { + return client; + } + + /** + * Pop a value from a list, push it to another list and return it; or block until one is available + * @param source + * @param destination + * @param timeout + * @return the element + */ + @Override + public byte[] brpoplpush(final byte[] source, final byte[] destination, final int timeout) { + client.brpoplpush(source, destination, timeout); + client.setTimeoutInfinite(); + try { + return client.getBinaryBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + + /** + * Sets or clears the bit at offset in the string value stored at key + * @param key + * @param offset + * @param value + * @return + */ + @Override + public Boolean setbit(final byte[] key, final long offset, final boolean value) { + checkIsInMultiOrPipeline(); + client.setbit(key, offset, value); + return client.getIntegerReply() == 1; + } + + @Override + public Boolean setbit(final byte[] key, final long offset, final byte[] value) { + checkIsInMultiOrPipeline(); + client.setbit(key, offset, value); + return client.getIntegerReply() == 1; + } + + /** + * Returns the bit value at offset in the string value stored at key + * @param key + * @param offset + * @return + */ + @Override + public Boolean getbit(final byte[] key, final long offset) { + checkIsInMultiOrPipeline(); + client.getbit(key, offset); + return client.getIntegerReply() == 1; + } + + public Long bitpos(final byte[] key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Long bitpos(final byte[] key, final boolean value, final BitPosParams params) { + checkIsInMultiOrPipeline(); + client.bitpos(key, value, params); + return client.getIntegerReply(); + } + + @Override + public Long setrange(final byte[] key, final long offset, final byte[] value) { + checkIsInMultiOrPipeline(); + client.setrange(key, offset, value); + return client.getIntegerReply(); + } + + @Override + public byte[] getrange(final byte[] key, final long startOffset, final long endOffset) { + checkIsInMultiOrPipeline(); + client.getrange(key, startOffset, endOffset); + return client.getBinaryBulkReply(); + } + + @Override + public Long publish(final byte[] channel, final byte[] message) { + checkIsInMultiOrPipeline(); + client.publish(channel, message); + return client.getIntegerReply(); + } + + @Override + public void subscribe(BinaryJedisPubSub jedisPubSub, final byte[]... channels) { + client.setTimeoutInfinite(); + try { + jedisPubSub.proceed(client, channels); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public void psubscribe(BinaryJedisPubSub jedisPubSub, final byte[]... patterns) { + client.setTimeoutInfinite(); + try { + jedisPubSub.proceedWithPatterns(client, patterns); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public int getDB() { + return client.getDB(); + } + + /** + * Evaluates scripts using the Lua interpreter built into Redis starting from version 2.6.0. + *

+ * @return Script result + */ + @Override + public Object eval(final byte[] script, final List keys, final List args) { + return eval(script, toByteArray(keys.size()), getParamsWithBinary(keys, args)); + } + + protected static byte[][] getParamsWithBinary(List keys, List args) { + final int keyCount = keys.size(); + final int argCount = args.size(); + byte[][] params = new byte[keyCount + argCount][]; + + for (int i = 0; i < keyCount; i++) + params[i] = keys.get(i); + + for (int i = 0; i < argCount; i++) + params[keyCount + i] = args.get(i); + + return params; + } + + @Override + public Object eval(final byte[] script, final byte[] keyCount, final byte[]... params) { + client.setTimeoutInfinite(); + try { + client.eval(script, keyCount, params); + return client.getOne(); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public Object eval(final byte[] script, final int keyCount, final byte[]... params) { + return eval(script, toByteArray(keyCount), params); + } + + @Override + public Object eval(final byte[] script) { + return eval(script, 0); + } + + @Override + public Object evalsha(final byte[] sha1) { + return evalsha(sha1, 0); + } + + @Override + public Object evalsha(final byte[] sha1, final List keys, final List args) { + return evalsha(sha1, keys.size(), getParamsWithBinary(keys, args)); + } + + @Override + public Object evalsha(final byte[] sha1, final int keyCount, final byte[]... params) { + client.setTimeoutInfinite(); + try { + client.evalsha(sha1, keyCount, params); + return client.getOne(); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public String scriptFlush() { + client.scriptFlush(); + return client.getStatusCodeReply(); + } + + public Long scriptExists(final byte[] sha1) { + byte[][] a = new byte[1][]; + a[0] = sha1; + return scriptExists(a).get(0); + } + + @Override + public List scriptExists(final byte[]... sha1) { + client.scriptExists(sha1); + return client.getIntegerMultiBulkReply(); + } + + @Override + public byte[] scriptLoad(final byte[] script) { + client.scriptLoad(script); + return client.getBinaryBulkReply(); + } + + @Override + public String scriptKill() { + client.scriptKill(); + return client.getStatusCodeReply(); + } + + @Override + public String slowlogReset() { + client.slowlogReset(); + return client.getBulkReply(); + } + + @Override + public Long slowlogLen() { + client.slowlogLen(); + return client.getIntegerReply(); + } + + @Override + public List slowlogGetBinary() { + client.slowlogGet(); + return client.getBinaryMultiBulkReply(); + } + + @Override + public List slowlogGetBinary(final long entries) { + client.slowlogGet(entries); + return client.getBinaryMultiBulkReply(); + } + + @Override + public Long objectRefcount(final byte[] key) { + client.objectRefcount(key); + return client.getIntegerReply(); + } + + @Override + public byte[] objectEncoding(final byte[] key) { + client.objectEncoding(key); + return client.getBinaryBulkReply(); + } + + @Override + public Long objectIdletime(final byte[] key) { + client.objectIdletime(key); + return client.getIntegerReply(); + } + + @Override + public Long bitcount(final byte[] key) { + checkIsInMultiOrPipeline(); + client.bitcount(key); + return client.getIntegerReply(); + } + + @Override + public Long bitcount(final byte[] key, final long start, final long end) { + checkIsInMultiOrPipeline(); + client.bitcount(key, start, end); + return client.getIntegerReply(); + } + + @Override + public Long bitop(final BitOP op, final byte[] destKey, final byte[]... srcKeys) { + checkIsInMultiOrPipeline(); + client.bitop(op, destKey, srcKeys); + return client.getIntegerReply(); + } + + @Override + public byte[] dump(final byte[] key) { + checkIsInMultiOrPipeline(); + client.dump(key); + return client.getBinaryBulkReply(); + } + + @Override + public String restore(final byte[] key, final int ttl, final byte[] serializedValue) { + checkIsInMultiOrPipeline(); + client.restore(key, ttl, serializedValue); + return client.getStatusCodeReply(); + } + + /** + * Set a timeout on the specified key. After the timeout the key will be automatically deleted by + * the server. A key with an associated timeout is said to be volatile in Redis terminology. + *

+ * Voltile keys are stored on disk like the other keys, the timeout is persistent too like all the + * other aspects of the dataset. Saving a dataset containing expires and stopping the server does + * not stop the flow of time as Redis stores on disk the time when the key will no longer be + * available as Unix time, and not the remaining milliseconds. + *

+ * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire + * set. It is also possible to undo the expire at all turning the key into a normal key using the + * {@link #persist(byte[]) PERSIST} command. + *

+ * Time complexity: O(1) + * @see PEXPIRE Command + * @param key + * @param milliseconds + * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since + * the key already has an associated timeout (this may happen only in Redis versions < + * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. + */ + @Override + public Long pexpire(final byte[] key, final long milliseconds) { + checkIsInMultiOrPipeline(); + client.pexpire(key, milliseconds); + return client.getIntegerReply(); + } + + @Override + public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) { + checkIsInMultiOrPipeline(); + client.pexpireAt(key, millisecondsTimestamp); + return client.getIntegerReply(); + } + + @Override + public Long pttl(final byte[] key) { + checkIsInMultiOrPipeline(); + client.pttl(key); + return client.getIntegerReply(); + } + + /** + * PSETEX works exactly like {@link #setex(byte[], int, byte[])} with the sole difference that the + * expire time is specified in milliseconds instead of seconds. Time complexity: O(1) + * @param key + * @param milliseconds + * @param value + * @return Status code reply + */ + public String psetex(final byte[] key, final long milliseconds, final byte[] value) { + checkIsInMultiOrPipeline(); + client.psetex(key, milliseconds, value); + return client.getStatusCodeReply(); + } + + public String clientKill(final byte[] client) { + checkIsInMultiOrPipeline(); + this.client.clientKill(client); + return this.client.getStatusCodeReply(); + } + + public String clientGetname() { + checkIsInMultiOrPipeline(); + client.clientGetname(); + return client.getBulkReply(); + } + + public String clientList() { + checkIsInMultiOrPipeline(); + client.clientList(); + return client.getBulkReply(); + } + + public String clientSetname(final byte[] name) { + checkIsInMultiOrPipeline(); + client.clientSetname(name); + return client.getBulkReply(); + } + + public List time() { + checkIsInMultiOrPipeline(); + client.time(); + return client.getMultiBulkReply(); + } + + public String migrate(final byte[] host, final int port, final byte[] key, + final int destinationDb, final int timeout) { + checkIsInMultiOrPipeline(); + client.migrate(host, port, key, destinationDb, timeout); + return client.getStatusCodeReply(); + } + + /** + * Syncrhonous replication of Redis as described here: http://antirez.com/news/66 Since Java + * Object class has implemented "wait" method, we cannot use it, so I had to change the name of + * the method. Sorry :S + */ + @Override + public Long waitReplicas(final int replicas, final long timeout) { + checkIsInMultiOrPipeline(); + client.waitReplicas(replicas, timeout); + return client.getIntegerReply(); + } + + @Override + public Long pfadd(final byte[] key, final byte[]... elements) { + checkIsInMultiOrPipeline(); + client.pfadd(key, elements); + return client.getIntegerReply(); + } + + @Override + public long pfcount(final byte[] key) { + checkIsInMultiOrPipeline(); + client.pfcount(key); + return client.getIntegerReply(); + } + + @Override + public String pfmerge(final byte[] destkey, final byte[]... sourcekeys) { + checkIsInMultiOrPipeline(); + client.pfmerge(destkey, sourcekeys); + return client.getStatusCodeReply(); + } + + @Override + public Long pfcount(final byte[]... keys) { + checkIsInMultiOrPipeline(); + client.pfcount(keys); + return client.getIntegerReply(); + } + + public ScanResult scan(final byte[] cursor) { + return scan(cursor, new ScanParams()); + } + + public ScanResult scan(final byte[] cursor, final ScanParams params) { + checkIsInMultiOrPipeline(); + client.scan(cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List rawResults = (List) result.get(1); + return new ScanResult(newcursor, rawResults); + } + + @Override + public ScanResult> hscan(final byte[] key, final byte[] cursor) { + return hscan(key, cursor, new ScanParams()); + } + + @Override + public ScanResult> hscan(final byte[] key, final byte[] cursor, + final ScanParams params) { + checkIsInMultiOrPipeline(); + client.hscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List> results = new ArrayList>(); + List rawResults = (List) result.get(1); + Iterator iterator = rawResults.iterator(); + while (iterator.hasNext()) { + results.add(new AbstractMap.SimpleEntry(iterator.next(), iterator.next())); + } + return new ScanResult>(newcursor, results); + } + + @Override + public ScanResult sscan(final byte[] key, final byte[] cursor) { + return sscan(key, cursor, new ScanParams()); + } + + @Override + public ScanResult sscan(final byte[] key, final byte[] cursor, final ScanParams params) { + checkIsInMultiOrPipeline(); + client.sscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List rawResults = (List) result.get(1); + return new ScanResult(newcursor, rawResults); + } + + @Override + public ScanResult zscan(final byte[] key, final byte[] cursor) { + return zscan(key, cursor, new ScanParams()); + } + + @Override + public ScanResult zscan(final byte[] key, final byte[] cursor, final ScanParams params) { + checkIsInMultiOrPipeline(); + client.zscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List results = new ArrayList(); + List rawResults = (List) result.get(1); + Iterator iterator = rawResults.iterator(); + while (iterator.hasNext()) { + results.add(new Tuple(iterator.next(), BuilderFactory.DOUBLE.build(iterator.next()))); + } + return new ScanResult(newcursor, results); + } + + @Override + public Long geoadd(final byte[] key, final double longitude, final double latitude, final byte[] member) { + checkIsInMultiOrPipeline(); + client.geoadd(key, longitude, latitude, member); + return client.getIntegerReply(); + } + + @Override + public Long geoadd(final byte[] key, final Map memberCoordinateMap) { + checkIsInMultiOrPipeline(); + client.geoadd(key, memberCoordinateMap); + return client.getIntegerReply(); + } + + @Override + public Double geodist(final byte[] key, final byte[] member1, final byte[] member2) { + checkIsInMultiOrPipeline(); + client.geodist(key, member1, member2); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + @Override + public Double geodist(final byte[] key, final byte[] member1, final byte[] member2, final GeoUnit unit) { + checkIsInMultiOrPipeline(); + client.geodist(key, member1, member2, unit); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + @Override + public List geohash(final byte[] key, final byte[]... members) { + checkIsInMultiOrPipeline(); + client.geohash(key, members); + return client.getBinaryMultiBulkReply(); + } + + @Override + public List geopos(final byte[] key, final byte[]... members) { + checkIsInMultiOrPipeline(); + client.geopos(key, members); + return BuilderFactory.GEO_COORDINATE_LIST.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadius(final byte[] key, final double longitude, final double latitude, + final double radius, final GeoUnit unit) { + checkIsInMultiOrPipeline(); + client.georadius(key, longitude, latitude, radius, unit); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadius(final byte[] key, final double longitude, final double latitude, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + checkIsInMultiOrPipeline(); + client.georadius(key, longitude, latitude, radius, unit, param); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadiusByMember(final byte[] key, final byte[] member, final double radius, + final GeoUnit unit) { + checkIsInMultiOrPipeline(); + client.georadiusByMember(key, member, radius, unit); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadiusByMember(final byte[] key, final byte[] member, final double radius, + final GeoUnit unit, final GeoRadiusParam param) { + checkIsInMultiOrPipeline(); + client.georadiusByMember(key, member, radius, unit, param); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + /** + * A decorator to implement Set from List. Assume that given List do not contains duplicated + * values. The resulting set displays the same ordering, concurrency, and performance + * characteristics as the backing list. This class should be used only for Redis commands which + * return Set result. + * @param + */ + protected static class SetFromList extends AbstractSet implements Serializable { + private static final long serialVersionUID = -2850347066962734052L; + private final transient List list; + + private SetFromList(List list) { + if (list == null) { + throw new NullPointerException("list"); + } + this.list = list; + } + + @Override + public void clear() { + list.clear(); + } + + @Override + public int size() { + return list.size(); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return list.contains(o); + } + + @Override + public boolean remove(Object o) { + return list.remove(o); + } + + @Override + public boolean add(E e) { + return !contains(e) && list.add(e); + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + + @Override + public Object[] toArray() { + return list.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return list.toArray(a); + } + + @Override + public String toString() { + return list.toString(); + } + + @Override + public int hashCode() { + return list.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof Set)) { + return false; + } + + Collection c = (Collection) o; + if (c.size() != size()) { + return false; + } + + return containsAll(c); + } + + @Override + public boolean containsAll(Collection c) { + return list.containsAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return list.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return list.retainAll(c); + } + + protected static SetFromList of(List list) { + return new SetFromList(list); + } + } + + @Override + public List bitfield(final byte[] key, final byte[]... arguments) { + checkIsInMultiOrPipeline(); + client.bitfield(key, arguments); + return client.getIntegerMultiBulkReply(); + } + + @Override + public Long hstrlen(final byte[] key, final byte[] field) { + checkIsInMultiOrPipeline(); + client.hstrlen(key, field); + return client.getIntegerReply(); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisCluster.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisCluster.java new file mode 100755 index 000000000..078699f6d --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisCluster.java @@ -0,0 +1,1977 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.commands.BinaryJedisClusterCommands; +import com.fr.third.redis.clients.jedis.commands.JedisClusterBinaryScriptingCommands; +import com.fr.third.redis.clients.jedis.commands.MultiKeyBinaryJedisClusterCommands; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.util.KeyMergeUtil; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +import java.io.Closeable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import com.fr.third.redis.clients.jedis.util.JedisClusterHashTagUtil; + +public class BinaryJedisCluster implements BinaryJedisClusterCommands, + MultiKeyBinaryJedisClusterCommands, JedisClusterBinaryScriptingCommands, Closeable { + + public static final int HASHSLOTS = 16384; + protected static final int DEFAULT_TIMEOUT = 2000; + protected static final int DEFAULT_MAX_ATTEMPTS = 5; + + protected int maxAttempts; + + protected JedisClusterConnectionHandler connectionHandler; + + public BinaryJedisCluster(Set nodes, int timeout) { + this(nodes, timeout, DEFAULT_MAX_ATTEMPTS, new GenericObjectPoolConfig()); + } + + public BinaryJedisCluster(Set nodes) { + this(nodes, DEFAULT_TIMEOUT); + } + + public BinaryJedisCluster(Set jedisClusterNode, int timeout, int maxAttempts, + final GenericObjectPoolConfig poolConfig) { + this.connectionHandler = new JedisSlotBasedConnectionHandler(jedisClusterNode, poolConfig, + timeout); + this.maxAttempts = maxAttempts; + } + + public BinaryJedisCluster(Set jedisClusterNode, int connectionTimeout, + int soTimeout, int maxAttempts, final GenericObjectPoolConfig poolConfig) { + this.connectionHandler = new JedisSlotBasedConnectionHandler(jedisClusterNode, poolConfig, + connectionTimeout, soTimeout); + this.maxAttempts = maxAttempts; + } + + public BinaryJedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String password, GenericObjectPoolConfig poolConfig) { + this.connectionHandler = new JedisSlotBasedConnectionHandler(jedisClusterNode, poolConfig, + connectionTimeout, soTimeout, password); + this.maxAttempts = maxAttempts; + } + + public BinaryJedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, GenericObjectPoolConfig poolConfig) { + this.connectionHandler = new JedisSlotBasedConnectionHandler(jedisClusterNode, poolConfig, + connectionTimeout, soTimeout, password, clientName); + this.maxAttempts = maxAttempts; + } + + @Override + public void close() { + if (connectionHandler != null) { + connectionHandler.close(); + } + } + + public Map getClusterNodes() { + return connectionHandler.getNodes(); + } + + public Jedis getConnectionFromSlot(int slot) { + return this.connectionHandler.getConnectionFromSlot(slot); + } + + @Override + public String set(final byte[] key, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.set(key, value); + } + }.runBinary(key); + } + + @Override + public String set(final byte[] key, final byte[] value, final SetParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.set(key, value, params); + } + }.runBinary(key); + } + + @Override + public byte[] get(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.get(key); + } + }.runBinary(key); + } + + @Override + public Long exists(final byte[]... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.exists(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public Boolean exists(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.exists(key); + } + }.runBinary(key); + } + + @Override + public Long persist(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.persist(key); + } + }.runBinary(key); + } + + @Override + public String type(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.type(key); + } + }.runBinary(key); + } + + @Override + public byte[] dump(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.dump(key); + } + }.runBinary(key); + } + + @Override + public String restore(final byte[] key, final int ttl, final byte[] serializedValue) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.restore(key, ttl, serializedValue); + } + }.runBinary(key); + } + + @Override + public Long expire(final byte[] key, final int seconds) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.expire(key, seconds); + } + }.runBinary(key); + } + + @Override + public Long pexpire(final byte[] key, final long milliseconds) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pexpire(key, milliseconds); + } + }.runBinary(key); + } + + @Override + public Long expireAt(final byte[] key, final long unixTime) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.expireAt(key, unixTime); + } + }.runBinary(key); + } + + @Override + public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pexpireAt(key, millisecondsTimestamp); + } + }.runBinary(key); + } + + @Override + public Long ttl(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.ttl(key); + } + }.runBinary(key); + } + + @Override + public Long pttl(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pttl(key); + } + }.runBinary(key); + } + + @Override + public Long touch(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.touch(key); + } + }.runBinary(key); + } + + @Override + public Long touch(final byte[]... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.touch(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public Boolean setbit(final byte[] key, final long offset, final boolean value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.setbit(key, offset, value); + } + }.runBinary(key); + } + + @Override + public Boolean setbit(final byte[] key, final long offset, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.setbit(key, offset, value); + } + }.runBinary(key); + } + + @Override + public Boolean getbit(final byte[] key, final long offset) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.getbit(key, offset); + } + }.runBinary(key); + } + + @Override + public Long setrange(final byte[] key, final long offset, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.setrange(key, offset, value); + } + }.runBinary(key); + } + + @Override + public byte[] getrange(final byte[] key, final long startOffset, final long endOffset) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.getrange(key, startOffset, endOffset); + } + }.runBinary(key); + } + + @Override + public byte[] getSet(final byte[] key, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.getSet(key, value); + } + }.runBinary(key); + } + + @Override + public Long setnx(final byte[] key, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.setnx(key, value); + } + }.runBinary(key); + } + + @Override + public String psetex(final byte[] key, final long milliseconds, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.psetex(key, milliseconds, value); + } + }.runBinary(key); + } + + @Override + public String setex(final byte[] key, final int seconds, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.setex(key, seconds, value); + } + }.runBinary(key); + } + + @Override + public Long decrBy(final byte[] key, final long decrement) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.decrBy(key, decrement); + } + }.runBinary(key); + } + + @Override + public Long decr(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.decr(key); + } + }.runBinary(key); + } + + @Override + public Long incrBy(final byte[] key, final long increment) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.incrBy(key, increment); + } + }.runBinary(key); + } + + @Override + public Double incrByFloat(final byte[] key, final double increment) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.incrByFloat(key, increment); + } + }.runBinary(key); + } + + @Override + public Long incr(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.incr(key); + } + }.runBinary(key); + } + + @Override + public Long append(final byte[] key, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.append(key, value); + } + }.runBinary(key); + } + + @Override + public byte[] substr(final byte[] key, final int start, final int end) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.substr(key, start, end); + } + }.runBinary(key); + } + + @Override + public Long hset(final byte[] key, final byte[] field, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hset(key, field, value); + } + }.runBinary(key); + } + + @Override + public Long hset(final byte[] key, final Map hash) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hset(key, hash); + } + }.runBinary(key); + } + + @Override + public byte[] hget(final byte[] key, final byte[] field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.hget(key, field); + } + }.runBinary(key); + } + + @Override + public Long hsetnx(final byte[] key, final byte[] field, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hsetnx(key, field, value); + } + }.runBinary(key); + } + + @Override + public String hmset(final byte[] key, final Map hash) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.hmset(key, hash); + } + }.runBinary(key); + } + + @Override + public List hmget(final byte[] key, final byte[]... fields) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.hmget(key, fields); + } + }.runBinary(key); + } + + @Override + public Long hincrBy(final byte[] key, final byte[] field, final long value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hincrBy(key, field, value); + } + }.runBinary(key); + } + + @Override + public Double hincrByFloat(final byte[] key, final byte[] field, final double value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.hincrByFloat(key, field, value); + } + }.runBinary(key); + } + + @Override + public Boolean hexists(final byte[] key, final byte[] field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.hexists(key, field); + } + }.runBinary(key); + } + + @Override + public Long hdel(final byte[] key, final byte[]... field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hdel(key, field); + } + }.runBinary(key); + } + + @Override + public Long hlen(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hlen(key); + } + }.runBinary(key); + } + + @Override + public Set hkeys(final byte[] key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.hkeys(key); + } + }.runBinary(key); + } + + @Override + public Collection hvals(final byte[] key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Collection execute(Jedis connection) { + return connection.hvals(key); + } + }.runBinary(key); + } + + @Override + public Map hgetAll(final byte[] key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Map execute(Jedis connection) { + return connection.hgetAll(key); + } + }.runBinary(key); + } + + @Override + public Long rpush(final byte[] key, final byte[]... args) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.rpush(key, args); + } + }.runBinary(key); + } + + @Override + public Long lpush(final byte[] key, final byte[]... args) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.lpush(key, args); + } + }.runBinary(key); + } + + @Override + public Long llen(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.llen(key); + } + }.runBinary(key); + } + + @Override + public List lrange(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.lrange(key, start, stop); + } + }.runBinary(key); + } + + @Override + public String ltrim(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.ltrim(key, start, stop); + } + }.runBinary(key); + } + + @Override + public byte[] lindex(final byte[] key, final long index) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.lindex(key, index); + } + }.runBinary(key); + } + + @Override + public String lset(final byte[] key, final long index, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.lset(key, index, value); + } + }.runBinary(key); + } + + @Override + public Long lrem(final byte[] key, final long count, final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.lrem(key, count, value); + } + }.runBinary(key); + } + + @Override + public byte[] lpop(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.lpop(key); + } + }.runBinary(key); + } + + @Override + public byte[] rpop(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.rpop(key); + } + }.runBinary(key); + } + + @Override + public Long sadd(final byte[] key, final byte[]... member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sadd(key, member); + } + }.runBinary(key); + } + + @Override + public Set smembers(final byte[] key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.smembers(key); + } + }.runBinary(key); + } + + @Override + public Long srem(final byte[] key, final byte[]... member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.srem(key, member); + } + }.runBinary(key); + } + + @Override + public byte[] spop(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.spop(key); + } + }.runBinary(key); + } + + @Override + public Set spop(final byte[] key, final long count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.spop(key, count); + } + }.runBinary(key); + } + + @Override + public Long scard(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.scard(key); + } + }.runBinary(key); + } + + @Override + public Boolean sismember(final byte[] key, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.sismember(key, member); + } + }.runBinary(key); + } + + @Override + public byte[] srandmember(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.srandmember(key); + } + }.runBinary(key); + } + + @Override + public Long strlen(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.strlen(key); + } + }.runBinary(key); + } + + @Override + public Long zadd(final byte[] key, final double score, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, score, member); + } + }.runBinary(key); + } + + @Override + public Long zadd(final byte[] key, final double score, final byte[] member, + final ZAddParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, score, member, params); + } + }.runBinary(key); + } + + @Override + public Long zadd(final byte[] key, final Map scoreMembers) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, scoreMembers); + } + }.runBinary(key); + } + + @Override + public Long zadd(final byte[] key, final Map scoreMembers, final ZAddParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, scoreMembers, params); + } + }.runBinary(key); + } + + @Override + public Set zrange(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrange(key, start, stop); + } + }.runBinary(key); + } + + @Override + public Long zrem(final byte[] key, final byte[]... members) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zrem(key, members); + } + }.runBinary(key); + } + + @Override + public Double zincrby(final byte[] key, final double increment, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.zincrby(key, increment, member); + } + }.runBinary(key); + } + + @Override + public Double zincrby(final byte[] key, final double increment, final byte[] member, + final ZIncrByParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.zincrby(key, increment, member, params); + } + }.runBinary(key); + } + + @Override + public Long zrank(final byte[] key, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zrank(key, member); + } + }.runBinary(key); + } + + @Override + public Long zrevrank(final byte[] key, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zrevrank(key, member); + } + }.runBinary(key); + } + + @Override + public Set zrevrange(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrange(key, start, stop); + } + }.runBinary(key); + } + + @Override + public Set zrangeWithScores(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeWithScores(key, start, stop); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeWithScores(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeWithScores(key, start, stop); + } + }.runBinary(key); + } + + @Override + public Long zcard(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zcard(key); + } + }.runBinary(key); + } + + @Override + public Double zscore(final byte[] key, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.zscore(key, member); + } + }.runBinary(key); + } + + @Override + public List sort(final byte[] key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.sort(key); + } + }.runBinary(key); + } + + @Override + public List sort(final byte[] key, final SortingParams sortingParameters) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.sort(key, sortingParameters); + } + }.runBinary(key); + } + + @Override + public Long zcount(final byte[] key, final double min, final double max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zcount(key, min, max); + } + }.runBinary(key); + } + + @Override + public Long zcount(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zcount(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScore(final byte[] key, final double min, final double max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final double max, final double min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScore(final byte[] key, final double min, final double max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScore(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final double max, final double min, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final double min, final double max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final double max, final double min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final double min, final double max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min); + } + }.runBinary(key); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final double max, + final double min, final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final byte[] max, + final byte[] min, final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + }.runBinary(key); + } + + @Override + public Long zremrangeByRank(final byte[] key, final long start, final long stop) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByRank(key, start, stop); + } + }.runBinary(key); + } + + @Override + public Long zremrangeByScore(final byte[] key, final double min, final double max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByScore(key, min, max); + } + }.runBinary(key); + } + + @Override + public Long zremrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByScore(key, min, max); + } + }.runBinary(key); + } + + @Override + public Long linsert(final byte[] key, final ListPosition where, final byte[] pivot, + final byte[] value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.linsert(key, where, pivot, value); + } + }.runBinary(key); + } + + @Override + public Long lpushx(final byte[] key, final byte[]... arg) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.lpushx(key, arg); + } + }.runBinary(key); + } + + @Override + public Long rpushx(final byte[] key, final byte[]... arg) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.rpushx(key, arg); + } + }.runBinary(key); + } + + @Override + public Long del(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.del(key); + } + }.runBinary(key); + } + + @Override + public Long unlink(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.unlink(key); + } + }.runBinary(key); + } + + @Override + public Long unlink(final byte[]... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.unlink(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public byte[] echo(final byte[] arg) { + // note that it'll be run from arbitary node + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.echo(arg); + } + }.runBinary(arg); + } + + @Override + public Long bitcount(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.bitcount(key); + } + }.runBinary(key); + } + + @Override + public Long bitcount(final byte[] key, final long start, final long end) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.bitcount(key, start, end); + } + }.runBinary(key); + } + + @Override + public Long pfadd(final byte[] key, final byte[]... elements) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pfadd(key, elements); + } + }.runBinary(key); + } + + @Override + public long pfcount(final byte[] key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pfcount(key); + } + }.runBinary(key); + } + + @Override + public List srandmember(final byte[] key, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.srandmember(key, count); + } + }.runBinary(key); + } + + @Override + public Long zlexcount(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zlexcount(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByLex(key, min, max); + } + }.runBinary(key); + } + + @Override + public Set zrangeByLex(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByLex(key, min, max, offset, count); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByLex(key, max, min); + } + }.runBinary(key); + } + + @Override + public Set zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByLex(key, max, min, offset, count); + } + }.runBinary(key); + } + + @Override + public Long zremrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByLex(key, min, max); + } + }.runBinary(key); + } + + @Override + public Object eval(final byte[] script, final byte[] keyCount, final byte[]... params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script, keyCount, params); + } + }.runBinary(Integer.parseInt(SafeEncoder.encode(keyCount)), params); + } + + @Override + public Object eval(final byte[] script, final int keyCount, final byte[]... params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script, keyCount, params); + } + }.runBinary(keyCount, params); + } + + @Override + public Object eval(final byte[] script, final List keys, final List args) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script, keys, args); + } + }.runBinary(keys.size(), keys.toArray(new byte[keys.size()][])); + } + + @Override + public Object eval(final byte[] script, final byte[] sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script); + } + }.runBinary(sampleKey); + } + + @Override + public Object evalsha(final byte[] sha1, final byte[] sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.evalsha(sha1); + } + }.runBinary(sampleKey); + } + + @Override + public Object evalsha(final byte[] sha1, final List keys, final List args) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.evalsha(sha1, keys, args); + } + }.runBinary(keys.size(), keys.toArray(new byte[keys.size()][])); + } + + @Override + public Object evalsha(final byte[] sha1, final int keyCount, final byte[]... params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.evalsha(sha1, keyCount, params); + } + }.runBinary(keyCount, params); + } + + @Override + public List scriptExists(final byte[] sampleKey, final byte[]... sha1) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.scriptExists(sha1); + } + }.runBinary(sampleKey); + } + + @Override + public byte[] scriptLoad(final byte[] script, final byte[] sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.scriptLoad(script); + } + }.runBinary(sampleKey); + } + + @Override + public String scriptFlush(final byte[] sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.scriptFlush(); + } + }.runBinary(sampleKey); + } + + @Override + public String scriptKill(final byte[] sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.scriptKill(); + } + }.runBinary(sampleKey); + } + + @Override + public Long del(final byte[]... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.del(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public List blpop(final int timeout, final byte[]... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.blpop(timeout, keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public List brpop(final int timeout, final byte[]... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.brpop(timeout, keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public List mget(final byte[]... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.mget(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public String mset(final byte[]... keysvalues) { + byte[][] keys = new byte[keysvalues.length / 2][]; + + for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) { + keys[keyIdx] = keysvalues[keyIdx * 2]; + } + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.mset(keysvalues); + } + }.runBinary(keys.length, keys); + } + + @Override + public Long msetnx(final byte[]... keysvalues) { + byte[][] keys = new byte[keysvalues.length / 2][]; + + for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) { + keys[keyIdx] = keysvalues[keyIdx * 2]; + } + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.msetnx(keysvalues); + } + }.runBinary(keys.length, keys); + } + + @Override + public String rename(final byte[] oldkey, final byte[] newkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.rename(oldkey, newkey); + } + }.runBinary(2, oldkey, newkey); + } + + @Override + public Long renamenx(final byte[] oldkey, final byte[] newkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.renamenx(oldkey, newkey); + } + }.runBinary(2, oldkey, newkey); + } + + @Override + public byte[] rpoplpush(final byte[] srckey, final byte[] dstkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.rpoplpush(srckey, dstkey); + } + }.runBinary(2, srckey, dstkey); + } + + @Override + public Set sdiff(final byte[]... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.sdiff(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public Long sdiffstore(final byte[] dstkey, final byte[]... keys) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, keys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sdiffstore(dstkey, keys); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Set sinter(final byte[]... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.sinter(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public Long sinterstore(final byte[] dstkey, final byte[]... keys) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, keys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sinterstore(dstkey, keys); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Long smove(final byte[] srckey, final byte[] dstkey, final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.smove(srckey, dstkey, member); + } + }.runBinary(2, srckey, dstkey); + } + + @Override + public Long sort(final byte[] key, final SortingParams sortingParameters, final byte[] dstkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sort(key, sortingParameters, dstkey); + } + }.runBinary(2, key, dstkey); + } + + @Override + public Long sort(final byte[] key, final byte[] dstkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sort(key, dstkey); + } + }.runBinary(2, key, dstkey); + } + + @Override + public Set sunion(final byte[]... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.sunion(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public Long sunionstore(final byte[] dstkey, final byte[]... keys) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, keys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sunionstore(dstkey, keys); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Long zinterstore(final byte[] dstkey, final byte[]... sets) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zinterstore(dstkey, sets); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Long zinterstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zinterstore(dstkey, params, sets); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Long zunionstore(final byte[] dstkey, final byte[]... sets) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zunionstore(dstkey, sets); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Long zunionstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { + byte[][] wholeKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zunionstore(dstkey, params, sets); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public byte[] brpoplpush(final byte[] source, final byte[] destination, final int timeout) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.brpoplpush(source, destination, timeout); + } + }.runBinary(2, source, destination); + } + + @Override + public Long publish(final byte[] channel, final byte[] message) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.publish(channel, message); + } + }.runWithAnyNode(); + } + + @Override + public void subscribe(final BinaryJedisPubSub jedisPubSub, final byte[]... channels) { + new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Integer execute(Jedis connection) { + connection.subscribe(jedisPubSub, channels); + return 0; + } + }.runWithAnyNode(); + } + + @Override + public void psubscribe(final BinaryJedisPubSub jedisPubSub, final byte[]... patterns) { + new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Integer execute(Jedis connection) { + connection.psubscribe(jedisPubSub, patterns); + return 0; + } + }.runWithAnyNode(); + } + + @Override + public Long bitop(final BitOP op, final byte[] destKey, final byte[]... srcKeys) { + byte[][] wholeKeys = KeyMergeUtil.merge(destKey, srcKeys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.bitop(op, destKey, srcKeys); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public String pfmerge(final byte[] destkey, final byte[]... sourcekeys) { + byte[][] wholeKeys = KeyMergeUtil.merge(destkey, sourcekeys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.pfmerge(destkey, sourcekeys); + } + }.runBinary(wholeKeys.length, wholeKeys); + } + + @Override + public Long pfcount(final byte[]... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pfcount(keys); + } + }.runBinary(keys.length, keys); + } + + @Override + public Long geoadd(final byte[] key, final double longitude, final double latitude, + final byte[] member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.geoadd(key, longitude, latitude, member); + } + }.runBinary(key); + } + + @Override + public Long geoadd(final byte[] key, final Map memberCoordinateMap) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.geoadd(key, memberCoordinateMap); + } + }.runBinary(key); + } + + @Override + public Double geodist(final byte[] key, final byte[] member1, final byte[] member2) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.geodist(key, member1, member2); + } + }.runBinary(key); + } + + @Override + public Double geodist(final byte[] key, final byte[] member1, final byte[] member2, + final GeoUnit unit) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.geodist(key, member1, member2, unit); + } + }.runBinary(key); + } + + @Override + public List geohash(final byte[] key, final byte[]... members) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.geohash(key, members); + } + }.runBinary(key); + } + + @Override + public List geopos(final byte[] key, final byte[]... members) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.geopos(key, members); + } + }.runBinary(key); + } + + @Override + public List georadius(final byte[] key, final double longitude, + final double latitude, final double radius, final GeoUnit unit) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadius(key, longitude, latitude, radius, unit); + } + }.runBinary(key); + } + + @Override + public List georadius(final byte[] key, final double longitude, + final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadius(key, longitude, latitude, radius, unit, param); + } + }.runBinary(key); + } + + @Override + public List georadiusByMember(final byte[] key, final byte[] member, + final double radius, final GeoUnit unit) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadiusByMember(key, member, radius, unit); + } + }.runBinary(key); + } + + @Override + public List georadiusByMember(final byte[] key, final byte[] member, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadiusByMember(key, member, radius, unit, param); + } + }.runBinary(key); + } + + @Override + public Set keys(final byte[] pattern) { + if (pattern == null || pattern.length == 0) { + throw new IllegalArgumentException(this.getClass().getSimpleName() + + " only supports KEYS commands with non-empty patterns"); + } + if (!JedisClusterHashTagUtil.isClusterCompliantMatchPattern(pattern)) { + throw new IllegalArgumentException(this.getClass().getSimpleName() + + " only supports KEYS commands with patterns containing hash-tags ( curly-brackets enclosed strings )"); + } + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.keys(pattern); + } + }.runBinary(pattern); + } + + @Override + public ScanResult scan(final byte[] cursor, final ScanParams params) { + + byte[] matchPattern = null; + + if (params == null || (matchPattern = params.binaryMatch()) == null || matchPattern.length == 0) { + throw new IllegalArgumentException(BinaryJedisCluster.class.getSimpleName() + + " only supports SCAN commands with non-empty MATCH patterns"); + } + + if (!JedisClusterHashTagUtil.isClusterCompliantMatchPattern(matchPattern)) { + throw new IllegalArgumentException(BinaryJedisCluster.class.getSimpleName() + + " only supports SCAN commands with MATCH patterns containing hash-tags ( curly-brackets enclosed strings )"); + } + + return new JedisClusterCommand< ScanResult>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.scan(cursor, params); + } + }.runBinary(matchPattern); + } + + @Override + public ScanResult> hscan(final byte[] key, final byte[] cursor) { + return new JedisClusterCommand>>(connectionHandler, + maxAttempts) { + @Override + public ScanResult> execute(Jedis connection) { + return connection.hscan(key, cursor); + } + }.runBinary(key); + } + + @Override + public ScanResult> hscan(final byte[] key, final byte[] cursor, + final ScanParams params) { + return new JedisClusterCommand>>(connectionHandler, + maxAttempts) { + @Override + public ScanResult> execute(Jedis connection) { + return connection.hscan(key, cursor, params); + } + }.runBinary(key); + } + + @Override + public ScanResult sscan(final byte[] key, final byte[] cursor) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.sscan(key, cursor); + } + }.runBinary(key); + } + + @Override + public ScanResult sscan(final byte[] key, final byte[] cursor, final ScanParams params) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.sscan(key, cursor, params); + } + }.runBinary(key); + } + + @Override + public ScanResult zscan(final byte[] key, final byte[] cursor) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.zscan(key, cursor); + } + }.runBinary(key); + } + + @Override + public ScanResult zscan(final byte[] key, final byte[] cursor, final ScanParams params) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.zscan(key, cursor, params); + } + }.runBinary(key); + } + + @Override + public List bitfield(final byte[] key, final byte[]... arguments) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.bitfield(key, arguments); + } + }.runBinary(key); + } + + @Override + public Long hstrlen(final byte[] key, final byte[] field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hstrlen(key, field); + } + }.runBinary(key); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisPubSub.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisPubSub.java new file mode 100755 index 000000000..ce007035f --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryJedisPubSub.java @@ -0,0 +1,127 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.MESSAGE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PMESSAGE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PSUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PUNSUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.SUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.UNSUBSCRIBE; + +import java.util.Arrays; +import java.util.List; + +import com.fr.third.redis.clients.jedis.exceptions.JedisException; + +public abstract class BinaryJedisPubSub { + private int subscribedChannels = 0; + private Client client; + + public void onMessage(byte[] channel, byte[] message) { + } + + public void onPMessage(byte[] pattern, byte[] channel, byte[] message) { + } + + public void onSubscribe(byte[] channel, int subscribedChannels) { + } + + public void onUnsubscribe(byte[] channel, int subscribedChannels) { + } + + public void onPUnsubscribe(byte[] pattern, int subscribedChannels) { + } + + public void onPSubscribe(byte[] pattern, int subscribedChannels) { + } + + public void unsubscribe() { + client.unsubscribe(); + client.flush(); + } + + public void unsubscribe(byte[]... channels) { + client.unsubscribe(channels); + client.flush(); + } + + public void subscribe(byte[]... channels) { + client.subscribe(channels); + client.flush(); + } + + public void psubscribe(byte[]... patterns) { + client.psubscribe(patterns); + client.flush(); + } + + public void punsubscribe() { + client.punsubscribe(); + client.flush(); + } + + public void punsubscribe(byte[]... patterns) { + client.punsubscribe(patterns); + client.flush(); + } + + public boolean isSubscribed() { + return subscribedChannels > 0; + } + + public void proceedWithPatterns(Client client, byte[]... patterns) { + this.client = client; + client.psubscribe(patterns); + client.flush(); + process(client); + } + + public void proceed(Client client, byte[]... channels) { + this.client = client; + client.subscribe(channels); + client.flush(); + process(client); + } + + private void process(Client client) { + do { + List reply = client.getRawObjectMultiBulkReply(); + final Object firstObj = reply.get(0); + if (!(firstObj instanceof byte[])) { + throw new JedisException("Unknown message type: " + firstObj); + } + final byte[] resp = (byte[]) firstObj; + if (Arrays.equals(SUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bchannel = (byte[]) reply.get(1); + onSubscribe(bchannel, subscribedChannels); + } else if (Arrays.equals(UNSUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bchannel = (byte[]) reply.get(1); + onUnsubscribe(bchannel, subscribedChannels); + } else if (Arrays.equals(MESSAGE.raw, resp)) { + final byte[] bchannel = (byte[]) reply.get(1); + final byte[] bmesg = (byte[]) reply.get(2); + onMessage(bchannel, bmesg); + } else if (Arrays.equals(PMESSAGE.raw, resp)) { + final byte[] bpattern = (byte[]) reply.get(1); + final byte[] bchannel = (byte[]) reply.get(2); + final byte[] bmesg = (byte[]) reply.get(3); + onPMessage(bpattern, bchannel, bmesg); + } else if (Arrays.equals(PSUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bpattern = (byte[]) reply.get(1); + onPSubscribe(bpattern, subscribedChannels); + } else if (Arrays.equals(PUNSUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bpattern = (byte[]) reply.get(1); + onPUnsubscribe(bpattern, subscribedChannels); + } else { + throw new JedisException("Unknown message type: " + firstObj); + } + } while (isSubscribed()); + } + + public int getSubscribedChannels() { + return subscribedChannels; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryShardedJedis.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryShardedJedis.java new file mode 100755 index 000000000..537db1ed8 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BinaryShardedJedis.java @@ -0,0 +1,925 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import com.fr.third.redis.clients.jedis.commands.BinaryJedisCommands; +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.util.Hashing; +import com.fr.third.redis.clients.jedis.util.Sharded; + +public class BinaryShardedJedis extends Sharded implements + BinaryJedisCommands { + public BinaryShardedJedis(List shards) { + super(shards); + } + + public BinaryShardedJedis(List shards, Hashing algo) { + super(shards, algo); + } + + public BinaryShardedJedis(List shards, Pattern keyTagPattern) { + super(shards, keyTagPattern); + } + + public BinaryShardedJedis(List shards, Hashing algo, Pattern keyTagPattern) { + super(shards, algo, keyTagPattern); + } + + public void disconnect() { + for (Jedis jedis : getAllShards()) { + if (jedis.isConnected()) { + try { + jedis.quit(); + } catch (JedisConnectionException e) { + // ignore the exception node, so that all other normal nodes can release all connections. + } + try { + jedis.disconnect(); + } catch (JedisConnectionException e) { + // ignore the exception node, so that all other normal nodes can release all connections. + } + } + } + } + + protected Jedis create(JedisShardInfo shard) { + return new Jedis(shard); + } + + @Override + public String set(final byte[] key, final byte[] value) { + Jedis j = getShard(key); + return j.set(key, value); + } + + @Override + public String set(final byte[] key, final byte[] value, SetParams params) { + Jedis j = getShard(key); + return j.set(key, value, params); + } + + @Override + public byte[] get(final byte[] key) { + Jedis j = getShard(key); + return j.get(key); + } + + @Override + public Boolean exists(final byte[] key) { + Jedis j = getShard(key); + return j.exists(key); + } + + @Override + public String type(final byte[] key) { + Jedis j = getShard(key); + return j.type(key); + } + + @Override + public byte[] dump(final byte[] key) { + Jedis j = getShard(key); + return j.dump(key); + } + + @Override + public String restore(final byte[] key, final int ttl, final byte[] serializedValue) { + Jedis j = getShard(key); + return j.restore(key, ttl, serializedValue); + } + + @Override + public Long expire(final byte[] key, final int seconds) { + Jedis j = getShard(key); + return j.expire(key, seconds); + } + + @Override + public Long pexpire(final byte[] key, final long milliseconds) { + Jedis j = getShard(key); + return j.pexpire(key, milliseconds); + } + + @Override + public Long expireAt(final byte[] key, final long unixTime) { + Jedis j = getShard(key); + return j.expireAt(key, unixTime); + } + + @Override + public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) { + Jedis j = getShard(key); + return j.pexpireAt(key, millisecondsTimestamp); + } + + @Override + public Long ttl(final byte[] key) { + Jedis j = getShard(key); + return j.ttl(key); + } + + @Override + public Long pttl(final byte[] key) { + Jedis j = getShard(key); + return j.pttl(key); + } + + @Override + public Long touch(final byte[] key) { + Jedis j = getShard(key); + return j.touch(key); + } + + @Override + public byte[] getSet(final byte[] key, final byte[] value) { + Jedis j = getShard(key); + return j.getSet(key, value); + } + + @Override + public Long setnx(final byte[] key, final byte[] value) { + Jedis j = getShard(key); + return j.setnx(key, value); + } + + @Override + public String setex(final byte[] key, final int seconds, final byte[] value) { + Jedis j = getShard(key); + return j.setex(key, seconds, value); + } + + @Override + public String psetex(final byte[] key, final long milliseconds, final byte[] value) { + Jedis j = getShard(key); + return j.psetex(key, milliseconds, value); + } + + @Override + public Long decrBy(final byte[] key, final long decrement) { + Jedis j = getShard(key); + return j.decrBy(key, decrement); + } + + @Override + public Long decr(final byte[] key) { + Jedis j = getShard(key); + return j.decr(key); + } + + @Override + public Long del(final byte[] key) { + Jedis j = getShard(key); + return j.del(key); + } + + @Override + public Long unlink(final byte[] key) { + Jedis j = getShard(key); + return j.unlink(key); + } + + @Override + public Long incrBy(final byte[] key, final long increment) { + Jedis j = getShard(key); + return j.incrBy(key, increment); + } + + @Override + public Double incrByFloat(final byte[] key, final double increment) { + Jedis j = getShard(key); + return j.incrByFloat(key, increment); + } + + @Override + public Long incr(final byte[] key) { + Jedis j = getShard(key); + return j.incr(key); + } + + @Override + public Long append(final byte[] key, final byte[] value) { + Jedis j = getShard(key); + return j.append(key, value); + } + + @Override + public byte[] substr(final byte[] key, final int start, final int end) { + Jedis j = getShard(key); + return j.substr(key, start, end); + } + + @Override + public Long hset(final byte[] key, final byte[] field, final byte[] value) { + Jedis j = getShard(key); + return j.hset(key, field, value); + } + + @Override + public Long hset(final byte[] key, final Map hash) { + Jedis j = getShard(key); + return j.hset(key, hash); + } + + @Override + public byte[] hget(final byte[] key, final byte[] field) { + Jedis j = getShard(key); + return j.hget(key, field); + } + + @Override + public Long hsetnx(final byte[] key, final byte[] field, final byte[] value) { + Jedis j = getShard(key); + return j.hsetnx(key, field, value); + } + + @Override + public String hmset(final byte[] key, final Map hash) { + Jedis j = getShard(key); + return j.hmset(key, hash); + } + + @Override + public List hmget(final byte[] key, final byte[]... fields) { + Jedis j = getShard(key); + return j.hmget(key, fields); + } + + @Override + public Long hincrBy(final byte[] key, final byte[] field, final long value) { + Jedis j = getShard(key); + return j.hincrBy(key, field, value); + } + + @Override + public Double hincrByFloat(final byte[] key, final byte[] field, final double value) { + Jedis j = getShard(key); + return j.hincrByFloat(key, field, value); + } + + @Override + public Boolean hexists(final byte[] key, final byte[] field) { + Jedis j = getShard(key); + return j.hexists(key, field); + } + + @Override + public Long hdel(final byte[] key, final byte[]... fields) { + Jedis j = getShard(key); + return j.hdel(key, fields); + } + + @Override + public Long hlen(final byte[] key) { + Jedis j = getShard(key); + return j.hlen(key); + } + + @Override + public Set hkeys(final byte[] key) { + Jedis j = getShard(key); + return j.hkeys(key); + } + + @Override + public Collection hvals(final byte[] key) { + Jedis j = getShard(key); + return j.hvals(key); + } + + @Override + public Map hgetAll(final byte[] key) { + Jedis j = getShard(key); + return j.hgetAll(key); + } + + @Override + public Long rpush(final byte[] key, final byte[]... strings) { + Jedis j = getShard(key); + return j.rpush(key, strings); + } + + @Override + public Long lpush(final byte[] key, final byte[]... strings) { + Jedis j = getShard(key); + return j.lpush(key, strings); + } + + @Override + public Long strlen(final byte[] key) { + Jedis j = getShard(key); + return j.strlen(key); + } + + @Override + public Long lpushx(final byte[] key, final byte[]... string) { + Jedis j = getShard(key); + return j.lpushx(key, string); + } + + @Override + public Long persist(final byte[] key) { + Jedis j = getShard(key); + return j.persist(key); + } + + @Override + public Long rpushx(final byte[] key, final byte[]... string) { + Jedis j = getShard(key); + return j.rpushx(key, string); + } + + @Override + public Long llen(final byte[] key) { + Jedis j = getShard(key); + return j.llen(key); + } + + @Override + public List lrange(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.lrange(key, start, stop); + } + + @Override + public String ltrim(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.ltrim(key, start, stop); + } + + @Override + public byte[] lindex(final byte[] key, final long index) { + Jedis j = getShard(key); + return j.lindex(key, index); + } + + @Override + public String lset(final byte[] key, final long index, final byte[] value) { + Jedis j = getShard(key); + return j.lset(key, index, value); + } + + @Override + public Long lrem(final byte[] key, final long count, final byte[] value) { + Jedis j = getShard(key); + return j.lrem(key, count, value); + } + + @Override + public byte[] lpop(final byte[] key) { + Jedis j = getShard(key); + return j.lpop(key); + } + + @Override + public byte[] rpop(final byte[] key) { + Jedis j = getShard(key); + return j.rpop(key); + } + + @Override + public Long sadd(final byte[] key, final byte[]... members) { + Jedis j = getShard(key); + return j.sadd(key, members); + } + + @Override + public Set smembers(final byte[] key) { + Jedis j = getShard(key); + return j.smembers(key); + } + + @Override + public Long srem(final byte[] key, final byte[]... members) { + Jedis j = getShard(key); + return j.srem(key, members); + } + + @Override + public byte[] spop(final byte[] key) { + Jedis j = getShard(key); + return j.spop(key); + } + + @Override + public Set spop(final byte[] key, final long count) { + Jedis j = getShard(key); + return j.spop(key, count); + } + + @Override + public Long scard(final byte[] key) { + Jedis j = getShard(key); + return j.scard(key); + } + + @Override + public Boolean sismember(final byte[] key, final byte[] member) { + Jedis j = getShard(key); + return j.sismember(key, member); + } + + @Override + public byte[] srandmember(final byte[] key) { + Jedis j = getShard(key); + return j.srandmember(key); + } + + @Override + public List srandmember(final byte[] key, final int count) { + Jedis j = getShard(key); + return j.srandmember(key, count); + } + + @Override + public Long zadd(final byte[] key, final double score, final byte[] member) { + Jedis j = getShard(key); + return j.zadd(key, score, member); + } + + @Override + public Long zadd(final byte[] key, final double score, final byte[] member, final ZAddParams params) { + Jedis j = getShard(key); + return j.zadd(key, score, member, params); + } + + @Override + public Long zadd(final byte[] key, final Map scoreMembers) { + Jedis j = getShard(key); + return j.zadd(key, scoreMembers); + } + + @Override + public Long zadd(final byte[] key, final Map scoreMembers, final ZAddParams params) { + Jedis j = getShard(key); + return j.zadd(key, scoreMembers, params); + } + + @Override + public Set zrange(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrange(key, start, stop); + } + + @Override + public Long zrem(final byte[] key, final byte[]... members) { + Jedis j = getShard(key); + return j.zrem(key, members); + } + + @Override + public Double zincrby(final byte[] key, final double increment, final byte[] member) { + Jedis j = getShard(key); + return j.zincrby(key, increment, member); + } + + @Override + public Double zincrby(final byte[] key, final double increment, final byte[] member, ZIncrByParams params) { + Jedis j = getShard(key); + return j.zincrby(key, increment, member, params); + } + + @Override + public Long zrank(final byte[] key, final byte[] member) { + Jedis j = getShard(key); + return j.zrank(key, member); + } + + @Override + public Long zrevrank(final byte[] key, final byte[] member) { + Jedis j = getShard(key); + return j.zrevrank(key, member); + } + + @Override + public Set zrevrange(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrevrange(key, start, stop); + } + + @Override + public Set zrangeWithScores(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrangeWithScores(key, start, stop); + } + + @Override + public Set zrevrangeWithScores(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrevrangeWithScores(key, start, stop); + } + + @Override + public Long zcard(final byte[] key) { + Jedis j = getShard(key); + return j.zcard(key); + } + + @Override + public Double zscore(final byte[] key, final byte[] member) { + Jedis j = getShard(key); + return j.zscore(key, member); + } + + @Override + public List sort(final byte[] key) { + Jedis j = getShard(key); + return j.sort(key); + } + + @Override + public List sort(final byte[] key, SortingParams sortingParameters) { + Jedis j = getShard(key); + return j.sort(key, sortingParameters); + } + + @Override + public Long zcount(final byte[] key, final double min, final double max) { + Jedis j = getShard(key); + return j.zcount(key, min, max); + } + + @Override + public Long zcount(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zcount(key, min, max); + } + + @Override + public Set zrangeByScore(final byte[] key, final double min, final double max) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max); + } + + @Override + public Set zrangeByScore(final byte[] key, final double min, final double max, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max, offset, count); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final double min, final double max) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final double min, final double max, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max, offset, count); + } + + @Override + public Set zrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max); + } + + @Override + public Set zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max, offset, count); + } + + @Override + public Set zrangeByScore(final byte[] key, final byte[] min, final byte[] max, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final double max, final double min) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final double max, final double min, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min, offset, count); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final double max, final double min) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final double max, final double min, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min); + } + + @Override + public Set zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min, offset, count); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min); + } + + @Override + public Set zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + + @Override + public Long zremrangeByRank(final byte[] key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zremrangeByRank(key, start, stop); + } + + @Override + public Long zremrangeByScore(final byte[] key, final double min, final double max) { + Jedis j = getShard(key); + return j.zremrangeByScore(key, min, max); + } + + @Override + public Long zremrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zremrangeByScore(key, min, max); + } + + @Override + public Long zlexcount(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zlexcount(key, min, max); + } + + @Override + public Set zrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zrangeByLex(key, min, max); + } + + @Override + public Set zrangeByLex(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + Jedis j = getShard(key); + return j.zrangeByLex(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min) { + Jedis j = getShard(key); + return j.zrevrangeByLex(key, max, min); + } + + @Override + public Set zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrevrangeByLex(key, max, min, offset, count); + } + + @Override + public Long zremrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + Jedis j = getShard(key); + return j.zremrangeByLex(key, min, max); + } + + @Override + public Long linsert(final byte[] key, final ListPosition where, final byte[] pivot, final byte[] value) { + Jedis j = getShard(key); + return j.linsert(key, where, pivot, value); + } + + public ShardedJedisPipeline pipelined() { + ShardedJedisPipeline pipeline = new ShardedJedisPipeline(); + pipeline.setShardedJedis(this); + return pipeline; + } + + public Long objectRefcount(final byte[] key) { + Jedis j = getShard(key); + return j.objectRefcount(key); + } + + public byte[] objectEncoding(final byte[] key) { + Jedis j = getShard(key); + return j.objectEncoding(key); + } + + public Long objectIdletime(final byte[] key) { + Jedis j = getShard(key); + return j.objectIdletime(key); + } + + @Override + public Boolean setbit(final byte[] key, final long offset, boolean value) { + Jedis j = getShard(key); + return j.setbit(key, offset, value); + } + + @Override + public Boolean setbit(final byte[] key, final long offset, final byte[] value) { + Jedis j = getShard(key); + return j.setbit(key, offset, value); + } + + @Override + public Boolean getbit(final byte[] key, final long offset) { + Jedis j = getShard(key); + return j.getbit(key, offset); + } + + @Override + public Long setrange(final byte[] key, final long offset, final byte[] value) { + Jedis j = getShard(key); + return j.setrange(key, offset, value); + } + + @Override + public byte[] getrange(final byte[] key, final long startOffset, final long endOffset) { + Jedis j = getShard(key); + return j.getrange(key, startOffset, endOffset); + } + + @Override + public Long move(final byte[] key, final int dbIndex) { + Jedis j = getShard(key); + return j.move(key, dbIndex); + } + + @Override + public byte[] echo(final byte[] arg) { + Jedis j = getShard(arg); + return j.echo(arg); + } + + public List brpop(final byte[] arg) { + Jedis j = getShard(arg); + return j.brpop(arg); + } + + public List blpop(final byte[] arg) { + Jedis j = getShard(arg); + return j.blpop(arg); + } + + @Override + public Long bitcount(final byte[] key) { + Jedis j = getShard(key); + return j.bitcount(key); + } + + @Override + public Long bitcount(final byte[] key, final long start, final long end) { + Jedis j = getShard(key); + return j.bitcount(key, start, end); + } + + @Override + public Long pfadd(final byte[] key, final byte[]... elements) { + Jedis j = getShard(key); + return j.pfadd(key, elements); + } + + @Override + public long pfcount(final byte[] key) { + Jedis j = getShard(key); + return j.pfcount(key); + } + + @Override + public Long geoadd(final byte[] key, final double longitude, final double latitude, final byte[] member) { + Jedis j = getShard(key); + return j.geoadd(key, longitude, latitude, member); + } + + @Override + public Long geoadd(final byte[] key, final Map memberCoordinateMap) { + Jedis j = getShard(key); + return j.geoadd(key, memberCoordinateMap); + } + + @Override + public Double geodist(final byte[] key, final byte[] member1, final byte[] member2) { + Jedis j = getShard(key); + return j.geodist(key, member1, member2); + } + + @Override + public Double geodist(final byte[] key, final byte[] member1, final byte[] member2, final GeoUnit unit) { + Jedis j = getShard(key); + return j.geodist(key, member1, member2, unit); + } + + @Override + public List geohash(final byte[] key, final byte[]... members) { + Jedis j = getShard(key); + return j.geohash(key, members); + } + + @Override + public List geopos(final byte[] key, final byte[]... members) { + Jedis j = getShard(key); + return j.geopos(key, members); + } + + @Override + public List georadius(final byte[] key, final double longitude, final double latitude, + final double radius, final GeoUnit unit) { + Jedis j = getShard(key); + return j.georadius(key, longitude, latitude, radius, unit); + } + + @Override + public List georadius(final byte[] key, final double longitude, final double latitude, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + Jedis j = getShard(key); + return j.georadius(key, longitude, latitude, radius, unit, param); + } + + @Override + public List georadiusByMember(final byte[] key, final byte[] member, final double radius, + final GeoUnit unit) { + Jedis j = getShard(key); + return j.georadiusByMember(key, member, radius, unit); + } + + @Override + public List georadiusByMember(final byte[] key, final byte[] member, final double radius, + final GeoUnit unit, final GeoRadiusParam param) { + Jedis j = getShard(key); + return j.georadiusByMember(key, member, radius, unit, param); + } + + @Override + public ScanResult> hscan(final byte[] key, final byte[] cursor) { + Jedis j = getShard(key); + return j.hscan(key, cursor); + } + + @Override + public ScanResult> hscan(final byte[] key, final byte[] cursor, final ScanParams params) { + Jedis j = getShard(key); + return j.hscan(key, cursor, params); + } + + @Override + public ScanResult sscan(final byte[] key, final byte[] cursor) { + Jedis j = getShard(key); + return j.sscan(key, cursor); + } + + @Override + public ScanResult sscan(final byte[] key, final byte[] cursor, final ScanParams params) { + Jedis j = getShard(key); + return j.sscan(key, cursor, params); + } + + @Override + public ScanResult zscan(final byte[] key, final byte[] cursor) { + Jedis j = getShard(key); + return j.zscan(key, cursor); + } + + @Override + public ScanResult zscan(final byte[] key, final byte[] cursor, final ScanParams params) { + Jedis j = getShard(key); + return j.zscan(key, cursor, params); + } + + @Override + public List bitfield(final byte[] key, final byte[]... arguments) { + Jedis j = getShard(key); + return j.bitfield(key, arguments); + } + + @Override + public Long hstrlen(final byte[] key, final byte[] field) { + Jedis j = getShard(key); + return j.hstrlen(key, field); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BitOP.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BitOP.java new file mode 100755 index 000000000..cbfe425c1 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BitOP.java @@ -0,0 +1,11 @@ +package com.fr.third.redis.clients.jedis; + +public enum BitOP { + AND, OR, XOR, NOT; + + public final byte[] raw; + + private BitOP() { + this.raw = com.fr.third.redis.clients.jedis.util.SafeEncoder.encode(name()); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BitPosParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BitPosParams.java new file mode 100755 index 000000000..3028289c6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BitPosParams.java @@ -0,0 +1,27 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class BitPosParams { + private List params = new ArrayList(); + + protected BitPosParams() { + } + + public BitPosParams(long start) { + params.add(Protocol.toByteArray(start)); + } + + public BitPosParams(long start, long end) { + this(start); + + params.add(Protocol.toByteArray(end)); + } + + public Collection getParams() { + return Collections.unmodifiableCollection(params); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Builder.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Builder.java new file mode 100755 index 000000000..c9c52558d --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Builder.java @@ -0,0 +1,5 @@ +package com.fr.third.redis.clients.jedis; + +public abstract class Builder { + public abstract T build(Object data); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/BuilderFactory.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/BuilderFactory.java new file mode 100755 index 000000000..747d94ac6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/BuilderFactory.java @@ -0,0 +1,482 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fr.third.redis.clients.jedis.util.JedisByteHashMap; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public final class BuilderFactory { + public static final Builder DOUBLE = new Builder() { + @Override + public Double build(Object data) { + String string = STRING.build(data); + if (string == null) return null; + try { + return Double.valueOf(string); + } catch (NumberFormatException e) { + if (string.equals("inf") || string.equals("+inf")) return Double.POSITIVE_INFINITY; + if (string.equals("-inf")) return Double.NEGATIVE_INFINITY; + throw e; + } + } + + @Override + public String toString() { + return "double"; + } + }; + public static final Builder BOOLEAN = new Builder() { + @Override + public Boolean build(Object data) { + return ((Long) data) == 1; + } + + @Override + public String toString() { + return "boolean"; + } + }; + public static final Builder BYTE_ARRAY = new Builder() { + @Override + public byte[] build(Object data) { + return ((byte[]) data); // deleted == 1 + } + + @Override + public String toString() { + return "byte[]"; + } + }; + + public static final Builder LONG = new Builder() { + @Override + public Long build(Object data) { + return (Long) data; + } + + @Override + public String toString() { + return "long"; + } + + }; + public static final Builder STRING = new Builder() { + @Override + public String build(Object data) { + return data == null ? null : SafeEncoder.encode((byte[]) data); + } + + @Override + public String toString() { + return "string"; + } + + }; + public static final Builder> STRING_LIST = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public List build(Object data) { + if (null == data) { + return null; + } + List l = (List) data; + final ArrayList result = new ArrayList(l.size()); + for (final byte[] barray : l) { + if (barray == null) { + result.add(null); + } else { + result.add(SafeEncoder.encode(barray)); + } + } + return result; + } + + @Override + public String toString() { + return "List"; + } + + }; + public static final Builder> STRING_MAP = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Map build(Object data) { + final List flatHash = (List) data; + final Map hash = new HashMap(flatHash.size()/2, 1); + final Iterator iterator = flatHash.iterator(); + while (iterator.hasNext()) { + hash.put(SafeEncoder.encode(iterator.next()), SafeEncoder.encode(iterator.next())); + } + + return hash; + } + + @Override + public String toString() { + return "Map"; + } + + }; + + public static final Builder> PUBSUB_NUMSUB_MAP = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Map build(Object data) { + final List flatHash = (List) data; + final Map hash = new HashMap(flatHash.size()/2, 1); + final Iterator iterator = flatHash.iterator(); + while (iterator.hasNext()) { + hash.put(SafeEncoder.encode((byte[]) iterator.next()), + String.valueOf((Long) iterator.next())); + } + + return hash; + } + + @Override + public String toString() { + return "PUBSUB_NUMSUB_MAP"; + } + + }; + + public static final Builder> STRING_SET = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Set build(Object data) { + if (null == data) { + return null; + } + List l = (List) data; + final Set result = new HashSet(l.size(), 1); + for (final byte[] barray : l) { + if (barray == null) { + result.add(null); + } else { + result.add(SafeEncoder.encode(barray)); + } + } + return result; + } + + @Override + public String toString() { + return "Set"; + } + + }; + + public static final Builder> BYTE_ARRAY_LIST = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public List build(Object data) { + if (null == data) { + return null; + } + List l = (List) data; + + return l; + } + + @Override + public String toString() { + return "List"; + } + }; + + public static final Builder> BYTE_ARRAY_ZSET = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Set build(Object data) { + if (null == data) { + return null; + } + List l = (List) data; + final Set result = new LinkedHashSet(l); + for (final byte[] barray : l) { + if (barray == null) { + result.add(null); + } else { + result.add(barray); + } + } + return result; + } + + @Override + public String toString() { + return "ZSet"; + } + }; + public static final Builder> BYTE_ARRAY_MAP = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Map build(Object data) { + final List flatHash = (List) data; + final Map hash = new JedisByteHashMap(); + final Iterator iterator = flatHash.iterator(); + while (iterator.hasNext()) { + hash.put(iterator.next(), iterator.next()); + } + + return hash; + } + + @Override + public String toString() { + return "Map"; + } + + }; + + public static final Builder> STRING_ZSET = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Set build(Object data) { + if (null == data) { + return null; + } + List l = (List) data; + final Set result = new LinkedHashSet(l.size(), 1); + for (final byte[] barray : l) { + if (barray == null) { + result.add(null); + } else { + result.add(SafeEncoder.encode(barray)); + } + } + return result; + } + + @Override + public String toString() { + return "ZSet"; + } + + }; + + public static final Builder> TUPLE_ZSET = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public Set build(Object data) { + if (null == data) { + return null; + } + List l = (List) data; + final Set result = new LinkedHashSet(l.size()/2, 1); + Iterator iterator = l.iterator(); + while (iterator.hasNext()) { + result.add(new Tuple(iterator.next(), DOUBLE.build(iterator.next()))); + } + return result; + } + + @Override + public String toString() { + return "ZSet"; + } + + }; + + public static final Builder EVAL_RESULT = new Builder() { + + @Override + public Object build(Object data) { + return evalResult(data); + } + + @Override + public String toString() { + return "Eval"; + } + + private Object evalResult(Object result) { + if (result instanceof byte[]) return SafeEncoder.encode((byte[]) result); + + if (result instanceof List) { + List list = (List) result; + List listResult = new ArrayList(list.size()); + for (Object bin : list) { + listResult.add(evalResult(bin)); + } + + return listResult; + } + + return result; + } + + }; + + public static final Builder EVAL_BINARY_RESULT = new Builder() { + + @Override + public Object build(Object data) { + return evalResult(data); + } + + @Override + public String toString() { + return "Eval"; + } + + private Object evalResult(Object result) { + if (result instanceof List) { + List list = (List) result; + List listResult = new ArrayList(list.size()); + for (Object bin : list) { + listResult.add(evalResult(bin)); + } + + return listResult; + } + + return result; + } + + }; + + public static final Builder> GEO_COORDINATE_LIST = new Builder>() { + @Override + public List build(Object data) { + if (null == data) { + return null; + } + return interpretGeoposResult((List) data); + } + + @Override + public String toString() { + return "List"; + } + + private List interpretGeoposResult(List responses) { + List responseCoordinate = new ArrayList(responses.size()); + for (Object response : responses) { + if (response == null) { + responseCoordinate.add(null); + } else { + List respList = (List) response; + GeoCoordinate coord = new GeoCoordinate(DOUBLE.build(respList.get(0)), + DOUBLE.build(respList.get(1))); + responseCoordinate.add(coord); + } + } + return responseCoordinate; + } + }; + + public static final Builder> GEORADIUS_WITH_PARAMS_RESULT = new Builder>() { + @Override + public List build(Object data) { + if (data == null) { + return null; + } + + List objectList = (List) data; + + List responses = new ArrayList(objectList.size()); + if (objectList.isEmpty()) { + return responses; + } + + if (objectList.get(0) instanceof List) { + // list of members with additional informations + GeoRadiusResponse resp; + for (Object obj : objectList) { + List informations = (List) obj; + + resp = new GeoRadiusResponse((byte[]) informations.get(0)); + + int size = informations.size(); + for (int idx = 1; idx < size; idx++) { + Object info = informations.get(idx); + if (info instanceof List) { + // coordinate + List coord = (List) info; + + resp.setCoordinate(new GeoCoordinate(DOUBLE.build(coord.get(0)), + DOUBLE.build(coord.get(1)))); + } else { + // distance + resp.setDistance(DOUBLE.build(info)); + } + } + + responses.add(resp); + } + } else { + // list of members + for (Object obj : objectList) { + responses.add(new GeoRadiusResponse((byte[]) obj)); + } + } + + return responses; + } + + @Override + public String toString() { + return "GeoRadiusWithParamsResult"; + } + }; + + + public static final Builder> MODULE_LIST = new Builder>() { + @Override + public List build(Object data) { + if (data == null) { + return null; + } + + List> objectList = (List>) data; + + List responses = new ArrayList(objectList.size()); + if (objectList.isEmpty()) { + return responses; + } + + for (List moduleResp: objectList) { + Module m = new Module(SafeEncoder.encode((byte[]) moduleResp.get(1)), ((Long) moduleResp.get(3)).intValue()); + responses.add(m); + } + + return responses; + } + + @Override + public String toString() { + return "List"; + } + }; + + public static final Builder> LONG_LIST = new Builder>() { + @Override + @SuppressWarnings("unchecked") + public List build(Object data) { + if (null == data) { + return null; + } + return (List) data; + } + + @Override + public String toString() { + return "List"; + } + + }; + + + private BuilderFactory() { + throw new InstantiationError( "Must not instantiate this class" ); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Client.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Client.java new file mode 100755 index 000000000..6a5307f45 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Client.java @@ -0,0 +1,1123 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.toByteArray; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.redis.clients.jedis.commands.Commands; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class Client extends BinaryClient implements Commands { + + public Client() { + super(); + } + + public Client(final String host) { + super(host); + } + + public Client(final String host, final int port) { + super(host, port); + } + + public Client(final String host, final int port, final boolean ssl) { + super(host, port, ssl); + } + + public Client(final String host, final int port, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + @Override + public void set(final String key, final String value) { + set(SafeEncoder.encode(key), SafeEncoder.encode(value)); + } + + @Override + public void set(final String key, final String value, final SetParams params) { + set(SafeEncoder.encode(key), SafeEncoder.encode(value), params); + } + + @Override + public void get(final String key) { + get(SafeEncoder.encode(key)); + } + + @Override + public void exists(final String... keys) { + exists(SafeEncoder.encodeMany(keys)); + } + + @Override + public void del(final String... keys) { + del(SafeEncoder.encodeMany(keys)); + } + + @Override + public void unlink(final String... keys) { + unlink(SafeEncoder.encodeMany(keys)); + } + + @Override + public void type(final String key) { + type(SafeEncoder.encode(key)); + } + + @Override + public void keys(final String pattern) { + keys(SafeEncoder.encode(pattern)); + } + + @Override + public void rename(final String oldkey, final String newkey) { + rename(SafeEncoder.encode(oldkey), SafeEncoder.encode(newkey)); + } + + @Override + public void renamenx(final String oldkey, final String newkey) { + renamenx(SafeEncoder.encode(oldkey), SafeEncoder.encode(newkey)); + } + + @Override + public void expire(final String key, final int seconds) { + expire(SafeEncoder.encode(key), seconds); + } + + @Override + public void expireAt(final String key, final long unixTime) { + expireAt(SafeEncoder.encode(key), unixTime); + } + + @Override + public void ttl(final String key) { + ttl(SafeEncoder.encode(key)); + } + + @Override + public void touch(final String... keys) { + touch(SafeEncoder.encodeMany(keys)); + } + + @Override + public void move(final String key, final int dbIndex) { + move(SafeEncoder.encode(key), dbIndex); + } + + @Override + public void getSet(final String key, final String value) { + getSet(SafeEncoder.encode(key), SafeEncoder.encode(value)); + } + + @Override + public void mget(final String... keys) { + mget(SafeEncoder.encodeMany(keys)); + } + + @Override + public void setnx(final String key, final String value) { + setnx(SafeEncoder.encode(key), SafeEncoder.encode(value)); + } + + @Override + public void setex(final String key, final int seconds, final String value) { + setex(SafeEncoder.encode(key), seconds, SafeEncoder.encode(value)); + } + + @Override + public void mset(final String... keysvalues) { + mset(SafeEncoder.encodeMany(keysvalues)); + } + + @Override + public void msetnx(final String... keysvalues) { + msetnx(SafeEncoder.encodeMany(keysvalues)); + } + + @Override + public void decrBy(final String key, final long decrement) { + decrBy(SafeEncoder.encode(key), decrement); + } + + @Override + public void decr(final String key) { + decr(SafeEncoder.encode(key)); + } + + @Override + public void incrBy(final String key, final long increment) { + incrBy(SafeEncoder.encode(key), increment); + } + + @Override + public void incr(final String key) { + incr(SafeEncoder.encode(key)); + } + + @Override + public void append(final String key, final String value) { + append(SafeEncoder.encode(key), SafeEncoder.encode(value)); + } + + @Override + public void substr(final String key, final int start, final int end) { + substr(SafeEncoder.encode(key), start, end); + } + + @Override + public void hset(final String key, final String field, final String value) { + hset(SafeEncoder.encode(key), SafeEncoder.encode(field), SafeEncoder.encode(value)); + } + + @Override + public void hset(final String key, final Map hash) { + final Map bhash = new HashMap(hash.size()); + for (final Entry entry : hash.entrySet()) { + bhash.put(SafeEncoder.encode(entry.getKey()), SafeEncoder.encode(entry.getValue())); + } + hset(SafeEncoder.encode(key), bhash); + } + + @Override + public void hget(final String key, final String field) { + hget(SafeEncoder.encode(key), SafeEncoder.encode(field)); + } + + @Override + public void hsetnx(final String key, final String field, final String value) { + hsetnx(SafeEncoder.encode(key), SafeEncoder.encode(field), SafeEncoder.encode(value)); + } + + @Override + public void hmset(final String key, final Map hash) { + final Map bhash = new HashMap(hash.size()); + for (final Entry entry : hash.entrySet()) { + bhash.put(SafeEncoder.encode(entry.getKey()), SafeEncoder.encode(entry.getValue())); + } + hmset(SafeEncoder.encode(key), bhash); + } + + @Override + public void hmget(final String key, final String... fields) { + hmget(SafeEncoder.encode(key), SafeEncoder.encodeMany(fields)); + } + + @Override + public void hincrBy(final String key, final String field, final long value) { + hincrBy(SafeEncoder.encode(key), SafeEncoder.encode(field), value); + } + + @Override + public void hexists(final String key, final String field) { + hexists(SafeEncoder.encode(key), SafeEncoder.encode(field)); + } + + @Override + public void hdel(final String key, final String... fields) { + hdel(SafeEncoder.encode(key), SafeEncoder.encodeMany(fields)); + } + + @Override + public void hlen(final String key) { + hlen(SafeEncoder.encode(key)); + } + + @Override + public void hkeys(final String key) { + hkeys(SafeEncoder.encode(key)); + } + + @Override + public void hvals(final String key) { + hvals(SafeEncoder.encode(key)); + } + + @Override + public void hgetAll(final String key) { + hgetAll(SafeEncoder.encode(key)); + } + + @Override + public void rpush(final String key, final String... string) { + rpush(SafeEncoder.encode(key), SafeEncoder.encodeMany(string)); + } + + @Override + public void lpush(final String key, final String... string) { + lpush(SafeEncoder.encode(key), SafeEncoder.encodeMany(string)); + } + + @Override + public void llen(final String key) { + llen(SafeEncoder.encode(key)); + } + + @Override + public void lrange(final String key, final long start, final long stop) { + lrange(SafeEncoder.encode(key), start, stop); + } + + @Override + public void ltrim(final String key, final long start, final long stop) { + ltrim(SafeEncoder.encode(key), start, stop); + } + + @Override + public void lindex(final String key, final long index) { + lindex(SafeEncoder.encode(key), index); + } + + @Override + public void lset(final String key, final long index, final String value) { + lset(SafeEncoder.encode(key), index, SafeEncoder.encode(value)); + } + + @Override + public void lrem(final String key, final long count, final String value) { + lrem(SafeEncoder.encode(key), count, SafeEncoder.encode(value)); + } + + @Override + public void lpop(final String key) { + lpop(SafeEncoder.encode(key)); + } + + @Override + public void rpop(final String key) { + rpop(SafeEncoder.encode(key)); + } + + @Override + public void rpoplpush(final String srckey, final String dstkey) { + rpoplpush(SafeEncoder.encode(srckey), SafeEncoder.encode(dstkey)); + } + + @Override + public void sadd(final String key, final String... members) { + sadd(SafeEncoder.encode(key), SafeEncoder.encodeMany(members)); + } + + @Override + public void smembers(final String key) { + smembers(SafeEncoder.encode(key)); + } + + @Override + public void srem(final String key, final String... members) { + srem(SafeEncoder.encode(key), SafeEncoder.encodeMany(members)); + } + + @Override + public void spop(final String key) { + spop(SafeEncoder.encode(key)); + } + + @Override + public void spop(final String key, final long count) { + spop(SafeEncoder.encode(key), count); + } + + @Override + public void smove(final String srckey, final String dstkey, final String member) { + smove(SafeEncoder.encode(srckey), SafeEncoder.encode(dstkey), SafeEncoder.encode(member)); + } + + @Override + public void scard(final String key) { + scard(SafeEncoder.encode(key)); + } + + @Override + public void sismember(final String key, final String member) { + sismember(SafeEncoder.encode(key), SafeEncoder.encode(member)); + } + + @Override + public void sinter(final String... keys) { + sinter(SafeEncoder.encodeMany(keys)); + } + + @Override + public void sinterstore(final String dstkey, final String... keys) { + sinterstore(SafeEncoder.encode(dstkey), SafeEncoder.encodeMany(keys)); + } + + @Override + public void sunion(final String... keys) { + sunion(SafeEncoder.encodeMany(keys)); + } + + @Override + public void sunionstore(final String dstkey, final String... keys) { + sunionstore(SafeEncoder.encode(dstkey), SafeEncoder.encodeMany(keys)); + } + + @Override + public void sdiff(final String... keys) { + sdiff(SafeEncoder.encodeMany(keys)); + } + + @Override + public void sdiffstore(final String dstkey, final String... keys) { + sdiffstore(SafeEncoder.encode(dstkey), SafeEncoder.encodeMany(keys)); + } + + @Override + public void srandmember(final String key) { + srandmember(SafeEncoder.encode(key)); + } + + @Override + public void zadd(final String key, final double score, final String member) { + zadd(SafeEncoder.encode(key), score, SafeEncoder.encode(member)); + } + + @Override + public void zadd(final String key, final double score, final String member, + final ZAddParams params) { + zadd(SafeEncoder.encode(key), score, SafeEncoder.encode(member), params); + } + + @Override + public void zadd(final String key, final Map scoreMembers) { + HashMap binaryScoreMembers = convertScoreMembersToBinary(scoreMembers); + zadd(SafeEncoder.encode(key), binaryScoreMembers); + } + + @Override + public void zadd(final String key, final Map scoreMembers, final ZAddParams params) { + HashMap binaryScoreMembers = convertScoreMembersToBinary(scoreMembers); + zadd(SafeEncoder.encode(key), binaryScoreMembers, params); + } + + @Override + public void zrange(final String key, final long start, final long stop) { + zrange(SafeEncoder.encode(key), start, stop); + } + + @Override + public void zrem(final String key, final String... members) { + zrem(SafeEncoder.encode(key), SafeEncoder.encodeMany(members)); + } + + @Override + public void zincrby(final String key, final double increment, final String member) { + zincrby(SafeEncoder.encode(key), increment, SafeEncoder.encode(member)); + } + + @Override + public void zincrby(final String key, final double increment, final String member, final ZIncrByParams params) { + zincrby(SafeEncoder.encode(key), increment, SafeEncoder.encode(member), params); + } + + @Override + public void zrank(final String key, final String member) { + zrank(SafeEncoder.encode(key), SafeEncoder.encode(member)); + } + + @Override + public void zrevrank(final String key, final String member) { + zrevrank(SafeEncoder.encode(key), SafeEncoder.encode(member)); + } + + @Override + public void zrevrange(final String key, final long start, final long stop) { + zrevrange(SafeEncoder.encode(key), start, stop); + } + + @Override + public void zrangeWithScores(final String key, final long start, final long stop) { + zrangeWithScores(SafeEncoder.encode(key), start, stop); + } + + @Override + public void zrevrangeWithScores(final String key, final long start, final long stop) { + zrevrangeWithScores(SafeEncoder.encode(key), start, stop); + } + + @Override + public void zcard(final String key) { + zcard(SafeEncoder.encode(key)); + } + + @Override + public void zscore(final String key, final String member) { + zscore(SafeEncoder.encode(key), SafeEncoder.encode(member)); + } + + @Override + public void watch(final String... keys) { + watch(SafeEncoder.encodeMany(keys)); + } + + @Override + public void sort(final String key) { + sort(SafeEncoder.encode(key)); + } + + @Override + public void sort(final String key, final SortingParams sortingParameters) { + sort(SafeEncoder.encode(key), sortingParameters); + } + + @Override + public void blpop(final String[] args) { + blpop(SafeEncoder.encodeMany(args)); + } + + public void blpop(final int timeout, final String... keys) { + final int size = keys.length + 1; + List args = new ArrayList(size); + for (String arg : keys) { + args.add(arg); + } + args.add(String.valueOf(timeout)); + blpop(args.toArray(new String[size])); + } + + @Override + public void sort(final String key, final SortingParams sortingParameters, final String dstkey) { + sort(SafeEncoder.encode(key), sortingParameters, SafeEncoder.encode(dstkey)); + } + + @Override + public void sort(final String key, final String dstkey) { + sort(SafeEncoder.encode(key), SafeEncoder.encode(dstkey)); + } + + @Override + public void brpop(final String[] args) { + brpop(SafeEncoder.encodeMany(args)); + } + + public void brpop(final int timeout, final String... keys) { + final int size = keys.length + 1; + List args = new ArrayList(size); + for (String arg : keys) { + args.add(arg); + } + args.add(String.valueOf(timeout)); + brpop(args.toArray(new String[size])); + } + + @Override + public void zcount(final String key, final double min, final double max) { + zcount(SafeEncoder.encode(key), toByteArray(min), toByteArray(max)); + } + + @Override + public void zcount(final String key, final String min, final String max) { + zcount(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max)); + } + + @Override + public void zrangeByScore(final String key, final double min, final double max) { + zrangeByScore(SafeEncoder.encode(key), toByteArray(min), toByteArray(max)); + } + + @Override + public void zrangeByScore(final String key, final String min, final String max) { + zrangeByScore(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max)); + } + + @Override + public void zrangeByScore(final String key, final double min, final double max, final int offset, + final int count) { + zrangeByScore(SafeEncoder.encode(key), toByteArray(min), toByteArray(max), offset, count); + } + + @Override + public void zrangeByScoreWithScores(final String key, final double min, final double max) { + zrangeByScoreWithScores(SafeEncoder.encode(key), toByteArray(min), toByteArray(max)); + } + + @Override + public void zrangeByScoreWithScores(final String key, final double min, final double max, + final int offset, final int count) { + zrangeByScoreWithScores(SafeEncoder.encode(key), toByteArray(min), toByteArray(max), offset, + count); + } + + @Override + public void zrevrangeByScore(final String key, final double max, final double min) { + zrevrangeByScore(SafeEncoder.encode(key), toByteArray(max), toByteArray(min)); + } + + @Override + public void zrangeByScore(final String key, final String min, final String max, final int offset, + final int count) { + zrangeByScore(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max), + offset, count); + } + + @Override + public void zrangeByScoreWithScores(final String key, final String min, final String max) { + zrangeByScoreWithScores(SafeEncoder.encode(key), SafeEncoder.encode(min), + SafeEncoder.encode(max)); + } + + @Override + public void zrangeByScoreWithScores(final String key, final String min, final String max, + final int offset, final int count) { + zrangeByScoreWithScores(SafeEncoder.encode(key), SafeEncoder.encode(min), + SafeEncoder.encode(max), offset, count); + } + + @Override + public void zrevrangeByScore(final String key, final String max, final String min) { + zrevrangeByScore(SafeEncoder.encode(key), SafeEncoder.encode(max), SafeEncoder.encode(min)); + } + + @Override + public void zrevrangeByScore(final String key, final double max, final double min, + final int offset, final int count) { + zrevrangeByScore(SafeEncoder.encode(key), toByteArray(max), toByteArray(min), offset, count); + } + + @Override + public void zrevrangeByScore(final String key, final String max, final String min, + final int offset, final int count) { + zrevrangeByScore(SafeEncoder.encode(key), SafeEncoder.encode(max), SafeEncoder.encode(min), + offset, count); + } + + @Override + public void zrevrangeByScoreWithScores(final String key, final double max, final double min) { + zrevrangeByScoreWithScores(SafeEncoder.encode(key), toByteArray(max), toByteArray(min)); + } + + @Override + public void zrevrangeByScoreWithScores(final String key, final String max, final String min) { + zrevrangeByScoreWithScores(SafeEncoder.encode(key), SafeEncoder.encode(max), + SafeEncoder.encode(min)); + } + + @Override + public void zrevrangeByScoreWithScores(final String key, final double max, final double min, + final int offset, final int count) { + zrevrangeByScoreWithScores(SafeEncoder.encode(key), toByteArray(max), toByteArray(min), offset, + count); + } + + @Override + public void zrevrangeByScoreWithScores(final String key, final String max, final String min, + final int offset, final int count) { + zrevrangeByScoreWithScores(SafeEncoder.encode(key), SafeEncoder.encode(max), + SafeEncoder.encode(min), offset, count); + } + + @Override + public void zremrangeByRank(final String key, final long start, final long stop) { + zremrangeByRank(SafeEncoder.encode(key), start, stop); + } + + @Override + public void zremrangeByScore(final String key, final double min, final double max) { + zremrangeByScore(SafeEncoder.encode(key), toByteArray(min), toByteArray(max)); + } + + @Override + public void zremrangeByScore(final String key, final String min, final String max) { + zremrangeByScore(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max)); + } + + @Override + public void zunionstore(final String dstkey, final String... sets) { + zunionstore(SafeEncoder.encode(dstkey), SafeEncoder.encodeMany(sets)); + } + + @Override + public void zunionstore(final String dstkey, final ZParams params, final String... sets) { + zunionstore(SafeEncoder.encode(dstkey), params, SafeEncoder.encodeMany(sets)); + } + + @Override + public void zinterstore(final String dstkey, final String... sets) { + zinterstore(SafeEncoder.encode(dstkey), SafeEncoder.encodeMany(sets)); + } + + @Override + public void zinterstore(final String dstkey, final ZParams params, final String... sets) { + zinterstore(SafeEncoder.encode(dstkey), params, SafeEncoder.encodeMany(sets)); + } + + public void zlexcount(final String key, final String min, final String max) { + zlexcount(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max)); + } + + public void zrangeByLex(final String key, final String min, final String max) { + zrangeByLex(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max)); + } + + public void zrangeByLex(final String key, final String min, final String max, final int offset, + final int count) { + zrangeByLex(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max), offset, + count); + } + + public void zrevrangeByLex(final String key, final String max, final String min) { + zrevrangeByLex(SafeEncoder.encode(key), SafeEncoder.encode(max), SafeEncoder.encode(min)); + } + + public void zrevrangeByLex(final String key, final String max, final String min, final int offset, final int count) { + zrevrangeByLex(SafeEncoder.encode(key), SafeEncoder.encode(max), SafeEncoder.encode(min), + offset, count); + } + + public void zremrangeByLex(final String key, final String min, final String max) { + zremrangeByLex(SafeEncoder.encode(key), SafeEncoder.encode(min), SafeEncoder.encode(max)); + } + + @Override + public void strlen(final String key) { + strlen(SafeEncoder.encode(key)); + } + + @Override + public void lpushx(final String key, final String... string) { + lpushx(SafeEncoder.encode(key), SafeEncoder.encodeMany(string)); + } + + @Override + public void persist(final String key) { + persist(SafeEncoder.encode(key)); + } + + @Override + public void rpushx(final String key, final String... string) { + rpushx(SafeEncoder.encode(key), SafeEncoder.encodeMany(string)); + } + + @Override + public void echo(final String string) { + echo(SafeEncoder.encode(string)); + } + + @Override + public void linsert(final String key, final ListPosition where, final String pivot, + final String value) { + linsert(SafeEncoder.encode(key), where, SafeEncoder.encode(pivot), SafeEncoder.encode(value)); + } + + @Override + public void brpoplpush(final String source, final String destination, final int timeout) { + brpoplpush(SafeEncoder.encode(source), SafeEncoder.encode(destination), timeout); + } + + @Override + public void setbit(final String key, final long offset, final boolean value) { + setbit(SafeEncoder.encode(key), offset, value); + } + + @Override + public void setbit(final String key, final long offset, final String value) { + setbit(SafeEncoder.encode(key), offset, SafeEncoder.encode(value)); + } + + @Override + public void getbit(final String key, final long offset) { + getbit(SafeEncoder.encode(key), offset); + } + + public void bitpos(final String key, final boolean value, final BitPosParams params) { + bitpos(SafeEncoder.encode(key), value, params); + } + + @Override + public void setrange(final String key, final long offset, final String value) { + setrange(SafeEncoder.encode(key), offset, SafeEncoder.encode(value)); + } + + @Override + public void getrange(final String key, final long startOffset, final long endOffset) { + getrange(SafeEncoder.encode(key), startOffset, endOffset); + } + + public void publish(final String channel, final String message) { + publish(SafeEncoder.encode(channel), SafeEncoder.encode(message)); + } + + public void unsubscribe(final String... channels) { + unsubscribe(SafeEncoder.encodeMany(channels)); + } + + public void psubscribe(final String... patterns) { + psubscribe(SafeEncoder.encodeMany(patterns)); + } + + public void punsubscribe(final String... patterns) { + punsubscribe(SafeEncoder.encodeMany(patterns)); + } + + public void subscribe(final String... channels) { + subscribe(SafeEncoder.encodeMany(channels)); + } + + public void pubsubChannels(final String pattern) { + pubsub(Protocol.PUBSUB_CHANNELS, pattern); + } + + public void pubsubNumPat() { + pubsub(Protocol.PUBSUB_NUM_PAT); + } + + public void pubsubNumSub(final String... channels) { + pubsub(Protocol.PUBSUB_NUMSUB, channels); + } + + @Override + public void configSet(final String parameter, final String value) { + configSet(SafeEncoder.encode(parameter), SafeEncoder.encode(value)); + } + + @Override + public void configGet(final String pattern) { + configGet(SafeEncoder.encode(pattern)); + } + + public void eval(final String script, final int keyCount, final String... params) { + eval(SafeEncoder.encode(script), toByteArray(keyCount), SafeEncoder.encodeMany(params)); + } + + public void evalsha(final String sha1, final int keyCount, final String... params) { + evalsha(SafeEncoder.encode(sha1), toByteArray(keyCount), SafeEncoder.encodeMany(params)); + } + + public void scriptExists(final String... sha1) { + scriptExists(SafeEncoder.encodeMany(sha1)); + } + + public void scriptLoad(final String script) { + scriptLoad(SafeEncoder.encode(script)); + } + + @Override + public void objectRefcount(final String key) { + objectRefcount(SafeEncoder.encode(key)); + } + + @Override + public void objectIdletime(final String key) { + objectIdletime(SafeEncoder.encode(key)); + } + + @Override + public void objectEncoding(final String key) { + objectEncoding(SafeEncoder.encode(key)); + } + + @Override + public void bitcount(final String key) { + bitcount(SafeEncoder.encode(key)); + } + + @Override + public void bitcount(final String key, final long start, final long end) { + bitcount(SafeEncoder.encode(key), start, end); + } + + @Override + public void bitop(final BitOP op, final String destKey, final String... srcKeys) { + bitop(op, SafeEncoder.encode(destKey), SafeEncoder.encodeMany(srcKeys)); + } + + public void sentinel(final String... args) { + sentinel(SafeEncoder.encodeMany(args)); + } + + public void dump(final String key) { + dump(SafeEncoder.encode(key)); + } + + public void restore(final String key, final int ttl, final byte[] serializedValue) { + restore(SafeEncoder.encode(key), ttl, serializedValue); + } + + public void pexpire(final String key, final long milliseconds) { + pexpire(SafeEncoder.encode(key), milliseconds); + } + + public void pexpireAt(final String key, final long millisecondsTimestamp) { + pexpireAt(SafeEncoder.encode(key), millisecondsTimestamp); + } + + @Override + public void pttl(final String key) { + pttl(SafeEncoder.encode(key)); + } + + @Override + public void incrByFloat(final String key, final double increment) { + incrByFloat(SafeEncoder.encode(key), increment); + } + + public void psetex(final String key, final long milliseconds, final String value) { + psetex(SafeEncoder.encode(key), milliseconds, SafeEncoder.encode(value)); + } + + public void srandmember(final String key, final int count) { + srandmember(SafeEncoder.encode(key), count); + } + + public void clientKill(final String client) { + clientKill(SafeEncoder.encode(client)); + } + + public void clientSetname(final String name) { + clientSetname(SafeEncoder.encode(name)); + } + + public void migrate(final String host, final int port, final String key, final int destinationDb, + final int timeout) { + migrate(SafeEncoder.encode(host), port, SafeEncoder.encode(key), destinationDb, timeout); + } + + @Override + public void hincrByFloat(final String key, final String field, final double increment) { + hincrByFloat(SafeEncoder.encode(key), SafeEncoder.encode(field), increment); + } + + @Override + public void scan(final String cursor, final ScanParams params) { + scan(SafeEncoder.encode(cursor), params); + } + + @Override + public void hscan(final String key, final String cursor, final ScanParams params) { + hscan(SafeEncoder.encode(key), SafeEncoder.encode(cursor), params); + } + + @Override + public void sscan(final String key, final String cursor, final ScanParams params) { + sscan(SafeEncoder.encode(key), SafeEncoder.encode(cursor), params); + } + + @Override + public void zscan(final String key, final String cursor, final ScanParams params) { + zscan(SafeEncoder.encode(key), SafeEncoder.encode(cursor), params); + } + + public void cluster(final String subcommand, final int... args) { + final byte[][] arg = new byte[args.length + 1][]; + for (int i = 1; i < arg.length; i++) { + arg[i] = toByteArray(args[i - 1]); + } + arg[0] = SafeEncoder.encode(subcommand); + cluster(arg); + } + + public void pubsub(final String subcommand, final String... args) { + final byte[][] arg = new byte[args.length + 1][]; + for (int i = 1; i < arg.length; i++) { + arg[i] = SafeEncoder.encode(args[i - 1]); + } + arg[0] = SafeEncoder.encode(subcommand); + pubsub(arg); + } + + public void cluster(final String subcommand, final String... args) { + final byte[][] arg = new byte[args.length + 1][]; + for (int i = 1; i < arg.length; i++) { + arg[i] = SafeEncoder.encode(args[i - 1]); + } + arg[0] = SafeEncoder.encode(subcommand); + cluster(arg); + } + + public void cluster(final String subcommand) { + final byte[][] arg = new byte[1][]; + arg[0] = SafeEncoder.encode(subcommand); + cluster(arg); + } + + public void clusterNodes() { + cluster(Protocol.CLUSTER_NODES); + } + + public void clusterMeet(final String ip, final int port) { + cluster(Protocol.CLUSTER_MEET, ip, String.valueOf(port)); + } + + public void clusterReset(final ClusterReset resetType) { + cluster(Protocol.CLUSTER_RESET, resetType.name()); + } + + public void clusterAddSlots(final int... slots) { + cluster(Protocol.CLUSTER_ADDSLOTS, slots); + } + + public void clusterDelSlots(final int... slots) { + cluster(Protocol.CLUSTER_DELSLOTS, slots); + } + + public void clusterInfo() { + cluster(Protocol.CLUSTER_INFO); + } + + public void clusterGetKeysInSlot(final int slot, final int count) { + final int[] args = new int[] { slot, count }; + cluster(Protocol.CLUSTER_GETKEYSINSLOT, args); + } + + public void clusterSetSlotNode(final int slot, final String nodeId) { + cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot), Protocol.CLUSTER_SETSLOT_NODE, nodeId); + } + + public void clusterSetSlotMigrating(final int slot, final String nodeId) { + cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot), Protocol.CLUSTER_SETSLOT_MIGRATING, + nodeId); + } + + public void clusterSetSlotImporting(final int slot, final String nodeId) { + cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot), Protocol.CLUSTER_SETSLOT_IMPORTING, + nodeId); + } + + public void pfadd(final String key, final String... elements) { + pfadd(SafeEncoder.encode(key), SafeEncoder.encodeMany(elements)); + } + + public void pfcount(final String key) { + pfcount(SafeEncoder.encode(key)); + } + + public void pfcount(final String... keys) { + pfcount(SafeEncoder.encodeMany(keys)); + } + + public void pfmerge(final String destkey, final String... sourcekeys) { + pfmerge(SafeEncoder.encode(destkey), SafeEncoder.encodeMany(sourcekeys)); + } + + public void clusterSetSlotStable(final int slot) { + cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot), Protocol.CLUSTER_SETSLOT_STABLE); + } + + public void clusterForget(final String nodeId) { + cluster(Protocol.CLUSTER_FORGET, nodeId); + } + + public void clusterFlushSlots() { + cluster(Protocol.CLUSTER_FLUSHSLOT); + } + + public void clusterKeySlot(final String key) { + cluster(Protocol.CLUSTER_KEYSLOT, key); + } + + public void clusterCountKeysInSlot(final int slot) { + cluster(Protocol.CLUSTER_COUNTKEYINSLOT, String.valueOf(slot)); + } + + public void clusterSaveConfig() { + cluster(Protocol.CLUSTER_SAVECONFIG); + } + + public void clusterReplicate(final String nodeId) { + cluster(Protocol.CLUSTER_REPLICATE, nodeId); + } + + public void clusterSlaves(final String nodeId) { + cluster(Protocol.CLUSTER_SLAVES, nodeId); + } + + public void clusterFailover() { + cluster(Protocol.CLUSTER_FAILOVER); + } + + public void clusterSlots() { + cluster(Protocol.CLUSTER_SLOTS); + } + + public void geoadd(final String key, final double longitude, final double latitude, final String member) { + geoadd(SafeEncoder.encode(key), longitude, latitude, SafeEncoder.encode(member)); + } + + public void geoadd(final String key, final Map memberCoordinateMap) { + geoadd(SafeEncoder.encode(key), convertMemberCoordinateMapToBinary(memberCoordinateMap)); + } + + public void geodist(final String key, final String member1, final String member2) { + geodist(SafeEncoder.encode(key), SafeEncoder.encode(member1), SafeEncoder.encode(member2)); + } + + public void geodist(final String key, final String member1, final String member2, final GeoUnit unit) { + geodist(SafeEncoder.encode(key), SafeEncoder.encode(member1), SafeEncoder.encode(member2), unit); + } + + public void geohash(final String key, final String... members) { + geohash(SafeEncoder.encode(key), SafeEncoder.encodeMany(members)); + } + + public void geopos(final String key, final String[] members) { + geopos(SafeEncoder.encode(key), SafeEncoder.encodeMany(members)); + } + + public void georadius(final String key, final double longitude, final double latitude, final double radius, final GeoUnit unit) { + georadius(SafeEncoder.encode(key), longitude, latitude, radius, unit); + } + + public void georadius(final String key, final double longitude, final double latitude, final double radius, final GeoUnit unit, + final GeoRadiusParam param) { + georadius(SafeEncoder.encode(key), longitude, latitude, radius, unit, param); + } + + public void georadiusByMember(final String key, final String member, final double radius, final GeoUnit unit) { + georadiusByMember(SafeEncoder.encode(key), SafeEncoder.encode(member), radius, unit); + } + + public void georadiusByMember(final String key, final String member, final double radius, final GeoUnit unit, + final GeoRadiusParam param) { + georadiusByMember(SafeEncoder.encode(key), SafeEncoder.encode(member), radius, unit, param); + } + + public void moduleLoad(final String path) { + moduleLoad(SafeEncoder.encode(path)); + } + + public void moduleUnload(final String name) { + moduleUnload(SafeEncoder.encode(name)); + } + + private HashMap convertScoreMembersToBinary(final Map scoreMembers) { + HashMap binaryScoreMembers = new HashMap(); + for (Entry entry : scoreMembers.entrySet()) { + binaryScoreMembers.put(SafeEncoder.encode(entry.getKey()), entry.getValue()); + } + return binaryScoreMembers; + } + + private HashMap convertMemberCoordinateMapToBinary( + final Map memberCoordinateMap) { + HashMap binaryMemberCoordinateMap = new HashMap(); + for (Entry entry : memberCoordinateMap.entrySet()) { + binaryMemberCoordinateMap.put(SafeEncoder.encode(entry.getKey()), entry.getValue()); + } + return binaryMemberCoordinateMap; + } + + @Override + public void bitfield(final String key, final String... arguments) { + bitfield(SafeEncoder.encode(key), SafeEncoder.encodeMany(arguments)); + } + + @Override + public void hstrlen(final String key, final String field) { + hstrlen(SafeEncoder.encode(key), SafeEncoder.encode(field)); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ClusterReset.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ClusterReset.java new file mode 100755 index 000000000..3f76a7806 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ClusterReset.java @@ -0,0 +1,5 @@ +package com.fr.third.redis.clients.jedis; + +public enum ClusterReset { + SOFT, HARD +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Connection.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Connection.java new file mode 100755 index 000000000..7fb5f9166 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Connection.java @@ -0,0 +1,328 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.redis.clients.jedis.commands.ProtocolCommand; +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; +import com.fr.third.redis.clients.jedis.util.IOUtils; +import com.fr.third.redis.clients.jedis.util.RedisInputStream; +import com.fr.third.redis.clients.jedis.util.RedisOutputStream; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class Connection implements Closeable { + + private static final byte[][] EMPTY_ARGS = new byte[0][]; + + private String host = Protocol.DEFAULT_HOST; + private int port = Protocol.DEFAULT_PORT; + private Socket socket; + private RedisOutputStream outputStream; + private RedisInputStream inputStream; + private int connectionTimeout = Protocol.DEFAULT_TIMEOUT; + private int soTimeout = Protocol.DEFAULT_TIMEOUT; + private boolean broken = false; + private boolean ssl; + private SSLSocketFactory sslSocketFactory; + private SSLParameters sslParameters; + private HostnameVerifier hostnameVerifier; + + public Connection() { + } + + public Connection(final String host) { + this.host = host; + } + + public Connection(final String host, final int port) { + this.host = host; + this.port = port; + } + + public Connection(final String host, final int port, final boolean ssl) { + this.host = host; + this.port = port; + this.ssl = ssl; + } + + public Connection(final String host, final int port, final boolean ssl, + SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier) { + this.host = host; + this.port = port; + this.ssl = ssl; + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + } + + public Socket getSocket() { + return socket; + } + + public int getConnectionTimeout() { + return connectionTimeout; + } + + public int getSoTimeout() { + return soTimeout; + } + + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public void setSoTimeout(int soTimeout) { + this.soTimeout = soTimeout; + } + + public void setTimeoutInfinite() { + try { + if (!isConnected()) { + connect(); + } + socket.setSoTimeout(0); + } catch (SocketException ex) { + broken = true; + throw new JedisConnectionException(ex); + } + } + + public void rollbackTimeout() { + try { + socket.setSoTimeout(soTimeout); + } catch (SocketException ex) { + broken = true; + throw new JedisConnectionException(ex); + } + } + + public void sendCommand(final ProtocolCommand cmd, final String... args) { + final byte[][] bargs = new byte[args.length][]; + for (int i = 0; i < args.length; i++) { + bargs[i] = SafeEncoder.encode(args[i]); + } + sendCommand(cmd, bargs); + } + + public void sendCommand(final ProtocolCommand cmd) { + sendCommand(cmd, EMPTY_ARGS); + } + + public void sendCommand(final ProtocolCommand cmd, final byte[]... args) { + try { + connect(); + Protocol.sendCommand(outputStream, cmd, args); + } catch (JedisConnectionException ex) { + /* + * When client send request which formed by invalid protocol, Redis send back error message + * before close connection. We try to read it to provide reason of failure. + */ + try { + String errorMessage = Protocol.readErrorLineIfPossible(inputStream); + if (errorMessage != null && errorMessage.length() > 0) { + ex = new JedisConnectionException(errorMessage, ex.getCause()); + } + } catch (Exception e) { + /* + * Catch any IOException or JedisConnectionException occurred from InputStream#read and just + * ignore. This approach is safe because reading error message is optional and connection + * will eventually be closed. + */ + } + // Any other exceptions related to connection? + broken = true; + throw ex; + } + } + + public String getHost() { + return host; + } + + public void setHost(final String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(final int port) { + this.port = port; + } + + public void connect() { + if (!isConnected()) { + try { + socket = new Socket(); + // ->@wjw_add + socket.setReuseAddress(true); + socket.setKeepAlive(true); // Will monitor the TCP connection is + // valid + socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to + // ensure timely delivery of data + socket.setSoLinger(true, 0); // Control calls close () method, + // the underlying socket is closed + // immediately + // <-@wjw_add + + socket.connect(new InetSocketAddress(host, port), connectionTimeout); + socket.setSoTimeout(soTimeout); + + if (ssl) { + if (null == sslSocketFactory) { + sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); + } + socket = sslSocketFactory.createSocket(socket, host, port, true); + if (null != sslParameters) { + ((SSLSocket) socket).setSSLParameters(sslParameters); + } + if ((null != hostnameVerifier) && + (!hostnameVerifier.verify(host, ((SSLSocket) socket).getSession()))) { + String message = String.format( + "The connection to '%s' failed ssl/tls hostname verification.", host); + throw new JedisConnectionException(message); + } + } + + outputStream = new RedisOutputStream(socket.getOutputStream()); + inputStream = new RedisInputStream(socket.getInputStream()); + } catch (IOException ex) { + broken = true; + throw new JedisConnectionException("Failed connecting to host " + + host + ":" + port, ex); + } + } + } + + @Override + public void close() { + disconnect(); + } + + public void disconnect() { + if (isConnected()) { + try { + outputStream.flush(); + socket.close(); + } catch (IOException ex) { + broken = true; + throw new JedisConnectionException(ex); + } finally { + IOUtils.closeQuietly(socket); + } + } + } + + public boolean isConnected() { + return socket != null && socket.isBound() && !socket.isClosed() && socket.isConnected() + && !socket.isInputShutdown() && !socket.isOutputShutdown(); + } + + public String getStatusCodeReply() { + flush(); + final byte[] resp = (byte[]) readProtocolWithCheckingBroken(); + if (null == resp) { + return null; + } else { + return SafeEncoder.encode(resp); + } + } + + public String getBulkReply() { + final byte[] result = getBinaryBulkReply(); + if (null != result) { + return SafeEncoder.encode(result); + } else { + return null; + } + } + + public byte[] getBinaryBulkReply() { + flush(); + return (byte[]) readProtocolWithCheckingBroken(); + } + + public Long getIntegerReply() { + flush(); + return (Long) readProtocolWithCheckingBroken(); + } + + public List getMultiBulkReply() { + return BuilderFactory.STRING_LIST.build(getBinaryMultiBulkReply()); + } + + @SuppressWarnings("unchecked") + public List getBinaryMultiBulkReply() { + flush(); + return (List) readProtocolWithCheckingBroken(); + } + + @SuppressWarnings("unchecked") + public List getRawObjectMultiBulkReply() { + flush(); + return (List) readProtocolWithCheckingBroken(); + } + + public List getObjectMultiBulkReply() { + return getRawObjectMultiBulkReply(); + } + + @SuppressWarnings("unchecked") + public List getIntegerMultiBulkReply() { + flush(); + return (List) readProtocolWithCheckingBroken(); + } + + public Object getOne() { + flush(); + return readProtocolWithCheckingBroken(); + } + + public boolean isBroken() { + return broken; + } + + protected void flush() { + try { + outputStream.flush(); + } catch (IOException ex) { + broken = true; + throw new JedisConnectionException(ex); + } + } + + protected Object readProtocolWithCheckingBroken() { + try { + return Protocol.read(inputStream); + } catch (JedisConnectionException exc) { + broken = true; + throw exc; + } + } + + public List getMany(final int count) { + flush(); + final List responses = new ArrayList(count); + for (int i = 0; i < count; i++) { + try { + responses.add(readProtocolWithCheckingBroken()); + } catch (JedisDataException e) { + responses.add(e); + } + } + return responses; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/DebugParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/DebugParams.java new file mode 100755 index 000000000..89b3c99d7 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/DebugParams.java @@ -0,0 +1,31 @@ +package com.fr.third.redis.clients.jedis; + +public class DebugParams { + private String[] command; + + private DebugParams() { + + } + + public String[] getCommand() { + return command; + } + + public static DebugParams SEGFAULT() { + DebugParams debugParams = new DebugParams(); + debugParams.command = new String[] { "SEGFAULT" }; + return debugParams; + } + + public static DebugParams OBJECT(String key) { + DebugParams debugParams = new DebugParams(); + debugParams.command = new String[] { "OBJECT", key }; + return debugParams; + } + + public static DebugParams RELOAD() { + DebugParams debugParams = new DebugParams(); + debugParams.command = new String[] { "RELOAD" }; + return debugParams; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoCoordinate.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoCoordinate.java new file mode 100755 index 000000000..d0734459d --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoCoordinate.java @@ -0,0 +1,47 @@ +package com.fr.third.redis.clients.jedis; + +public class GeoCoordinate { + private double longitude; + private double latitude; + + public GeoCoordinate(double longitude, double latitude) { + this.longitude = longitude; + this.latitude = latitude; + } + + public double getLongitude() { + return longitude; + } + + public double getLatitude() { + return latitude; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GeoCoordinate)) return false; + + GeoCoordinate that = (GeoCoordinate) o; + + if (Double.compare(that.longitude, longitude) != 0) return false; + return Double.compare(that.latitude, latitude) == 0; + } + + @Override + public int hashCode() { + // follows IntelliJ default hashCode implementation + int result; + long temp; + temp = Double.doubleToLongBits(longitude); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(latitude); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public String toString() { + return "(" + longitude + "," + latitude + ")"; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoRadiusResponse.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoRadiusResponse.java new file mode 100755 index 000000000..ced7169d6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoRadiusResponse.java @@ -0,0 +1,37 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class GeoRadiusResponse { + private byte[] member; + private double distance; + private GeoCoordinate coordinate; + + public GeoRadiusResponse(byte[] member) { + this.member = member; + } + + public void setDistance(double distance) { + this.distance = distance; + } + + public void setCoordinate(GeoCoordinate coordinate) { + this.coordinate = coordinate; + } + + public byte[] getMember() { + return member; + } + + public String getMemberByString() { + return SafeEncoder.encode(member); + } + + public double getDistance() { + return distance; + } + + public GeoCoordinate getCoordinate() { + return coordinate; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoUnit.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoUnit.java new file mode 100755 index 000000000..a12a8e57e --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/GeoUnit.java @@ -0,0 +1,13 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public enum GeoUnit { + M, KM, MI, FT; + + public final byte[] raw; + + GeoUnit() { + raw = SafeEncoder.encode(this.name().toLowerCase()); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/HostAndPort.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/HostAndPort.java new file mode 100755 index 000000000..bd2f53ac2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/HostAndPort.java @@ -0,0 +1,153 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.Serializable; +import java.net.InetAddress; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HostAndPort implements Serializable { + private static final long serialVersionUID = -519876229978427751L; + + protected static Logger log = LoggerFactory.getLogger(HostAndPort.class.getName()); + public static String localhost; + + + private String host; + private int port; + + public HostAndPort(String host, int port) { + this.host = host; + this.port = port; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof HostAndPort) { + HostAndPort hp = (HostAndPort) obj; + + String thisHost = convertHost(host); + String hpHost = convertHost(hp.host); + return port == hp.port && thisHost.equals(hpHost); + + } + + return false; + } + + @Override + public int hashCode() { + return 31 * convertHost(host).hashCode() + port; + } + + @Override + public String toString() { + return host + ":" + port; + } + + /** + * Splits String into host and port parts. + * String must be in ( host + ":" + port ) format. + * Port is optional + * @param from String to parse + * @return array of host and port strings + */ + public static String[] extractParts(String from){ + int idx = from.lastIndexOf(":"); + String host = idx != -1 ? from.substring(0, idx) : from; + String port = idx != -1 ? from.substring(idx + 1) : ""; + return new String[] { host, port }; + } + + /** + * Creates HostAndPort instance from string. + * String must be in ( host + ":" + port ) format. + * Port is mandatory. Can convert host part. + * @see #convertHost(String) + * @param from String to parse + * @return HostAndPort instance + */ + public static HostAndPort parseString(String from){ + // NOTE: redis answers with + // '99aa9999aa9a99aa099aaa990aa99a09aa9a9999 9a09:9a9:a090:9a::99a slave 8c88888888cc08088cc8c8c888c88c8888c88cc8 0 1468251272993 37 connected' + // for CLUSTER NODES, ASK and MOVED scenarios. That's why there is no possibility to parse address in 'correct' way. + // Redis should switch to 'bracketized' (RFC 3986) IPv6 address. + try { + String[] parts = extractParts(from); + String host = parts[0]; + int port = Integer.parseInt(parts[1]); + return new HostAndPort(convertHost(host), port); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(ex); + } + } + + public static String convertHost(String host) { + try { + /* + * Validate the host name as an IPV4/IPV6 address. + * If this is an AWS ENDPOINT it will not parse. + * In that case accept host as is. + * + * Costs: If this is an IPV4/6 encoding, e.g. 127.0.0.1 then no DNS lookup + * is done. If it is a name then a DNS lookup is done but it is normally cached. + * Secondarily, this class is typically used to create a connection once + * at the beginning of processing and then not used again. So even if the DNS + * lookup needs to be done then the cost is miniscule. + */ + InetAddress inetAddress = InetAddress.getByName(host); + + // isLoopbackAddress() handles both IPV4 and IPV6 + if (inetAddress.isLoopbackAddress() || host.equals("0.0.0.0") || host.startsWith("169.254")) + return getLocalhost(); + else + return host; + } catch (Exception e) { + // Not a valid IP address + log.warn("{}.convertHost '" + host + "' is not a valid IP address. ", HostAndPort.class.getName(), e); + return host; + } + } + + public static void setLocalhost(String localhost) { + synchronized (HostAndPort.class) { + HostAndPort.localhost = localhost; + } + } + + /** + * This method resolves the localhost in a 'lazy manner'. + * + * @return localhost + */ + public static String getLocalhost() { + if (localhost == null) { + synchronized (HostAndPort.class) { + if (localhost == null) { + return localhost = getLocalHostQuietly(); + } + } + } + return localhost; + } + + public static String getLocalHostQuietly() { + String localAddress; + try { + localAddress = InetAddress.getLocalHost().getHostAddress(); + } catch (Exception ex) { + log.error("{}.getLocalHostQuietly : cant resolve localhost address", + HostAndPort.class.getName(), ex); + localAddress = "localhost"; + } + return localAddress; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Jedis.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Jedis.java new file mode 100755 index 000000000..a3e87d024 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Jedis.java @@ -0,0 +1,3582 @@ +package com.fr.third.redis.clients.jedis; + +import java.net.URI; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.redis.clients.jedis.commands.AdvancedJedisCommands; +import com.fr.third.redis.clients.jedis.commands.BasicCommands; +import com.fr.third.redis.clients.jedis.commands.ClusterCommands; +import com.fr.third.redis.clients.jedis.commands.JedisCommands; +import com.fr.third.redis.clients.jedis.commands.ModuleCommands; +import com.fr.third.redis.clients.jedis.commands.MultiKeyCommands; +import com.fr.third.redis.clients.jedis.commands.ScriptingCommands; +import com.fr.third.redis.clients.jedis.commands.SentinelCommands; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; +import com.fr.third.redis.clients.jedis.util.Slowlog; + +public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands, + AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands, SentinelCommands, ModuleCommands { + + protected JedisPoolAbstract dataSource = null; + + public Jedis() { + super(); + } + + public Jedis(final String host) { + super(host); + } + + public Jedis(final HostAndPort hp) { + super(hp); + } + + public Jedis(final String host, final int port) { + super(host, port); + } + + public Jedis(final String host, final int port, final boolean ssl) { + super(host, port, ssl); + } + + public Jedis(final String host, final int port, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(host, port, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public Jedis(final String host, final int port, final int timeout) { + super(host, port, timeout); + } + + public Jedis(final String host, final int port, final int timeout, final boolean ssl) { + super(host, port, timeout, ssl); + } + + public Jedis(final String host, final int port, final int timeout, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(host, port, timeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout) { + super(host, port, connectionTimeout, soTimeout); + } + + public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout, + final boolean ssl) { + super(host, port, connectionTimeout, soTimeout, ssl); + } + + public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout, + final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(host, port, connectionTimeout, soTimeout, ssl, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public Jedis(JedisShardInfo shardInfo) { + super(shardInfo); + } + + public Jedis(URI uri) { + super(uri); + } + + public Jedis(URI uri, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(uri, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public Jedis(final URI uri, final int timeout) { + super(uri, timeout); + } + + public Jedis(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + super(uri, timeout, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public Jedis(final URI uri, final int connectionTimeout, final int soTimeout) { + super(uri, connectionTimeout, soTimeout); + } + + public Jedis(final URI uri, final int connectionTimeout, final int soTimeout, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + super(uri, connectionTimeout, soTimeout, sslSocketFactory, sslParameters, hostnameVerifier); + } + + /** + * Works same as ping() but returns argument message instead of PONG. + * @param message + * @return message + */ + public String ping(final String message) { + checkIsInMultiOrPipeline(); + client.sendCommand(Protocol.Command.PING, message); + return client.getBulkReply(); + } + + /** + * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 + * GB). + *

+ * Time complexity: O(1) + * @param key + * @param value + * @return Status code reply + */ + @Override + public String set(final String key, final String value) { + checkIsInMultiOrPipeline(); + client.set(key, value); + return client.getStatusCodeReply(); + } + + /** + * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 + * GB). + * @param key + * @param value + * @param params NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the + * key if it already exist. EX|PX, expire time units: EX = seconds; PX = milliseconds + * @return Status code reply + */ + @Override + public String set(final String key, final String value, final SetParams params) { + checkIsInMultiOrPipeline(); + client.set(key, value, params); + return client.getStatusCodeReply(); + } + + /** + * Get the value of the specified key. If the key does not exist null is returned. If the value + * stored at key is not a string an error is returned because GET can only handle string values. + *

+ * Time complexity: O(1) + * @param key + * @return Bulk reply + */ + @Override + public String get(final String key) { + checkIsInMultiOrPipeline(); + client.sendCommand(Protocol.Command.GET, key); + return client.getBulkReply(); + } + + /** + * Test if the specified keys exist. The command returns the number of keys exist. + * Time complexity: O(N) + * @param keys + * @return Integer reply, specifically: an integer greater than 0 if one or more keys exist, + * 0 if none of the specified keys exist. + */ + @Override + public Long exists(final String... keys) { + checkIsInMultiOrPipeline(); + client.exists(keys); + return client.getIntegerReply(); + } + + /** + * Test if the specified key exists. The command returns "1" if the key exists, otherwise "0" is + * returned. Note that even keys set with an empty string as value will return "1". Time + * complexity: O(1) + * @param key + * @return Boolean reply, true if the key exists, otherwise false + */ + @Override + public Boolean exists(final String key) { + checkIsInMultiOrPipeline(); + client.exists(key); + return client.getIntegerReply() == 1; + } + + /** + * Remove the specified keys. If a given key does not exist no operation is performed for this + * key. The command returns the number of keys removed. Time complexity: O(1) + * @param keys + * @return Integer reply, specifically: an integer greater than 0 if one or more keys were removed + * 0 if none of the specified key existed + */ + @Override + public Long del(final String... keys) { + checkIsInMultiOrPipeline(); + client.del(keys); + return client.getIntegerReply(); + } + + @Override + public Long del(final String key) { + checkIsInMultiOrPipeline(); + client.del(key); + return client.getIntegerReply(); + } + + /** + * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is + * ignored if it does not exist. However the command performs the actual memory reclaiming in a + * different thread, so it is not blocking, while DEL is. This is where the command name comes + * from: the command just unlinks the keys from the keyspace. The actual removal will happen later + * asynchronously. + *

+ * Time complexity: O(1) for each key removed regardless of its size. Then the command does O(N) + * work in a different thread in order to reclaim memory, where N is the number of allocations the + * deleted objects where composed of. + * @param keys + * @return Integer reply: The number of keys that were unlinked + */ + @Override + public Long unlink(final String... keys) { + checkIsInMultiOrPipeline(); + client.unlink(keys); + return client.getIntegerReply(); + } + + @Override + public Long unlink(final String key) { + client.unlink(key); + return client.getIntegerReply(); + } + + /** + * Return the type of the value stored at key in form of a string. The type can be one of "none", + * "string", "list", "set". "none" is returned if the key does not exist. Time complexity: O(1) + * @param key + * @return Status code reply, specifically: "none" if the key does not exist "string" if the key + * contains a String value "list" if the key contains a List value "set" if the key + * contains a Set value "zset" if the key contains a Sorted Set value "hash" if the key + * contains a Hash value + */ + @Override + public String type(final String key) { + checkIsInMultiOrPipeline(); + client.type(key); + return client.getStatusCodeReply(); + } + + @Override + public Set keys(final String pattern) { + checkIsInMultiOrPipeline(); + client.keys(pattern); + return BuilderFactory.STRING_SET.build(client.getBinaryMultiBulkReply()); + } + + /** + * Return a randomly selected key from the currently selected DB. + *

+ * Time complexity: O(1) + * @return Singe line reply, specifically the randomly selected key or an empty string is the + * database is empty + */ + @Override + public String randomKey() { + checkIsInMultiOrPipeline(); + client.randomKey(); + return client.getBulkReply(); + } + + /** + * Atomically renames the key oldkey to newkey. If the source and destination name are the same an + * error is returned. If newkey already exists it is overwritten. + *

+ * Time complexity: O(1) + * @param oldkey + * @param newkey + * @return Status code repy + */ + @Override + public String rename(final String oldkey, final String newkey) { + checkIsInMultiOrPipeline(); + client.rename(oldkey, newkey); + return client.getStatusCodeReply(); + } + + /** + * Rename oldkey into newkey but fails if the destination key newkey already exists. + *

+ * Time complexity: O(1) + * @param oldkey + * @param newkey + * @return Integer reply, specifically: 1 if the key was renamed 0 if the target key already exist + */ + @Override + public Long renamenx(final String oldkey, final String newkey) { + checkIsInMultiOrPipeline(); + client.renamenx(oldkey, newkey); + return client.getIntegerReply(); + } + + /** + * Set a timeout on the specified key. After the timeout the key will be automatically deleted by + * the server. A key with an associated timeout is said to be volatile in Redis terminology. + *

+ * Voltile keys are stored on disk like the other keys, the timeout is persistent too like all the + * other aspects of the dataset. Saving a dataset containing expires and stopping the server does + * not stop the flow of time as Redis stores on disk the time when the key will no longer be + * available as Unix time, and not the remaining seconds. + *

+ * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire + * set. It is also possible to undo the expire at all turning the key into a normal key using the + * {@link #persist(String) PERSIST} command. + *

+ * Time complexity: O(1) + * @see Expire Command + * @param key + * @param seconds + * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since + * the key already has an associated timeout (this may happen only in Redis versions < + * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. + */ + @Override + public Long expire(final String key, final int seconds) { + checkIsInMultiOrPipeline(); + client.expire(key, seconds); + return client.getIntegerReply(); + } + + /** + * EXPIREAT works exctly like {@link #expire(String, int) EXPIRE} but instead to get the number of + * seconds representing the Time To Live of the key as a second argument (that is a relative way + * of specifing the TTL), it takes an absolute one in the form of a UNIX timestamp (Number of + * seconds elapsed since 1 Gen 1970). + *

+ * EXPIREAT was introduced in order to implement the Append Only File persistence mode so that + * EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. + * Of course EXPIREAT can also used by programmers that need a way to simply specify that a given + * key should expire at a given time in the future. + *

+ * Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire + * set. It is also possible to undo the expire at all turning the key into a normal key using the + * {@link #persist(String) PERSIST} command. + *

+ * Time complexity: O(1) + * @see Expire Command + * @param key + * @param unixTime + * @return Integer reply, specifically: 1: the timeout was set. 0: the timeout was not set since + * the key already has an associated timeout (this may happen only in Redis versions < + * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. + */ + @Override + public Long expireAt(final String key, final long unixTime) { + checkIsInMultiOrPipeline(); + client.expireAt(key, unixTime); + return client.getIntegerReply(); + } + + /** + * The TTL command returns the remaining time to live in seconds of a key that has an + * {@link #expire(String, int) EXPIRE} set. This introspection capability allows a Redis client to + * check how many seconds a given key will continue to be part of the dataset. + * @param key + * @return Integer reply, returns the remaining time to live in seconds of a key that has an + * EXPIRE. In Redis 2.6 or older, if the Key does not exists or does not have an + * associated expire, -1 is returned. In Redis 2.8 or newer, if the Key does not have an + * associated expire, -1 is returned or if the Key does not exists, -2 is returned. + */ + @Override + public Long ttl(final String key) { + checkIsInMultiOrPipeline(); + client.ttl(key); + return client.getIntegerReply(); + } + + /** + * Alters the last access time of a key(s). A key is ignored if it does not exist. + * Time complexity: O(N) where N is the number of keys that will be touched. + * @param keys + * @return Integer reply: The number of keys that were touched. + */ + @Override + public Long touch(final String... keys) { + checkIsInMultiOrPipeline(); + client.touch(keys); + return client.getIntegerReply(); + } + + @Override + public Long touch(final String key) { + checkIsInMultiOrPipeline(); + client.touch(key); + return client.getIntegerReply(); + } + + /** + * Move the specified key from the currently selected DB to the specified destination DB. Note + * that this command returns 1 only if the key was successfully moved, and 0 if the target key was + * already there or if the source key was not found at all, so it is possible to use MOVE as a + * locking primitive. + * @param key + * @param dbIndex + * @return Integer reply, specifically: 1 if the key was moved 0 if the key was not moved because + * already present on the target DB or was not found in the current DB. + */ + @Override + public Long move(final String key, final int dbIndex) { + checkIsInMultiOrPipeline(); + client.move(key, dbIndex); + return client.getIntegerReply(); + } + + /** + * GETSET is an atomic set this value and return the old value command. Set key to the string + * value and return the old value stored at key. The string can't be longer than 1073741824 bytes + * (1 GB). + *

+ * Time complexity: O(1) + * @param key + * @param value + * @return Bulk reply + */ + @Override + public String getSet(final String key, final String value) { + checkIsInMultiOrPipeline(); + client.getSet(key, value); + return client.getBulkReply(); + } + + /** + * Get the values of all the specified keys. If one or more keys dont exist or is not of type + * String, a 'nil' value is returned instead of the value of the specified key, but the operation + * never fails. + *

+ * Time complexity: O(1) for every key + * @param keys + * @return Multi bulk reply + */ + @Override + public List mget(final String... keys) { + checkIsInMultiOrPipeline(); + client.mget(keys); + return client.getMultiBulkReply(); + } + + /** + * SETNX works exactly like {@link #set(String, String) SET} with the only difference that if the + * key already exists no operation is performed. SETNX actually means "SET if Not eXists". + *

+ * Time complexity: O(1) + * @param key + * @param value + * @return Integer reply, specifically: 1 if the key was set 0 if the key was not set + */ + @Override + public Long setnx(final String key, final String value) { + checkIsInMultiOrPipeline(); + client.setnx(key, value); + return client.getIntegerReply(); + } + + /** + * The command is exactly equivalent to the following group of commands: + * {@link #set(String, String) SET} + {@link #expire(String, int) EXPIRE}. The operation is + * atomic. + *

+ * Time complexity: O(1) + * @param key + * @param seconds + * @param value + * @return Status code reply + */ + @Override + public String setex(final String key, final int seconds, final String value) { + checkIsInMultiOrPipeline(); + client.setex(key, seconds, value); + return client.getStatusCodeReply(); + } + + /** + * Set the the respective keys to the respective values. MSET will replace old values with new + * values, while {@link #msetnx(String...) MSETNX} will not perform any operation at all even if + * just a single key already exists. + *

+ * Because of this semantic MSETNX can be used in order to set different keys representing + * different fields of an unique logic object in a way that ensures that either all the fields or + * none at all are set. + *

+ * Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B + * are modified, another client talking to Redis can either see the changes to both A and B at + * once, or no modification at all. + * @see #msetnx(String...) + * @param keysvalues + * @return Status code reply Basically +OK as MSET can't fail + */ + @Override + public String mset(final String... keysvalues) { + checkIsInMultiOrPipeline(); + client.mset(keysvalues); + return client.getStatusCodeReply(); + } + + /** + * Set the the respective keys to the respective values. {@link #mset(String...) MSET} will + * replace old values with new values, while MSETNX will not perform any operation at all even if + * just a single key already exists. + *

+ * Because of this semantic MSETNX can be used in order to set different keys representing + * different fields of an unique logic object in a way that ensures that either all the fields or + * none at all are set. + *

+ * Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B + * are modified, another client talking to Redis can either see the changes to both A and B at + * once, or no modification at all. + * @see #mset(String...) + * @param keysvalues + * @return Integer reply, specifically: 1 if the all the keys were set 0 if no key was set (at + * least one key already existed) + */ + @Override + public Long msetnx(final String... keysvalues) { + checkIsInMultiOrPipeline(); + client.msetnx(keysvalues); + return client.getIntegerReply(); + } + + /** + * IDECRBY work just like {@link #decr(String) INCR} but instead to decrement by 1 the decrement + * is integer. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incr(String) + * @see #decr(String) + * @see #incrBy(String, long) + * @param key + * @param decrement + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long decrBy(final String key, final long decrement) { + checkIsInMultiOrPipeline(); + client.decrBy(key, decrement); + return client.getIntegerReply(); + } + + /** + * Decrement the number stored at key by one. If the key does not exist or contains a value of a + * wrong type, set the key to the value of "0" before to perform the decrement operation. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incr(String) + * @see #incrBy(String, long) + * @see #decrBy(String, long) + * @param key + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long decr(final String key) { + checkIsInMultiOrPipeline(); + client.decr(key); + return client.getIntegerReply(); + } + + /** + * INCRBY work just like {@link #incr(String) INCR} but instead to increment by 1 the increment is + * integer. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incr(String) + * @see #decr(String) + * @see #decrBy(String, long) + * @param key + * @param increment + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long incrBy(final String key, final long increment) { + checkIsInMultiOrPipeline(); + client.incrBy(key, increment); + return client.getIntegerReply(); + } + + /** + * INCRBYFLOAT + *

+ * INCRBYFLOAT commands are limited to double precision floating point values. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "double" types. + * Simply the string stored at the key is parsed as a base double precision floating point value, + * incremented, and then converted back as a string. There is no DECRYBYFLOAT but providing a + * negative value will work as expected. + *

+ * Time complexity: O(1) + * @param key + * @param increment + * @return Double reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Double incrByFloat(final String key, final double increment) { + checkIsInMultiOrPipeline(); + client.incrByFloat(key, increment); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + /** + * Increment the number stored at key by one. If the key does not exist or contains a value of a + * wrong type, set the key to the value of "0" before to perform the increment operation. + *

+ * INCR commands are limited to 64 bit signed integers. + *

+ * Note: this is actually a string operation, that is, in Redis there are not "integer" types. + * Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, + * and then converted back as a string. + *

+ * Time complexity: O(1) + * @see #incrBy(String, long) + * @see #decr(String) + * @see #decrBy(String, long) + * @param key + * @return Integer reply, this commands will reply with the new value of key after the increment. + */ + @Override + public Long incr(final String key) { + checkIsInMultiOrPipeline(); + client.incr(key); + return client.getIntegerReply(); + } + + /** + * If the key already exists and is a string, this command appends the provided value at the end + * of the string. If the key does not exist it is created and set as an empty string, so APPEND + * will be very similar to SET in this special case. + *

+ * Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is + * small and the already present value is of any size, since the dynamic string library used by + * Redis will double the free space available on every reallocation. + * @param key + * @param value + * @return Integer reply, specifically the total length of the string after the append operation. + */ + @Override + public Long append(final String key, final String value) { + checkIsInMultiOrPipeline(); + client.append(key, value); + return client.getIntegerReply(); + } + + /** + * Return a subset of the string from offset start to offset end (both offsets are inclusive). + * Negative offsets can be used in order to provide an offset starting from the end of the string. + * So -1 means the last char, -2 the penultimate and so forth. + *

+ * The function handles out of range requests without raising an error, but just limiting the + * resulting range to the actual length of the string. + *

+ * Time complexity: O(start+n) (with start being the start index and n the total length of the + * requested range). Note that the lookup part of this command is O(1) so for small strings this + * is actually an O(1) command. + * @param key + * @param start + * @param end + * @return Bulk reply + */ + @Override + public String substr(final String key, final int start, final int end) { + checkIsInMultiOrPipeline(); + client.substr(key, start, end); + return client.getBulkReply(); + } + + /** + * Set the specified hash field to the specified value. + *

+ * If key does not exist, a new key holding a hash is created. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @param value + * @return If the field already exists, and the HSET just produced an update of the value, 0 is + * returned, otherwise if a new field is created 1 is returned. + */ + @Override + public Long hset(final String key, final String field, final String value) { + checkIsInMultiOrPipeline(); + client.hset(key, field, value); + return client.getIntegerReply(); + } + + @Override + public Long hset(final String key, final Map hash) { + checkIsInMultiOrPipeline(); + client.hset(key, hash); + return client.getIntegerReply(); + } + + /** + * If key holds a hash, retrieve the value associated to the specified field. + *

+ * If the field is not found or the key does not exist, a special 'nil' value is returned. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @return Bulk reply + */ + @Override + public String hget(final String key, final String field) { + checkIsInMultiOrPipeline(); + client.hget(key, field); + return client.getBulkReply(); + } + + /** + * Set the specified hash field to the specified value if the field not exists. Time + * complexity: O(1) + * @param key + * @param field + * @param value + * @return If the field already exists, 0 is returned, otherwise if a new field is created 1 is + * returned. + */ + @Override + public Long hsetnx(final String key, final String field, final String value) { + checkIsInMultiOrPipeline(); + client.hsetnx(key, field, value); + return client.getIntegerReply(); + } + + /** + * Set the respective fields to the respective values. HMSET replaces old values with new values. + *

+ * If key does not exist, a new key holding a hash is created. + *

+ * Time complexity: O(N) (with N being the number of fields) + * @param key + * @param hash + * @return Return OK or Exception if hash is empty + */ + @Override + public String hmset(final String key, final Map hash) { + checkIsInMultiOrPipeline(); + client.hmset(key, hash); + return client.getStatusCodeReply(); + } + + /** + * Retrieve the values associated to the specified fields. + *

+ * If some of the specified fields do not exist, nil values are returned. Non existing keys are + * considered like empty hashes. + *

+ * Time complexity: O(N) (with N being the number of fields) + * @param key + * @param fields + * @return Multi Bulk Reply specifically a list of all the values associated with the specified + * fields, in the same order of the request. + */ + @Override + public List hmget(final String key, final String... fields) { + checkIsInMultiOrPipeline(); + client.hmget(key, fields); + return client.getMultiBulkReply(); + } + + /** + * Increment the number stored at field in the hash at key by value. If key does not exist, a new + * key holding a hash is created. If field does not exist or holds a string, the value is set to 0 + * before applying the operation. Since the value argument is signed you can use this command to + * perform both increments and decrements. + *

+ * The range of values supported by HINCRBY is limited to 64 bit signed integers. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @param value + * @return Integer reply The new value at field after the increment operation. + */ + @Override + public Long hincrBy(final String key, final String field, final long value) { + checkIsInMultiOrPipeline(); + client.hincrBy(key, field, value); + return client.getIntegerReply(); + } + + /** + * Increment the number stored at field in the hash at key by a double precision floating point + * value. If key does not exist, a new key holding a hash is created. If field does not exist or + * holds a string, the value is set to 0 before applying the operation. Since the value argument + * is signed you can use this command to perform both increments and decrements. + *

+ * The range of values supported by HINCRBYFLOAT is limited to double precision floating point + * values. + *

+ * Time complexity: O(1) + * @param key + * @param field + * @param value + * @return Double precision floating point reply The new value at field after the increment + * operation. + */ + @Override + public Double hincrByFloat(final String key, final String field, final double value) { + checkIsInMultiOrPipeline(); + client.hincrByFloat(key, field, value); + final String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + /** + * Test for existence of a specified field in a hash. Time complexity: O(1) + * @param key + * @param field + * @return Return 1 if the hash stored at key contains the specified field. Return 0 if the key is + * not found or the field is not present. + */ + @Override + public Boolean hexists(final String key, final String field) { + checkIsInMultiOrPipeline(); + client.hexists(key, field); + return client.getIntegerReply() == 1; + } + + /** + * Remove the specified field from an hash stored at key. + *

+ * Time complexity: O(1) + * @param key + * @param fields + * @return If the field was present in the hash it is deleted and 1 is returned, otherwise 0 is + * returned and no operation is performed. + */ + @Override + public Long hdel(final String key, final String... fields) { + checkIsInMultiOrPipeline(); + client.hdel(key, fields); + return client.getIntegerReply(); + } + + /** + * Return the number of items in a hash. + *

+ * Time complexity: O(1) + * @param key + * @return The number of entries (fields) contained in the hash stored at key. If the specified + * key does not exist, 0 is returned assuming an empty hash. + */ + @Override + public Long hlen(final String key) { + checkIsInMultiOrPipeline(); + client.hlen(key); + return client.getIntegerReply(); + } + + /** + * Return all the fields in a hash. + *

+ * Time complexity: O(N), where N is the total number of entries + * @param key + * @return All the fields names contained into a hash. + */ + @Override + public Set hkeys(final String key) { + checkIsInMultiOrPipeline(); + client.hkeys(key); + return BuilderFactory.STRING_SET.build(client.getBinaryMultiBulkReply()); + } + + /** + * Return all the values in a hash. + *

+ * Time complexity: O(N), where N is the total number of entries + * @param key + * @return All the fields values contained into a hash. + */ + @Override + public List hvals(final String key) { + checkIsInMultiOrPipeline(); + client.hvals(key); + final List lresult = client.getMultiBulkReply(); + return lresult; + } + + /** + * Return all the fields and associated values in a hash. + *

+ * Time complexity: O(N), where N is the total number of entries + * @param key + * @return All the fields and values contained into a hash. + */ + @Override + public Map hgetAll(final String key) { + checkIsInMultiOrPipeline(); + client.hgetAll(key); + return BuilderFactory.STRING_MAP.build(client.getBinaryMultiBulkReply()); + } + + /** + * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key + * does not exist an empty list is created just before the append operation. If the key exists but + * is not a List an error is returned. + *

+ * Time complexity: O(1) + * @param key + * @param strings + * @return Integer reply, specifically, the number of elements inside the list after the push + * operation. + */ + @Override + public Long rpush(final String key, final String... strings) { + checkIsInMultiOrPipeline(); + client.rpush(key, strings); + return client.getIntegerReply(); + } + + /** + * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key + * does not exist an empty list is created just before the append operation. If the key exists but + * is not a List an error is returned. + *

+ * Time complexity: O(1) + * @param key + * @param strings + * @return Integer reply, specifically, the number of elements inside the list after the push + * operation. + */ + @Override + public Long lpush(final String key, final String... strings) { + checkIsInMultiOrPipeline(); + client.lpush(key, strings); + return client.getIntegerReply(); + } + + /** + * Return the length of the list stored at the specified key. If the key does not exist zero is + * returned (the same behaviour as for empty lists). If the value stored at key is not a list an + * error is returned. + *

+ * Time complexity: O(1) + * @param key + * @return The length of the list. + */ + @Override + public Long llen(final String key) { + checkIsInMultiOrPipeline(); + client.llen(key); + return client.getIntegerReply(); + } + + /** + * Return the specified elements of the list stored at the specified key. Start and end are + * zero-based indexes. 0 is the first element of the list (the list head), 1 the next element and + * so on. + *

+ * For example LRANGE foobar 0 2 will return the first three elements of the list. + *

+ * start and end can also be negative numbers indicating offsets from the end of the list. For + * example -1 is the last element of the list, -2 the penultimate element and so on. + *

+ * Consistency with range functions in various programming languages + *

+ * Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return 11 elements, + * that is, rightmost item is included. This may or may not be consistent with behavior of + * range-related functions in your programming language of choice (think Ruby's Range.new, + * Array#slice or Python's range() function). + *

+ * LRANGE behavior is consistent with one of Tcl. + *

+ * Out-of-range indexes + *

+ * Indexes out of range will not produce an error: if start is over the end of the list, or start + * > end, an empty list is returned. If end is over the end of the list Redis will threat it + * just like the last element of the list. + *

+ * Time complexity: O(start+n) (with n being the length of the range and start being the start + * offset) + * @param key + * @param start + * @param stop + * @return Multi bulk reply, specifically a list of elements in the specified range. + */ + @Override + public List lrange(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.lrange(key, start, stop); + return client.getMultiBulkReply(); + } + + /** + * Trim an existing list so that it will contain only the specified range of elements specified. + * Start and end are zero-based indexes. 0 is the first element of the list (the list head), 1 the + * next element and so on. + *

+ * For example LTRIM foobar 0 2 will modify the list stored at foobar key so that only the first + * three elements of the list will remain. + *

+ * start and end can also be negative numbers indicating offsets from the end of the list. For + * example -1 is the last element of the list, -2 the penultimate element and so on. + *

+ * Indexes out of range will not produce an error: if start is over the end of the list, or start + * > end, an empty list is left as value. If end over the end of the list Redis will threat it + * just like the last element of the list. + *

+ * Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example: + *

+ * {@code lpush("mylist", "someelement"); ltrim("mylist", 0, 99); * } + *

+ * The above two commands will push elements in the list taking care that the list will not grow + * without limits. This is very useful when using Redis to store logs for example. It is important + * to note that when used in this way LTRIM is an O(1) operation because in the average case just + * one element is removed from the tail of the list. + *

+ * Time complexity: O(n) (with n being len of list - len of range) + * @param key + * @param start + * @param stop + * @return Status code reply + */ + @Override + public String ltrim(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.ltrim(key, start, stop); + return client.getStatusCodeReply(); + } + + /** + * Return the specified element of the list stored at the specified key. 0 is the first element, 1 + * the second and so on. Negative indexes are supported, for example -1 is the last element, -2 + * the penultimate and so on. + *

+ * If the value stored at key is not of list type an error is returned. If the index is out of + * range a 'nil' reply is returned. + *

+ * Note that even if the average time complexity is O(n) asking for the first or the last element + * of the list is O(1). + *

+ * Time complexity: O(n) (with n being the length of the list) + * @param key + * @param index + * @return Bulk reply, specifically the requested element + */ + @Override + public String lindex(final String key, final long index) { + checkIsInMultiOrPipeline(); + client.lindex(key, index); + return client.getBulkReply(); + } + + /** + * Set a new value as the element at index position of the List at key. + *

+ * Out of range indexes will generate an error. + *

+ * Similarly to other list commands accepting indexes, the index can be negative to access + * elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, + * and so forth. + *

+ * Time complexity: + *

+ * O(N) (with N being the length of the list), setting the first or last elements of the list is + * O(1). + * @see #lindex(String, long) + * @param key + * @param index + * @param value + * @return Status code reply + */ + @Override + public String lset(final String key, final long index, final String value) { + checkIsInMultiOrPipeline(); + client.lset(key, index, value); + return client.getStatusCodeReply(); + } + + /** + * Remove the first count occurrences of the value element from the list. If count is zero all the + * elements are removed. If count is negative elements are removed from tail to head, instead to + * go from head to tail that is the normal behaviour. So for example LREM with count -2 and hello + * as value to remove against the list (a,b,c,hello,x,hello,hello) will lave the list + * (a,b,c,hello,x). The number of removed elements is returned as an integer, see below for more + * information about the returned value. Note that non existing keys are considered like empty + * lists by LREM, so LREM against non existing keys will always return 0. + *

+ * Time complexity: O(N) (with N being the length of the list) + * @param key + * @param count + * @param value + * @return Integer Reply, specifically: The number of removed elements if the operation succeeded + */ + @Override + public Long lrem(final String key, final long count, final String value) { + checkIsInMultiOrPipeline(); + client.lrem(key, count, value); + return client.getIntegerReply(); + } + + /** + * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example + * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become + * "b","c". + *

+ * If the key does not exist or the list is already empty the special value 'nil' is returned. + * @see #rpop(String) + * @param key + * @return Bulk reply + */ + @Override + public String lpop(final String key) { + checkIsInMultiOrPipeline(); + client.lpop(key); + return client.getBulkReply(); + } + + /** + * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example + * if the list contains the elements "a","b","c" RPOP will return "c" and the list will become + * "a","b". + *

+ * If the key does not exist or the list is already empty the special value 'nil' is returned. + * @see #lpop(String) + * @param key + * @return Bulk reply + */ + @Override + public String rpop(final String key) { + checkIsInMultiOrPipeline(); + client.rpop(key); + return client.getBulkReply(); + } + + /** + * Atomically return and remove the last (tail) element of the srckey list, and push the element + * as the first (head) element of the dstkey list. For example if the source list contains the + * elements "a","b","c" and the destination list contains the elements "foo","bar" after an + * RPOPLPUSH command the content of the two lists will be "a","b" and "c","foo","bar". + *

+ * If the key does not exist or the list is already empty the special value 'nil' is returned. If + * the srckey and dstkey are the same the operation is equivalent to removing the last element + * from the list and pusing it as first element of the list, so it's a "list rotation" command. + *

+ * Time complexity: O(1) + * @param srckey + * @param dstkey + * @return Bulk reply + */ + @Override + public String rpoplpush(final String srckey, final String dstkey) { + checkIsInMultiOrPipeline(); + client.rpoplpush(srckey, dstkey); + return client.getBulkReply(); + } + + /** + * Add the specified member to the set value stored at key. If member is already a member of the + * set no operation is performed. If key does not exist a new set with the specified member as + * sole member is created. If the key exists but does not hold a set value an error is returned. + *

+ * Time complexity O(1) + * @param key + * @param members + * @return Integer reply, specifically: 1 if the new element was added 0 if the element was + * already a member of the set + */ + @Override + public Long sadd(final String key, final String... members) { + checkIsInMultiOrPipeline(); + client.sadd(key, members); + return client.getIntegerReply(); + } + + /** + * Return all the members (elements) of the set value stored at key. This is just syntax glue for + * {@link #sinter(String...) SINTER}. + *

+ * Time complexity O(N) + * @param key + * @return Multi bulk reply + */ + @Override + public Set smembers(final String key) { + checkIsInMultiOrPipeline(); + client.smembers(key); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * Remove the specified member from the set value stored at key. If member was not a member of the + * set no operation is performed. If key does not hold a set value an error is returned. + *

+ * Time complexity O(1) + * @param key + * @param members + * @return Integer reply, specifically: 1 if the new element was removed 0 if the new element was + * not a member of the set + */ + @Override + public Long srem(final String key, final String... members) { + checkIsInMultiOrPipeline(); + client.srem(key, members); + return client.getIntegerReply(); + } + + /** + * Remove a random element from a Set returning it as return value. If the Set is empty or the key + * does not exist, a nil object is returned. + *

+ * The {@link #srandmember(String)} command does a similar work but the returned element is not + * removed from the Set. + *

+ * Time complexity O(1) + * @param key + * @return Bulk reply + */ + @Override + public String spop(final String key) { + checkIsInMultiOrPipeline(); + client.spop(key); + return client.getBulkReply(); + } + + @Override + public Set spop(final String key, final long count) { + checkIsInMultiOrPipeline(); + client.spop(key, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * Move the specifided member from the set at srckey to the set at dstkey. This operation is + * atomic, in every given moment the element will appear to be in the source or destination set + * for accessing clients. + *

+ * If the source set does not exist or does not contain the specified element no operation is + * performed and zero is returned, otherwise the element is removed from the source set and added + * to the destination set. On success one is returned, even if the element was already present in + * the destination set. + *

+ * An error is raised if the source or destination keys contain a non Set value. + *

+ * Time complexity O(1) + * @param srckey + * @param dstkey + * @param member + * @return Integer reply, specifically: 1 if the element was moved 0 if the element was not found + * on the first set and no operation was performed + */ + @Override + public Long smove(final String srckey, final String dstkey, final String member) { + checkIsInMultiOrPipeline(); + client.smove(srckey, dstkey, member); + return client.getIntegerReply(); + } + + /** + * Return the set cardinality (number of elements). If the key does not exist 0 is returned, like + * for empty sets. + * @param key + * @return Integer reply, specifically: the cardinality (number of elements) of the set as an + * integer. + */ + @Override + public Long scard(final String key) { + checkIsInMultiOrPipeline(); + client.scard(key); + return client.getIntegerReply(); + } + + /** + * Return 1 if member is a member of the set stored at key, otherwise 0 is returned. + *

+ * Time complexity O(1) + * @param key + * @param member + * @return Integer reply, specifically: 1 if the element is a member of the set 0 if the element + * is not a member of the set OR if the key does not exist + */ + @Override + public Boolean sismember(final String key, final String member) { + checkIsInMultiOrPipeline(); + client.sismember(key, member); + return client.getIntegerReply() == 1; + } + + /** + * Return the members of a set resulting from the intersection of all the sets hold at the + * specified keys. Like in {@link #lrange(String, long, long) LRANGE} the result is sent to the + * client as a multi-bulk reply (see the protocol specification for more information). If just a + * single key is specified, then this command produces the same result as + * {@link #smembers(String) SMEMBERS}. Actually SMEMBERS is just syntax sugar for SINTER. + *

+ * Non existing keys are considered like empty sets, so if one of the keys is missing an empty set + * is returned (since the intersection with an empty set always is an empty set). + *

+ * Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the + * number of sets + * @param keys + * @return Multi bulk reply, specifically the list of common elements. + */ + @Override + public Set sinter(final String... keys) { + checkIsInMultiOrPipeline(); + client.sinter(keys); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * This commnad works exactly like {@link #sinter(String...) SINTER} but instead of being returned + * the resulting set is sotred as dstkey. + *

+ * Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the + * number of sets + * @param dstkey + * @param keys + * @return Status code reply + */ + @Override + public Long sinterstore(final String dstkey, final String... keys) { + checkIsInMultiOrPipeline(); + client.sinterstore(dstkey, keys); + return client.getIntegerReply(); + } + + /** + * Return the members of a set resulting from the union of all the sets hold at the specified + * keys. Like in {@link #lrange(String, long, long) LRANGE} the result is sent to the client as a + * multi-bulk reply (see the protocol specification for more information). If just a single key is + * specified, then this command produces the same result as {@link #smembers(String) SMEMBERS}. + *

+ * Non existing keys are considered like empty sets. + *

+ * Time complexity O(N) where N is the total number of elements in all the provided sets + * @param keys + * @return Multi bulk reply, specifically the list of common elements. + */ + @Override + public Set sunion(final String... keys) { + checkIsInMultiOrPipeline(); + client.sunion(keys); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * This command works exactly like {@link #sunion(String...) SUNION} but instead of being returned + * the resulting set is stored as dstkey. Any existing value in dstkey will be over-written. + *

+ * Time complexity O(N) where N is the total number of elements in all the provided sets + * @param dstkey + * @param keys + * @return Status code reply + */ + @Override + public Long sunionstore(final String dstkey, final String... keys) { + checkIsInMultiOrPipeline(); + client.sunionstore(dstkey, keys); + return client.getIntegerReply(); + } + + /** + * Return the difference between the Set stored at key1 and all the Sets key2, ..., keyN + *

+ * Example: + * + *

+   * key1 = [x, a, b, c]
+   * key2 = [c]
+   * key3 = [a, d]
+   * SDIFF key1,key2,key3 => [x, b]
+   * 
+ * + * Non existing keys are considered like empty sets. + *

+ * Time complexity: + *

+ * O(N) with N being the total number of elements of all the sets + * @param keys + * @return Return the members of a set resulting from the difference between the first set + * provided and all the successive sets. + */ + @Override + public Set sdiff(final String... keys) { + checkIsInMultiOrPipeline(); + client.sdiff(keys); + return BuilderFactory.STRING_SET.build(client.getBinaryMultiBulkReply()); + } + + /** + * This command works exactly like {@link #sdiff(String...) SDIFF} but instead of being returned + * the resulting set is stored in dstkey. + * @param dstkey + * @param keys + * @return Status code reply + */ + @Override + public Long sdiffstore(final String dstkey, final String... keys) { + checkIsInMultiOrPipeline(); + client.sdiffstore(dstkey, keys); + return client.getIntegerReply(); + } + + /** + * Return a random element from a Set, without removing the element. If the Set is empty or the + * key does not exist, a nil object is returned. + *

+ * The SPOP command does a similar work but the returned element is popped (removed) from the Set. + *

+ * Time complexity O(1) + * @param key + * @return Bulk reply + */ + @Override + public String srandmember(final String key) { + checkIsInMultiOrPipeline(); + client.srandmember(key); + return client.getBulkReply(); + } + + @Override + public List srandmember(final String key, final int count) { + checkIsInMultiOrPipeline(); + client.srandmember(key, count); + return client.getMultiBulkReply(); + } + + /** + * Add the specified member having the specifeid score to the sorted set stored at key. If member + * is already a member of the sorted set the score is updated, and the element reinserted in the + * right position to ensure sorting. If key does not exist a new sorted set with the specified + * member as sole member is crated. If the key exists but does not hold a sorted set value an + * error is returned. + *

+ * The score value can be the string representation of a double precision floating point number. + *

+ * Time complexity O(log(N)) with N being the number of elements in the sorted set + * @param key + * @param score + * @param member + * @return Integer reply, specifically: 1 if the new element was added 0 if the element was + * already a member of the sorted set and the score was updated + */ + @Override + public Long zadd(final String key, final double score, final String member) { + checkIsInMultiOrPipeline(); + client.zadd(key, score, member); + return client.getIntegerReply(); + } + + @Override + public Long zadd(final String key, final double score, final String member, + final ZAddParams params) { + checkIsInMultiOrPipeline(); + client.zadd(key, score, member, params); + return client.getIntegerReply(); + } + + @Override + public Long zadd(final String key, final Map scoreMembers) { + checkIsInMultiOrPipeline(); + client.zadd(key, scoreMembers); + return client.getIntegerReply(); + } + + @Override + public Long zadd(final String key, final Map scoreMembers, final ZAddParams params) { + checkIsInMultiOrPipeline(); + client.zadd(key, scoreMembers, params); + return client.getIntegerReply(); + } + + @Override + public Set zrange(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrange(key, start, stop); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * Remove the specified member from the sorted set value stored at key. If member was not a member + * of the set no operation is performed. If key does not not hold a set value an error is + * returned. + *

+ * Time complexity O(log(N)) with N being the number of elements in the sorted set + * @param key + * @param members + * @return Integer reply, specifically: 1 if the new element was removed 0 if the new element was + * not a member of the set + */ + @Override + public Long zrem(final String key, final String... members) { + checkIsInMultiOrPipeline(); + client.zrem(key, members); + return client.getIntegerReply(); + } + + /** + * If member already exists in the sorted set adds the increment to its score and updates the + * position of the element in the sorted set accordingly. If member does not already exist in the + * sorted set it is added with increment as score (that is, like if the previous score was + * virtually zero). If key does not exist a new sorted set with the specified member as sole + * member is crated. If the key exists but does not hold a sorted set value an error is returned. + *

+ * The score value can be the string representation of a double precision floating point number. + * It's possible to provide a negative value to perform a decrement. + *

+ * For an introduction to sorted sets check the Introduction to Redis data types page. + *

+ * Time complexity O(log(N)) with N being the number of elements in the sorted set + * @param key + * @param increment + * @param member + * @return The new score + */ + @Override + public Double zincrby(final String key, final double increment, final String member) { + checkIsInMultiOrPipeline(); + client.zincrby(key, increment, member); + return BuilderFactory.DOUBLE.build(client.getOne()); + } + + @Override + public Double zincrby(final String key, final double increment, final String member, final ZIncrByParams params) { + checkIsInMultiOrPipeline(); + client.zincrby(key, increment, member, params); + return BuilderFactory.DOUBLE.build(client.getOne()); + } + + /** + * Return the rank (or index) of member in the sorted set at key, with scores being ordered from + * low to high. + *

+ * When the given member does not exist in the sorted set, the special value 'nil' is returned. + * The returned rank (or index) of the member is 0-based for both commands. + *

+ * Time complexity: + *

+ * O(log(N)) + * @see #zrevrank(String, String) + * @param key + * @param member + * @return Integer reply or a nil bulk reply, specifically: the rank of the element as an integer + * reply if the element exists. A nil bulk reply if there is no such element. + */ + @Override + public Long zrank(final String key, final String member) { + checkIsInMultiOrPipeline(); + client.zrank(key, member); + return client.getIntegerReply(); + } + + /** + * Return the rank (or index) of member in the sorted set at key, with scores being ordered from + * high to low. + *

+ * When the given member does not exist in the sorted set, the special value 'nil' is returned. + * The returned rank (or index) of the member is 0-based for both commands. + *

+ * Time complexity: + *

+ * O(log(N)) + * @see #zrank(String, String) + * @param key + * @param member + * @return Integer reply or a nil bulk reply, specifically: the rank of the element as an integer + * reply if the element exists. A nil bulk reply if there is no such element. + */ + @Override + public Long zrevrank(final String key, final String member) { + checkIsInMultiOrPipeline(); + client.zrevrank(key, member); + return client.getIntegerReply(); + } + + @Override + public Set zrevrange(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrevrange(key, start, stop); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrangeWithScores(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrangeWithScores(key, start, stop); + return getTupledSet(); + } + + @Override + public Set zrevrangeWithScores(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zrevrangeWithScores(key, start, stop); + return getTupledSet(); + } + + /** + * Return the sorted set cardinality (number of elements). If the key does not exist 0 is + * returned, like for empty sorted sets. + *

+ * Time complexity O(1) + * @param key + * @return the cardinality (number of elements) of the set as an integer. + */ + @Override + public Long zcard(final String key) { + checkIsInMultiOrPipeline(); + client.zcard(key); + return client.getIntegerReply(); + } + + /** + * Return the score of the specified element of the sorted set at key. If the specified element + * does not exist in the sorted set, or the key does not exist at all, a special 'nil' value is + * returned. + *

+ * Time complexity: O(1) + * @param key + * @param member + * @return the score + */ + @Override + public Double zscore(final String key, final String member) { + checkIsInMultiOrPipeline(); + client.zscore(key, member); + return BuilderFactory.DOUBLE.build(client.getOne()); + } + + @Override + public String watch(final String... keys) { + client.watch(keys); + return client.getStatusCodeReply(); + } + + /** + * Sort a Set or a List. + *

+ * Sort the elements contained in the List, Set, or Sorted Set value at key. By default sorting is + * numeric with elements being compared as double precision floating point numbers. This is the + * simplest form of SORT. + * @see #sort(String, String) + * @see #sort(String, SortingParams) + * @see #sort(String, SortingParams, String) + * @param key + * @return Assuming the Set/List at key contains a list of numbers, the return value will be the + * list of numbers ordered from the smallest to the biggest number. + */ + @Override + public List sort(final String key) { + checkIsInMultiOrPipeline(); + client.sort(key); + return client.getMultiBulkReply(); + } + + /** + * Sort a Set or a List accordingly to the specified parameters. + *

+ * examples: + *

+ * Given are the following sets and key/values: + * + *

+   * x = [1, 2, 3]
+   * y = [a, b, c]
+   * 
+   * k1 = z
+   * k2 = y
+   * k3 = x
+   * 
+   * w1 = 9
+   * w2 = 8
+   * w3 = 7
+   * 
+ * + * Sort Order: + * + *
+   * sort(x) or sort(x, sp.asc())
+   * -> [1, 2, 3]
+   * 
+   * sort(x, sp.desc())
+   * -> [3, 2, 1]
+   * 
+   * sort(y)
+   * -> [c, a, b]
+   * 
+   * sort(y, sp.alpha())
+   * -> [a, b, c]
+   * 
+   * sort(y, sp.alpha().desc())
+   * -> [c, a, b]
+   * 
+ * + * Limit (e.g. for Pagination): + * + *
+   * sort(x, sp.limit(0, 2))
+   * -> [1, 2]
+   * 
+   * sort(y, sp.alpha().desc().limit(1, 2))
+   * -> [b, a]
+   * 
+ * + * Sorting by external keys: + * + *
+   * sort(x, sb.by(w*))
+   * -> [3, 2, 1]
+   * 
+   * sort(x, sb.by(w*).desc())
+   * -> [1, 2, 3]
+   * 
+ * + * Getting external keys: + * + *
+   * sort(x, sp.by(w*).get(k*))
+   * -> [x, y, z]
+   * 
+   * sort(x, sp.by(w*).get(#).get(k*))
+   * -> [3, x, 2, y, 1, z]
+   * 
+ * @see #sort(String) + * @see #sort(String, SortingParams, String) + * @param key + * @param sortingParameters + * @return a list of sorted elements. + */ + @Override + public List sort(final String key, final SortingParams sortingParameters) { + checkIsInMultiOrPipeline(); + client.sort(key, sortingParameters); + return client.getMultiBulkReply(); + } + + /** + * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking + * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty + * lists. + *

+ * The following is a description of the exact semantic. We describe BLPOP but the two commands + * are identical, the only difference is that BLPOP pops the element from the left (head) of the + * list, and BRPOP pops from the right (tail). + *

+ * Non blocking behavior + *

+ * When BLPOP is called, if at least one of the specified keys contain a non empty list, an + * element is popped from the head of the list and returned to the caller together with the name + * of the key (BLPOP returns a two elements array, the first element is the key, the second the + * popped value). + *

+ * Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 + * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP + * guarantees to return an element from the list stored at list2 (since it is the first non empty + * list starting from the left). + *

+ * Blocking behavior + *

+ * If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other + * client performs a LPUSH or an RPUSH operation against one of the lists. + *

+ * Once new data is present on one of the lists, the client finally returns with the name of the + * key unblocking it and the popped value. + *

+ * When blocking, if a non-zero timeout is specified, the client will unblock returning a nil + * special value if the specified amount of seconds passed without a push operation against at + * least one of the specified keys. + *

+ * The timeout argument is interpreted as an integer value. A timeout of zero means instead to + * block forever. + *

+ * Multiple clients blocking for the same keys + *

+ * Multiple clients can block for the same key. They are put into a queue, so the first to be + * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. + *

+ * blocking POP inside a MULTI/EXEC transaction + *

+ * BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies + * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis + * transaction). + *

+ * The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil + * reply, exactly what happens when the timeout is reached. If you like science fiction, think at + * it like if inside MULTI/EXEC the time will flow at infinite speed :) + *

+ * Time complexity: O(1) + * @see #brpop(int, String...) + * @param timeout + * @param keys + * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the + * unblocking key and the popped value. + *

+ * When a non-zero timeout is specified, and the BLPOP operation timed out, the return + * value is a nil multi bulk reply. Most client values will return false or nil + * accordingly to the programming language used. + */ + @Override + public List blpop(final int timeout, final String... keys) { + return blpop(getArgsAddTimeout(timeout, keys)); + } + + private String[] getArgsAddTimeout(int timeout, String[] keys) { + final int keyCount = keys.length; + final String[] args = new String[keyCount + 1]; + for (int at = 0; at != keyCount; ++at) { + args[at] = keys[at]; + } + + args[keyCount] = String.valueOf(timeout); + return args; + } + + @Override + public List blpop(final String... args) { + checkIsInMultiOrPipeline(); + client.blpop(args); + client.setTimeoutInfinite(); + try { + return client.getMultiBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public List brpop(final String... args) { + checkIsInMultiOrPipeline(); + client.brpop(args); + client.setTimeoutInfinite(); + try { + return client.getMultiBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + + /** + * Sort a Set or a List accordingly to the specified parameters and store the result at dstkey. + * @see #sort(String, SortingParams) + * @see #sort(String) + * @see #sort(String, String) + * @param key + * @param sortingParameters + * @param dstkey + * @return The number of elements of the list at dstkey. + */ + @Override + public Long sort(final String key, final SortingParams sortingParameters, final String dstkey) { + checkIsInMultiOrPipeline(); + client.sort(key, sortingParameters, dstkey); + return client.getIntegerReply(); + } + + /** + * Sort a Set or a List and Store the Result at dstkey. + *

+ * Sort the elements contained in the List, Set, or Sorted Set value at key and store the result + * at dstkey. By default sorting is numeric with elements being compared as double precision + * floating point numbers. This is the simplest form of SORT. + * @see #sort(String) + * @see #sort(String, SortingParams) + * @see #sort(String, SortingParams, String) + * @param key + * @param dstkey + * @return The number of elements of the list at dstkey. + */ + @Override + public Long sort(final String key, final String dstkey) { + checkIsInMultiOrPipeline(); + client.sort(key, dstkey); + return client.getIntegerReply(); + } + + /** + * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking + * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty + * lists. + *

+ * The following is a description of the exact semantic. We describe BLPOP but the two commands + * are identical, the only difference is that BLPOP pops the element from the left (head) of the + * list, and BRPOP pops from the right (tail). + *

+ * Non blocking behavior + *

+ * When BLPOP is called, if at least one of the specified keys contain a non empty list, an + * element is popped from the head of the list and returned to the caller together with the name + * of the key (BLPOP returns a two elements array, the first element is the key, the second the + * popped value). + *

+ * Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 + * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP + * guarantees to return an element from the list stored at list2 (since it is the first non empty + * list starting from the left). + *

+ * Blocking behavior + *

+ * If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other + * client performs a LPUSH or an RPUSH operation against one of the lists. + *

+ * Once new data is present on one of the lists, the client finally returns with the name of the + * key unblocking it and the popped value. + *

+ * When blocking, if a non-zero timeout is specified, the client will unblock returning a nil + * special value if the specified amount of seconds passed without a push operation against at + * least one of the specified keys. + *

+ * The timeout argument is interpreted as an integer value. A timeout of zero means instead to + * block forever. + *

+ * Multiple clients blocking for the same keys + *

+ * Multiple clients can block for the same key. They are put into a queue, so the first to be + * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. + *

+ * blocking POP inside a MULTI/EXEC transaction + *

+ * BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies + * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis + * transaction). + *

+ * The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil + * reply, exactly what happens when the timeout is reached. If you like science fiction, think at + * it like if inside MULTI/EXEC the time will flow at infinite speed :) + *

+ * Time complexity: O(1) + * @see #blpop(int, String...) + * @param timeout + * @param keys + * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the + * unblocking key and the popped value. + *

+ * When a non-zero timeout is specified, and the BLPOP operation timed out, the return + * value is a nil multi bulk reply. Most client values will return false or nil + * accordingly to the programming language used. + */ + @Override + public List brpop(final int timeout, final String... keys) { + return brpop(getArgsAddTimeout(timeout, keys)); + } + + @Override + public Long zcount(final String key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zcount(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Long zcount(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zcount(key, min, max); + return client.getIntegerReply(); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(String, double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(String, double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(String, double, double) + * @see #zrangeByScore(String, double, double, int, int) + * @see #zrangeByScoreWithScores(String, double, double) + * @see #zrangeByScoreWithScores(String, String, String) + * @see #zrangeByScoreWithScores(String, double, double, int, int) + * @see #zcount(String, double, double) + * @param key + * @param min a double or Double.NEGATIVE_INFINITY for "-inf" + * @param max a double or Double.POSITIVE_INFINITY for "+inf" + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScore(final String key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrangeByScore(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(String, double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(String, double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(String, double, double) + * @see #zrangeByScore(String, double, double, int, int) + * @see #zrangeByScoreWithScores(String, double, double) + * @see #zrangeByScoreWithScores(String, double, double, int, int) + * @see #zcount(String, double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScore(final String key, final double min, final double max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max, offset, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrangeByScore(final String key, final String min, final String max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScore(key, min, max, offset, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(String, double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(String, double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(String, double, double) + * @see #zrangeByScore(String, double, double, int, int) + * @see #zrangeByScoreWithScores(String, double, double) + * @see #zrangeByScoreWithScores(String, double, double, int, int) + * @see #zcount(String, double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScoreWithScores(final String key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max); + return getTupledSet(); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max); + return getTupledSet(); + } + + /** + * Return the all the elements in the sorted set at key with a score between min and max + * (including elements with score equal to min or max). + *

+ * The elements having the same score are returned sorted lexicographically as ASCII strings (this + * follows from a property of Redis sorted sets and does not involve further computation). + *

+ * Using the optional {@link #zrangeByScore(String, double, double, int, int) LIMIT} it's possible + * to get only a range of the matching elements in an SQL-alike way. Note that if offset is large + * the commands needs to traverse the list for offset elements and this adds up to the O(M) + * figure. + *

+ * The {@link #zcount(String, double, double) ZCOUNT} command is similar to + * {@link #zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the + * actual elements in the specified interval, it just returns the number of matching elements. + *

+ * Exclusive intervals and infinity + *

+ * min and max can be -inf and +inf, so that you are not required to know what's the greatest or + * smallest element in order to take, for instance, elements "up to a given value". + *

+ * Also while the interval is for default closed (inclusive) it's possible to specify open + * intervals prefixing the score with a "(" character, so for instance: + *

+ * {@code ZRANGEBYSCORE zset (1.3 5} + *

+ * Will return all the values with score > 1.3 and <= 5, while for instance: + *

+ * {@code ZRANGEBYSCORE zset (5 (10} + *

+ * Will return all the values with score > 5 and < 10 (5 and 10 excluded). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements returned by the command, so if M is constant (for instance you always ask for the + * first ten elements with LIMIT) you can consider it O(log(N)) + * @see #zrangeByScore(String, double, double) + * @see #zrangeByScore(String, double, double, int, int) + * @see #zrangeByScoreWithScores(String, double, double) + * @see #zrangeByScoreWithScores(String, double, double, int, int) + * @see #zcount(String, double, double) + * @param key + * @param min + * @param max + * @return Multi bulk reply specifically a list of elements in the specified score range. + */ + @Override + public Set zrangeByScoreWithScores(final String key, final double min, final double max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max, offset, count); + return getTupledSet(); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final String min, final String max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByScoreWithScores(key, min, max, offset, count); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScore(final String key, final double max, final double min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrevrangeByScore(final String key, final String max, final String min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrevrangeByScore(final String key, final double max, final double min, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min, offset, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final double max, final double min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final double max, + final double min, final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min, offset, count); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final String max, + final String min, final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min, offset, count); + return getTupledSet(); + } + + @Override + public Set zrevrangeByScore(final String key, final String max, final String min, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScore(key, max, min, offset, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final String max, final String min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByScoreWithScores(key, max, min); + return getTupledSet(); + } + + /** + * Remove all elements in the sorted set at key with rank between start and end. Start and end are + * 0-based with rank 0 being the element with the lowest score. Both start and end can be negative + * numbers, where they indicate offsets starting at the element with the highest rank. For + * example: -1 is the element with the highest score, -2 the element with the second highest score + * and so forth. + *

+ * Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set + * and M the number of elements removed by the operation + */ + @Override + public Long zremrangeByRank(final String key, final long start, final long stop) { + checkIsInMultiOrPipeline(); + client.zremrangeByRank(key, start, stop); + return client.getIntegerReply(); + } + + /** + * Remove all the elements in the sorted set at key with a score between min and max (including + * elements with score equal to min or max). + *

+ * Time complexity: + *

+ * O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of + * elements removed by the operation + * @param key + * @param min + * @param max + * @return Integer reply, specifically the number of elements removed. + */ + @Override + public Long zremrangeByScore(final String key, final double min, final double max) { + checkIsInMultiOrPipeline(); + client.zremrangeByScore(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Long zremrangeByScore(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zremrangeByScore(key, min, max); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(String, String...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The + * {@link #zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across all + * inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(String, String...) + * @see #zunionstore(String, ZParams, String...) + * @see #zinterstore(String, String...) + * @see #zinterstore(String, ZParams, String...) + * @param dstkey + * @param sets + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zunionstore(final String dstkey, final String... sets) { + checkIsInMultiOrPipeline(); + client.zunionstore(dstkey, sets); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(String, String...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The + * {@link #zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across all + * inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(String, String...) + * @see #zunionstore(String, ZParams, String...) + * @see #zinterstore(String, String...) + * @see #zinterstore(String, ZParams, String...) + * @param dstkey + * @param sets + * @param params + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zunionstore(final String dstkey, final ZParams params, final String... sets) { + checkIsInMultiOrPipeline(); + client.zunionstore(dstkey, params, sets); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(String, String...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The + * {@link #zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across all + * inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(String, String...) + * @see #zunionstore(String, ZParams, String...) + * @see #zinterstore(String, String...) + * @see #zinterstore(String, ZParams, String...) + * @param dstkey + * @param sets + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zinterstore(final String dstkey, final String... sets) { + checkIsInMultiOrPipeline(); + client.zinterstore(dstkey, sets); + return client.getIntegerReply(); + } + + /** + * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at + * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys + * and the other (optional) arguments. + *

+ * As the terms imply, the {@link #zinterstore(String, String...) ZINTERSTORE} command requires an + * element to be present in each of the given inputs to be inserted in the result. The + * {@link #zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across all + * inputs. + *

+ * Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means + * that the score of each element in the sorted set is first multiplied by this weight before + * being passed to the aggregation. When this option is not given, all weights default to 1. + *

+ * With the AGGREGATE option, it's possible to specify how the results of the union or + * intersection are aggregated. This option defaults to SUM, where the score of an element is + * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the + * resulting set will contain the minimum or maximum score of an element across the inputs where + * it exists. + *

+ * Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input + * sorted sets, and M being the number of elements in the resulting sorted set + * @see #zunionstore(String, String...) + * @see #zunionstore(String, ZParams, String...) + * @see #zinterstore(String, String...) + * @see #zinterstore(String, ZParams, String...) + * @param dstkey + * @param sets + * @param params + * @return Integer reply, specifically the number of elements in the sorted set at dstkey + */ + @Override + public Long zinterstore(final String dstkey, final ZParams params, final String... sets) { + checkIsInMultiOrPipeline(); + client.zinterstore(dstkey, params, sets); + return client.getIntegerReply(); + } + + @Override + public Long zlexcount(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zlexcount(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Set zrangeByLex(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zrangeByLex(key, min, max); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrangeByLex(final String key, final String min, final String max, + final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrangeByLex(key, min, max, offset, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrevrangeByLex(final String key, final String max, final String min) { + checkIsInMultiOrPipeline(); + client.zrevrangeByLex(key, max, min); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Set zrevrangeByLex(final String key, final String max, final String min, final int offset, final int count) { + checkIsInMultiOrPipeline(); + client.zrevrangeByLex(key, max, min, offset, count); + final List members = client.getMultiBulkReply(); + return SetFromList.of(members); + } + + @Override + public Long zremrangeByLex(final String key, final String min, final String max) { + checkIsInMultiOrPipeline(); + client.zremrangeByLex(key, min, max); + return client.getIntegerReply(); + } + + @Override + public Long strlen(final String key) { + checkIsInMultiOrPipeline(); + client.strlen(key); + return client.getIntegerReply(); + } + + @Override + public Long lpushx(final String key, final String... string) { + checkIsInMultiOrPipeline(); + client.lpushx(key, string); + return client.getIntegerReply(); + } + + /** + * Undo a {@link #expire(String, int) expire} at turning the expire key into a normal key. + *

+ * Time complexity: O(1) + * @param key + * @return Integer reply, specifically: 1: the key is now persist. 0: the key is not persist (only + * happens when key not set). + */ + @Override + public Long persist(final String key) { + client.persist(key); + return client.getIntegerReply(); + } + + @Override + public Long rpushx(final String key, final String... string) { + checkIsInMultiOrPipeline(); + client.rpushx(key, string); + return client.getIntegerReply(); + } + + @Override + public String echo(final String string) { + checkIsInMultiOrPipeline(); + client.echo(string); + return client.getBulkReply(); + } + + @Override + public Long linsert(final String key, final ListPosition where, final String pivot, + final String value) { + checkIsInMultiOrPipeline(); + client.linsert(key, where, pivot, value); + return client.getIntegerReply(); + } + + /** + * Pop a value from a list, push it to another list and return it; or block until one is available + * @param source + * @param destination + * @param timeout + * @return the element + */ + @Override + public String brpoplpush(final String source, final String destination, final int timeout) { + client.brpoplpush(source, destination, timeout); + client.setTimeoutInfinite(); + try { + return client.getBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + + /** + * Sets or clears the bit at offset in the string value stored at key + * @param key + * @param offset + * @param value + * @return + */ + @Override + public Boolean setbit(final String key, final long offset, final boolean value) { + checkIsInMultiOrPipeline(); + client.setbit(key, offset, value); + return client.getIntegerReply() == 1; + } + + @Override + public Boolean setbit(final String key, final long offset, final String value) { + checkIsInMultiOrPipeline(); + client.setbit(key, offset, value); + return client.getIntegerReply() == 1; + } + + /** + * Returns the bit value at offset in the string value stored at key + * @param key + * @param offset + * @return + */ + @Override + public Boolean getbit(final String key, final long offset) { + checkIsInMultiOrPipeline(); + client.getbit(key, offset); + return client.getIntegerReply() == 1; + } + + @Override + public Long setrange(final String key, final long offset, final String value) { + checkIsInMultiOrPipeline(); + client.setrange(key, offset, value); + return client.getIntegerReply(); + } + + @Override + public String getrange(final String key, final long startOffset, final long endOffset) { + checkIsInMultiOrPipeline(); + client.getrange(key, startOffset, endOffset); + return client.getBulkReply(); + } + + @Override + public Long bitpos(final String key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + @Override + public Long bitpos(final String key, final boolean value, final BitPosParams params) { + checkIsInMultiOrPipeline(); + client.bitpos(key, value, params); + return client.getIntegerReply(); + } + + /** + * Retrieve the configuration of a running Redis server. Not all the configuration parameters are + * supported. + *

+ * CONFIG GET returns the current configuration parameters. This sub command only accepts a single + * argument, that is glob style pattern. All the configuration parameters matching this parameter + * are reported as a list of key-value pairs. + *

+ * Example: + * + *

+   * $ redis-cli config get '*'
+   * 1. "dbfilename"
+   * 2. "dump.rdb"
+   * 3. "requirepass"
+   * 4. (nil)
+   * 5. "masterauth"
+   * 6. (nil)
+   * 7. "maxmemory"
+   * 8. "0\n"
+   * 9. "appendfsync"
+   * 10. "everysec"
+   * 11. "save"
+   * 12. "3600 1 300 100 60 10000"
+   * 
+   * $ redis-cli config get 'm*'
+   * 1. "masterauth"
+   * 2. (nil)
+   * 3. "maxmemory"
+   * 4. "0\n"
+   * 
+ * @param pattern + * @return Bulk reply. + */ + @Override + public List configGet(final String pattern) { + client.configGet(pattern); + return client.getMultiBulkReply(); + } + + /** + * Alter the configuration of a running Redis server. Not all the configuration parameters are + * supported. + *

+ * The list of configuration parameters supported by CONFIG SET can be obtained issuing a + * {@link #configGet(String) CONFIG GET *} command. + *

+ * The configuration set using CONFIG SET is immediately loaded by the Redis server that will + * start acting as specified starting from the next command. + *

+ * Parameters value format + *

+ * The value of the configuration parameter is the same as the one of the same parameter in the + * Redis configuration file, with the following exceptions: + *

+ *

    + *
  • The save paramter is a list of space-separated integers. Every pair of integers specify the + * time and number of changes limit to trigger a save. For instance the command CONFIG SET save + * "3600 10 60 10000" will configure the server to issue a background saving of the RDB file every + * 3600 seconds if there are at least 10 changes in the dataset, and every 60 seconds if there are + * at least 10000 changes. To completely disable automatic snapshots just set the parameter as an + * empty string. + *
  • All the integer parameters representing memory are returned and accepted only using bytes + * as unit. + *
+ * @param parameter + * @param value + * @return Status code reply + */ + @Override + public String configSet(final String parameter, final String value) { + client.configSet(parameter, value); + return client.getStatusCodeReply(); + } + + @Override + public Object eval(final String script, final int keyCount, final String... params) { + client.setTimeoutInfinite(); + try { + client.eval(script, keyCount, params); + return getEvalResult(); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public void subscribe(final JedisPubSub jedisPubSub, final String... channels) { + client.setTimeoutInfinite(); + try { + jedisPubSub.proceed(client, channels); + } finally { + client.rollbackTimeout(); + } + } + + @Override + public Long publish(final String channel, final String message) { + checkIsInMultiOrPipeline(); + connect(); + client.publish(channel, message); + return client.getIntegerReply(); + } + + @Override + public void psubscribe(final JedisPubSub jedisPubSub, final String... patterns) { + checkIsInMultiOrPipeline(); + client.setTimeoutInfinite(); + try { + jedisPubSub.proceedWithPatterns(client, patterns); + } finally { + client.rollbackTimeout(); + } + } + + protected static String[] getParams(List keys, List args) { + int keyCount = keys.size(); + int argCount = args.size(); + + String[] params = new String[keyCount + args.size()]; + + for (int i = 0; i < keyCount; i++) + params[i] = keys.get(i); + + for (int i = 0; i < argCount; i++) + params[keyCount + i] = args.get(i); + + return params; + } + + @Override + public Object eval(final String script, final List keys, final List args) { + return eval(script, keys.size(), getParams(keys, args)); + } + + @Override + public Object eval(final String script) { + return eval(script, 0); + } + + @Override + public Object evalsha(final String sha1) { + return evalsha(sha1, 0); + } + + private Object getEvalResult() { + return evalResult(client.getOne()); + } + + private Object evalResult(Object result) { + if (result instanceof byte[]) return SafeEncoder.encode((byte[]) result); + + if (result instanceof List) { + List list = (List) result; + List listResult = new ArrayList(list.size()); + for (Object bin : list) { + listResult.add(evalResult(bin)); + } + + return listResult; + } + + return result; + } + + @Override + public Object evalsha(final String sha1, final List keys, final List args) { + return evalsha(sha1, keys.size(), getParams(keys, args)); + } + + @Override + public Object evalsha(final String sha1, final int keyCount, final String... params) { + checkIsInMultiOrPipeline(); + client.evalsha(sha1, keyCount, params); + return getEvalResult(); + } + + @Override + public Boolean scriptExists(final String sha1) { + String[] a = new String[1]; + a[0] = sha1; + return scriptExists(a).get(0); + } + + @Override + public List scriptExists(final String... sha1) { + client.scriptExists(sha1); + List result = client.getIntegerMultiBulkReply(); + List exists = new ArrayList(); + + for (Long value : result) + exists.add(value == 1); + + return exists; + } + + @Override + public String scriptLoad(final String script) { + client.scriptLoad(script); + return client.getBulkReply(); + } + + @Override + public List slowlogGet() { + client.slowlogGet(); + return Slowlog.from(client.getObjectMultiBulkReply()); + } + + @Override + public List slowlogGet(final long entries) { + client.slowlogGet(entries); + return Slowlog.from(client.getObjectMultiBulkReply()); + } + + @Override + public Long objectRefcount(final String key) { + client.objectRefcount(key); + return client.getIntegerReply(); + } + + @Override + public String objectEncoding(final String key) { + client.objectEncoding(key); + return client.getBulkReply(); + } + + @Override + public Long objectIdletime(final String key) { + client.objectIdletime(key); + return client.getIntegerReply(); + } + + @Override + public Long bitcount(final String key) { + checkIsInMultiOrPipeline(); + client.bitcount(key); + return client.getIntegerReply(); + } + + @Override + public Long bitcount(final String key, final long start, final long end) { + checkIsInMultiOrPipeline(); + client.bitcount(key, start, end); + return client.getIntegerReply(); + } + + @Override + public Long bitop(final BitOP op, final String destKey, final String... srcKeys) { + checkIsInMultiOrPipeline(); + client.bitop(op, destKey, srcKeys); + return client.getIntegerReply(); + } + + /** + *
+   * redis 127.0.0.1:26381> sentinel masters
+   * 1)  1) "name"
+   *     2) "mymaster"
+   *     3) "ip"
+   *     4) "127.0.0.1"
+   *     5) "port"
+   *     6) "6379"
+   *     7) "runid"
+   *     8) "93d4d4e6e9c06d0eea36e27f31924ac26576081d"
+   *     9) "flags"
+   *    10) "master"
+   *    11) "pending-commands"
+   *    12) "0"
+   *    13) "last-ok-ping-reply"
+   *    14) "423"
+   *    15) "last-ping-reply"
+   *    16) "423"
+   *    17) "info-refresh"
+   *    18) "6107"
+   *    19) "num-slaves"
+   *    20) "1"
+   *    21) "num-other-sentinels"
+   *    22) "2"
+   *    23) "quorum"
+   *    24) "2"
+   * 
+   * 
+ * @return + */ + @Override + @SuppressWarnings("rawtypes") + public List> sentinelMasters() { + client.sentinel(Protocol.SENTINEL_MASTERS); + final List reply = client.getObjectMultiBulkReply(); + + final List> masters = new ArrayList>(); + for (Object obj : reply) { + masters.add(BuilderFactory.STRING_MAP.build((List) obj)); + } + return masters; + } + + /** + *
+   * redis 127.0.0.1:26381> sentinel get-master-addr-by-name mymaster
+   * 1) "127.0.0.1"
+   * 2) "6379"
+   * 
+ * @param masterName + * @return two elements list of strings : host and port. + */ + @Override + public List sentinelGetMasterAddrByName(final String masterName) { + client.sentinel(Protocol.SENTINEL_GET_MASTER_ADDR_BY_NAME, masterName); + final List reply = client.getObjectMultiBulkReply(); + return BuilderFactory.STRING_LIST.build(reply); + } + + /** + *
+   * redis 127.0.0.1:26381> sentinel reset mymaster
+   * (integer) 1
+   * 
+ * @param pattern + * @return + */ + @Override + public Long sentinelReset(final String pattern) { + client.sentinel(Protocol.SENTINEL_RESET, pattern); + return client.getIntegerReply(); + } + + /** + *
+   * redis 127.0.0.1:26381> sentinel slaves mymaster
+   * 1)  1) "name"
+   *     2) "127.0.0.1:6380"
+   *     3) "ip"
+   *     4) "127.0.0.1"
+   *     5) "port"
+   *     6) "6380"
+   *     7) "runid"
+   *     8) "d7f6c0ca7572df9d2f33713df0dbf8c72da7c039"
+   *     9) "flags"
+   *    10) "slave"
+   *    11) "pending-commands"
+   *    12) "0"
+   *    13) "last-ok-ping-reply"
+   *    14) "47"
+   *    15) "last-ping-reply"
+   *    16) "47"
+   *    17) "info-refresh"
+   *    18) "657"
+   *    19) "master-link-down-time"
+   *    20) "0"
+   *    21) "master-link-status"
+   *    22) "ok"
+   *    23) "master-host"
+   *    24) "localhost"
+   *    25) "master-port"
+   *    26) "6379"
+   *    27) "slave-priority"
+   *    28) "100"
+   * 
+ * @param masterName + * @return + */ + @Override + @SuppressWarnings("rawtypes") + public List> sentinelSlaves(final String masterName) { + client.sentinel(Protocol.SENTINEL_SLAVES, masterName); + final List reply = client.getObjectMultiBulkReply(); + + final List> slaves = new ArrayList>(); + for (Object obj : reply) { + slaves.add(BuilderFactory.STRING_MAP.build((List) obj)); + } + return slaves; + } + + @Override + public String sentinelFailover(final String masterName) { + client.sentinel(Protocol.SENTINEL_FAILOVER, masterName); + return client.getStatusCodeReply(); + } + + @Override + public String sentinelMonitor(final String masterName, final String ip, final int port, final int quorum) { + client.sentinel(Protocol.SENTINEL_MONITOR, masterName, ip, String.valueOf(port), + String.valueOf(quorum)); + return client.getStatusCodeReply(); + } + + @Override + public String sentinelRemove(final String masterName) { + client.sentinel(Protocol.SENTINEL_REMOVE, masterName); + return client.getStatusCodeReply(); + } + + @Override + public String sentinelSet(final String masterName, final Map parameterMap) { + int index = 0; + int paramsLength = parameterMap.size() * 2 + 2; + String[] params = new String[paramsLength]; + + params[index++] = Protocol.SENTINEL_SET; + params[index++] = masterName; + for (Entry entry : parameterMap.entrySet()) { + params[index++] = entry.getKey(); + params[index++] = entry.getValue(); + } + + client.sentinel(params); + return client.getStatusCodeReply(); + } + + @Override + public byte[] dump(final String key) { + checkIsInMultiOrPipeline(); + client.dump(key); + return client.getBinaryBulkReply(); + } + + @Override + public String restore(final String key, final int ttl, final byte[] serializedValue) { + checkIsInMultiOrPipeline(); + client.restore(key, ttl, serializedValue); + return client.getStatusCodeReply(); + } + + @Override + public Long pexpire(final String key, final long milliseconds) { + checkIsInMultiOrPipeline(); + client.pexpire(key, milliseconds); + return client.getIntegerReply(); + } + + @Override + public Long pexpireAt(final String key, final long millisecondsTimestamp) { + checkIsInMultiOrPipeline(); + client.pexpireAt(key, millisecondsTimestamp); + return client.getIntegerReply(); + } + + @Override + public Long pttl(final String key) { + checkIsInMultiOrPipeline(); + client.pttl(key); + return client.getIntegerReply(); + } + + /** + * PSETEX works exactly like {@link #setex(String, int, String)} with the sole difference that the + * expire time is specified in milliseconds instead of seconds. Time complexity: O(1) + * @param key + * @param milliseconds + * @param value + * @return Status code reply + */ + + @Override + public String psetex(final String key, final long milliseconds, final String value) { + checkIsInMultiOrPipeline(); + client.psetex(key, milliseconds, value); + return client.getStatusCodeReply(); + } + + public String clientKill(final String client) { + checkIsInMultiOrPipeline(); + this.client.clientKill(client); + return this.client.getStatusCodeReply(); + } + + public String clientSetname(final String name) { + checkIsInMultiOrPipeline(); + client.clientSetname(name); + return client.getStatusCodeReply(); + } + + public String migrate(final String host, final int port, final String key, + final int destinationDb, final int timeout) { + checkIsInMultiOrPipeline(); + client.migrate(host, port, key, destinationDb, timeout); + return client.getStatusCodeReply(); + } + + @Override + public ScanResult scan(final String cursor) { + return scan(cursor, new ScanParams()); + } + + @Override + public ScanResult scan(final String cursor, final ScanParams params) { + checkIsInMultiOrPipeline(); + client.scan(cursor, params); + List result = client.getObjectMultiBulkReply(); + String newcursor = new String((byte[]) result.get(0)); + List results = new ArrayList(); + List rawResults = (List) result.get(1); + for (byte[] bs : rawResults) { + results.add(SafeEncoder.encode(bs)); + } + return new ScanResult(newcursor, results); + } + + @Override + public ScanResult> hscan(final String key, final String cursor) { + return hscan(key, cursor, new ScanParams()); + } + + @Override + public ScanResult> hscan(final String key, final String cursor, + final ScanParams params) { + checkIsInMultiOrPipeline(); + client.hscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + String newcursor = new String((byte[]) result.get(0)); + List> results = new ArrayList>(); + List rawResults = (List) result.get(1); + Iterator iterator = rawResults.iterator(); + while (iterator.hasNext()) { + results.add(new AbstractMap.SimpleEntry(SafeEncoder.encode(iterator.next()), + SafeEncoder.encode(iterator.next()))); + } + return new ScanResult>(newcursor, results); + } + + @Override + public ScanResult sscan(final String key, final String cursor) { + return sscan(key, cursor, new ScanParams()); + } + + @Override + public ScanResult sscan(final String key, final String cursor, final ScanParams params) { + checkIsInMultiOrPipeline(); + client.sscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + String newcursor = new String((byte[]) result.get(0)); + List results = new ArrayList(); + List rawResults = (List) result.get(1); + for (byte[] bs : rawResults) { + results.add(SafeEncoder.encode(bs)); + } + return new ScanResult(newcursor, results); + } + + @Override + public ScanResult zscan(final String key, final String cursor) { + return zscan(key, cursor, new ScanParams()); + } + + @Override + public ScanResult zscan(final String key, final String cursor, final ScanParams params) { + checkIsInMultiOrPipeline(); + client.zscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + String newcursor = new String((byte[]) result.get(0)); + List results = new ArrayList(); + List rawResults = (List) result.get(1); + Iterator iterator = rawResults.iterator(); + while (iterator.hasNext()) { + results.add(new Tuple(iterator.next(), BuilderFactory.DOUBLE.build(iterator.next()))); + } + return new ScanResult(newcursor, results); + } + + @Override + public String clusterNodes() { + checkIsInMultiOrPipeline(); + client.clusterNodes(); + return client.getBulkReply(); + } + + @Override + public String readonly() { + client.readonly(); + return client.getStatusCodeReply(); + } + + @Override + public String clusterMeet(final String ip, final int port) { + checkIsInMultiOrPipeline(); + client.clusterMeet(ip, port); + return client.getStatusCodeReply(); + } + + @Override + public String clusterReset(final ClusterReset resetType) { + checkIsInMultiOrPipeline(); + client.clusterReset(resetType); + return client.getStatusCodeReply(); + } + + @Override + public String clusterAddSlots(final int... slots) { + checkIsInMultiOrPipeline(); + client.clusterAddSlots(slots); + return client.getStatusCodeReply(); + } + + @Override + public String clusterDelSlots(final int... slots) { + checkIsInMultiOrPipeline(); + client.clusterDelSlots(slots); + return client.getStatusCodeReply(); + } + + @Override + public String clusterInfo() { + checkIsInMultiOrPipeline(); + client.clusterInfo(); + return client.getStatusCodeReply(); + } + + @Override + public List clusterGetKeysInSlot(final int slot, final int count) { + checkIsInMultiOrPipeline(); + client.clusterGetKeysInSlot(slot, count); + return client.getMultiBulkReply(); + } + + @Override + public String clusterSetSlotNode(final int slot, final String nodeId) { + checkIsInMultiOrPipeline(); + client.clusterSetSlotNode(slot, nodeId); + return client.getStatusCodeReply(); + } + + @Override + public String clusterSetSlotMigrating(final int slot, final String nodeId) { + checkIsInMultiOrPipeline(); + client.clusterSetSlotMigrating(slot, nodeId); + return client.getStatusCodeReply(); + } + + @Override + public String clusterSetSlotImporting(final int slot, final String nodeId) { + checkIsInMultiOrPipeline(); + client.clusterSetSlotImporting(slot, nodeId); + return client.getStatusCodeReply(); + } + + @Override + public String clusterSetSlotStable(final int slot) { + checkIsInMultiOrPipeline(); + client.clusterSetSlotStable(slot); + return client.getStatusCodeReply(); + } + + @Override + public String clusterForget(final String nodeId) { + checkIsInMultiOrPipeline(); + client.clusterForget(nodeId); + return client.getStatusCodeReply(); + } + + @Override + public String clusterFlushSlots() { + checkIsInMultiOrPipeline(); + client.clusterFlushSlots(); + return client.getStatusCodeReply(); + } + + @Override + public Long clusterKeySlot(final String key) { + checkIsInMultiOrPipeline(); + client.clusterKeySlot(key); + return client.getIntegerReply(); + } + + @Override + public Long clusterCountKeysInSlot(final int slot) { + checkIsInMultiOrPipeline(); + client.clusterCountKeysInSlot(slot); + return client.getIntegerReply(); + } + + @Override + public String clusterSaveConfig() { + checkIsInMultiOrPipeline(); + client.clusterSaveConfig(); + return client.getStatusCodeReply(); + } + + @Override + public String clusterReplicate(final String nodeId) { + checkIsInMultiOrPipeline(); + client.clusterReplicate(nodeId); + return client.getStatusCodeReply(); + } + + @Override + public List clusterSlaves(final String nodeId) { + checkIsInMultiOrPipeline(); + client.clusterSlaves(nodeId); + return client.getMultiBulkReply(); + } + + @Override + public String clusterFailover() { + checkIsInMultiOrPipeline(); + client.clusterFailover(); + return client.getStatusCodeReply(); + } + + @Override + public List clusterSlots() { + checkIsInMultiOrPipeline(); + client.clusterSlots(); + return client.getObjectMultiBulkReply(); + } + + public String asking() { + checkIsInMultiOrPipeline(); + client.asking(); + return client.getStatusCodeReply(); + } + + public List pubsubChannels(final String pattern) { + checkIsInMultiOrPipeline(); + client.pubsubChannels(pattern); + return client.getMultiBulkReply(); + } + + public Long pubsubNumPat() { + checkIsInMultiOrPipeline(); + client.pubsubNumPat(); + return client.getIntegerReply(); + } + + public Map pubsubNumSub(String... channels) { + checkIsInMultiOrPipeline(); + client.pubsubNumSub(channels); + return BuilderFactory.PUBSUB_NUMSUB_MAP.build(client.getBinaryMultiBulkReply()); + } + + @Override + public void close() { + if (dataSource != null) { + if (client.isBroken()) { + this.dataSource.returnBrokenResource(this); + } else { + this.dataSource.returnResource(this); + } + } else { + client.close(); + } + } + + public void setDataSource(JedisPoolAbstract jedisPool) { + this.dataSource = jedisPool; + } + + @Override + public Long pfadd(final String key, final String... elements) { + checkIsInMultiOrPipeline(); + client.pfadd(key, elements); + return client.getIntegerReply(); + } + + @Override + public long pfcount(final String key) { + checkIsInMultiOrPipeline(); + client.pfcount(key); + return client.getIntegerReply(); + } + + @Override + public long pfcount(final String... keys) { + checkIsInMultiOrPipeline(); + client.pfcount(keys); + return client.getIntegerReply(); + } + + @Override + public String pfmerge(final String destkey, final String... sourcekeys) { + checkIsInMultiOrPipeline(); + client.pfmerge(destkey, sourcekeys); + return client.getStatusCodeReply(); + } + + @Override + public List blpop(final int timeout, final String key) { + return blpop(key, String.valueOf(timeout)); + } + + @Override + public List brpop(final int timeout, final String key) { + return brpop(key, String.valueOf(timeout)); + } + + @Override + public Long geoadd(final String key, final double longitude, final double latitude, final String member) { + checkIsInMultiOrPipeline(); + client.geoadd(key, longitude, latitude, member); + return client.getIntegerReply(); + } + + @Override + public Long geoadd(final String key, final Map memberCoordinateMap) { + checkIsInMultiOrPipeline(); + client.geoadd(key, memberCoordinateMap); + return client.getIntegerReply(); + } + + @Override + public Double geodist(final String key, final String member1, final String member2) { + checkIsInMultiOrPipeline(); + client.geodist(key, member1, member2); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + @Override + public Double geodist(final String key, final String member1, final String member2, final GeoUnit unit) { + checkIsInMultiOrPipeline(); + client.geodist(key, member1, member2, unit); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + + @Override + public List geohash(final String key, String... members) { + checkIsInMultiOrPipeline(); + client.geohash(key, members); + return client.getMultiBulkReply(); + } + + @Override + public List geopos(final String key, String... members) { + checkIsInMultiOrPipeline(); + client.geopos(key, members); + return BuilderFactory.GEO_COORDINATE_LIST.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadius(final String key, final double longitude, final double latitude, + final double radius, final GeoUnit unit) { + checkIsInMultiOrPipeline(); + client.georadius(key, longitude, latitude, radius, unit); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadius(final String key, final double longitude, final double latitude, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + checkIsInMultiOrPipeline(); + client.georadius(key, longitude, latitude, radius, unit, param); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadiusByMember(final String key, final String member, final double radius, + final GeoUnit unit) { + checkIsInMultiOrPipeline(); + client.georadiusByMember(key, member, radius, unit); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public List georadiusByMember(final String key, final String member, final double radius, + final GeoUnit unit, final GeoRadiusParam param) { + checkIsInMultiOrPipeline(); + client.georadiusByMember(key, member, radius, unit, param); + return BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT.build(client.getObjectMultiBulkReply()); + } + + @Override + public String moduleLoad(final String path) { + client.moduleLoad(path); + return client.getStatusCodeReply(); + } + + @Override + public String moduleUnload(final String name) { + client.moduleUnload(name); + return client.getStatusCodeReply(); + } + + @Override + public List moduleList() { + client.moduleList(); + return BuilderFactory.MODULE_LIST.build(client.getObjectMultiBulkReply()); + } + + @Override + public List bitfield(final String key, final String...arguments) { + checkIsInMultiOrPipeline(); + client.bitfield(key, arguments); + return client.getIntegerMultiBulkReply(); + } + + @Override + public Long hstrlen(final String key, final String field) { + checkIsInMultiOrPipeline(); + client.hstrlen(key, field); + return client.getIntegerReply(); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisCluster.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisCluster.java new file mode 100755 index 000000000..500f96acc --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisCluster.java @@ -0,0 +1,1979 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.commands.JedisClusterCommands; +import com.fr.third.redis.clients.jedis.commands.JedisClusterScriptingCommands; +import com.fr.third.redis.clients.jedis.commands.MultiKeyJedisClusterCommands; +import com.fr.third.redis.clients.jedis.util.KeyMergeUtil; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.util.JedisClusterHashTagUtil; + +public class JedisCluster extends BinaryJedisCluster implements JedisClusterCommands, + MultiKeyJedisClusterCommands, JedisClusterScriptingCommands { + + public JedisCluster(HostAndPort node) { + this(Collections.singleton(node)); + } + + public JedisCluster(HostAndPort node, int timeout) { + this(Collections.singleton(node), timeout); + } + + public JedisCluster(HostAndPort node, int timeout, int maxAttempts) { + this(Collections.singleton(node), timeout, maxAttempts); + } + + public JedisCluster(HostAndPort node, final GenericObjectPoolConfig poolConfig) { + this(Collections.singleton(node), poolConfig); + } + + public JedisCluster(HostAndPort node, int timeout, final GenericObjectPoolConfig poolConfig) { + this(Collections.singleton(node), timeout, poolConfig); + } + + public JedisCluster(HostAndPort node, int timeout, int maxAttempts, + final GenericObjectPoolConfig poolConfig) { + this(Collections.singleton(node), timeout, maxAttempts, poolConfig); + } + + public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, + int maxAttempts, final GenericObjectPoolConfig poolConfig) { + this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, poolConfig); + } + + public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, + int maxAttempts, String password, final GenericObjectPoolConfig poolConfig) { + this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, poolConfig); + } + + public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, + int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig) { + this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig); + } + + public JedisCluster(Set nodes) { + this(nodes, DEFAULT_TIMEOUT); + } + + public JedisCluster(Set nodes, int timeout) { + this(nodes, timeout, DEFAULT_MAX_ATTEMPTS); + } + + public JedisCluster(Set nodes, int timeout, int maxAttempts) { + this(nodes, timeout, maxAttempts, new GenericObjectPoolConfig()); + } + + public JedisCluster(Set nodes, final GenericObjectPoolConfig poolConfig) { + this(nodes, DEFAULT_TIMEOUT, DEFAULT_MAX_ATTEMPTS, poolConfig); + } + + public JedisCluster(Set nodes, int timeout, final GenericObjectPoolConfig poolConfig) { + this(nodes, timeout, DEFAULT_MAX_ATTEMPTS, poolConfig); + } + + public JedisCluster(Set jedisClusterNode, int timeout, int maxAttempts, + final GenericObjectPoolConfig poolConfig) { + super(jedisClusterNode, timeout, maxAttempts, poolConfig); + } + + public JedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, + int maxAttempts, final GenericObjectPoolConfig poolConfig) { + super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, poolConfig); + } + + public JedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, + int maxAttempts, String password, final GenericObjectPoolConfig poolConfig) { + super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, poolConfig); + } + + public JedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, + int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig) { + super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig); +} + + @Override + public String set(final String key, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.set(key, value); + } + }.run(key); + } + + @Override + public String set(final String key, final String value, final SetParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.set(key, value, params); + } + }.run(key); + } + + @Override + public String get(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.get(key); + } + }.run(key); + } + + @Override + public Boolean exists(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.exists(key); + } + }.run(key); + } + + @Override + public Long exists(final String... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.exists(keys); + } + }.run(keys.length, keys); + } + + @Override + public Long persist(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.persist(key); + } + }.run(key); + } + + @Override + public String type(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.type(key); + } + }.run(key); + } + + @Override + public byte[] dump(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.dump(key); + } + }.run(key); + } + + @Override + public String restore(final String key, final int ttl, final byte[] serializedValue) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.restore(key, ttl, serializedValue); + } + }.run(key); + } + + @Override + public Long expire(final String key, final int seconds) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.expire(key, seconds); + } + }.run(key); + } + + @Override + public Long pexpire(final String key, final long milliseconds) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pexpire(key, milliseconds); + } + }.run(key); + } + + @Override + public Long expireAt(final String key, final long unixTime) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.expireAt(key, unixTime); + } + }.run(key); + } + + @Override + public Long pexpireAt(final String key, final long millisecondsTimestamp) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pexpireAt(key, millisecondsTimestamp); + } + }.run(key); + } + + @Override + public Long ttl(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.ttl(key); + } + }.run(key); + } + + @Override + public Long pttl(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pttl(key); + } + }.run(key); + } + + @Override + public Long touch(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.touch(key); + } + }.run(key); + } + + @Override + public Long touch(final String... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.touch(keys); + } + }.run(keys.length, keys); + } + + @Override + public Boolean setbit(final String key, final long offset, final boolean value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.setbit(key, offset, value); + } + }.run(key); + } + + @Override + public Boolean setbit(final String key, final long offset, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.setbit(key, offset, value); + } + }.run(key); + } + + @Override + public Boolean getbit(final String key, final long offset) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.getbit(key, offset); + } + }.run(key); + } + + @Override + public Long setrange(final String key, final long offset, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.setrange(key, offset, value); + } + }.run(key); + } + + @Override + public String getrange(final String key, final long startOffset, final long endOffset) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.getrange(key, startOffset, endOffset); + } + }.run(key); + } + + @Override + public String getSet(final String key, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.getSet(key, value); + } + }.run(key); + } + + @Override + public Long setnx(final String key, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.setnx(key, value); + } + }.run(key); + } + + @Override + public String setex(final String key, final int seconds, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.setex(key, seconds, value); + } + }.run(key); + } + + @Override + public String psetex(final String key, final long milliseconds, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.psetex(key, milliseconds, value); + } + }.run(key); + } + + @Override + public Long decrBy(final String key, final long decrement) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.decrBy(key, decrement); + } + }.run(key); + } + + @Override + public Long decr(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.decr(key); + } + }.run(key); + } + + @Override + public Long incrBy(final String key, final long increment) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.incrBy(key, increment); + } + }.run(key); + } + + @Override + public Double incrByFloat(final String key, final double increment) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.incrByFloat(key, increment); + } + }.run(key); + } + + @Override + public Long incr(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.incr(key); + } + }.run(key); + } + + @Override + public Long append(final String key, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.append(key, value); + } + }.run(key); + } + + @Override + public String substr(final String key, final int start, final int end) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.substr(key, start, end); + } + }.run(key); + } + + @Override + public Long hset(final String key, final String field, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hset(key, field, value); + } + }.run(key); + } + + @Override + public Long hset(final String key, final Map hash) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hset(key, hash); + } + }.run(key); + } + + @Override + public String hget(final String key, final String field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.hget(key, field); + } + }.run(key); + } + + @Override + public Long hsetnx(final String key, final String field, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hsetnx(key, field, value); + } + }.run(key); + } + + @Override + public String hmset(final String key, final Map hash) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.hmset(key, hash); + } + }.run(key); + } + + @Override + public List hmget(final String key, final String... fields) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.hmget(key, fields); + } + }.run(key); + } + + @Override + public Long hincrBy(final String key, final String field, final long value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hincrBy(key, field, value); + } + }.run(key); + } + + @Override + public Boolean hexists(final String key, final String field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.hexists(key, field); + } + }.run(key); + } + + @Override + public Long hdel(final String key, final String... field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hdel(key, field); + } + }.run(key); + } + + @Override + public Long hlen(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hlen(key); + } + }.run(key); + } + + @Override + public Set hkeys(final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.hkeys(key); + } + }.run(key); + } + + @Override + public List hvals(final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.hvals(key); + } + }.run(key); + } + + @Override + public Map hgetAll(final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Map execute(Jedis connection) { + return connection.hgetAll(key); + } + }.run(key); + } + + @Override + public Long rpush(final String key, final String... string) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.rpush(key, string); + } + }.run(key); + } + + @Override + public Long lpush(final String key, final String... string) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.lpush(key, string); + } + }.run(key); + } + + @Override + public Long llen(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.llen(key); + } + }.run(key); + } + + @Override + public List lrange(final String key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.lrange(key, start, stop); + } + }.run(key); + } + + @Override + public String ltrim(final String key, final long start, final long stop) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.ltrim(key, start, stop); + } + }.run(key); + } + + @Override + public String lindex(final String key, final long index) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.lindex(key, index); + } + }.run(key); + } + + @Override + public String lset(final String key, final long index, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.lset(key, index, value); + } + }.run(key); + } + + @Override + public Long lrem(final String key, final long count, final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.lrem(key, count, value); + } + }.run(key); + } + + @Override + public String lpop(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.lpop(key); + } + }.run(key); + } + + @Override + public String rpop(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.rpop(key); + } + }.run(key); + } + + @Override + public Long sadd(final String key, final String... member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sadd(key, member); + } + }.run(key); + } + + @Override + public Set smembers(final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.smembers(key); + } + }.run(key); + } + + @Override + public Long srem(final String key, final String... member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.srem(key, member); + } + }.run(key); + } + + @Override + public String spop(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.spop(key); + } + }.run(key); + } + + @Override + public Set spop(final String key, final long count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.spop(key, count); + } + }.run(key); + } + + @Override + public Long scard(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.scard(key); + } + }.run(key); + } + + @Override + public Boolean sismember(final String key, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.sismember(key, member); + } + }.run(key); + } + + @Override + public String srandmember(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.srandmember(key); + } + }.run(key); + } + + @Override + public List srandmember(final String key, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.srandmember(key, count); + } + }.run(key); + } + + @Override + public Long strlen(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.strlen(key); + } + }.run(key); + } + + @Override + public Long zadd(final String key, final double score, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, score, member); + } + }.run(key); + } + + @Override + public Long zadd(final String key, final double score, final String member, + final ZAddParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, score, member, params); + } + }.run(key); + } + + @Override + public Long zadd(final String key, final Map scoreMembers) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, scoreMembers); + } + }.run(key); + } + + @Override + public Long zadd(final String key, final Map scoreMembers, final ZAddParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zadd(key, scoreMembers, params); + } + }.run(key); + } + + @Override + public Set zrange(final String key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrange(key, start, stop); + } + }.run(key); + } + + @Override + public Long zrem(final String key, final String... members) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zrem(key, members); + } + }.run(key); + } + + @Override + public Double zincrby(final String key, final double increment, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.zincrby(key, increment, member); + } + }.run(key); + } + + @Override + public Double zincrby(final String key, final double increment, final String member, + final ZIncrByParams params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.zincrby(key, increment, member, params); + } + }.run(key); + } + + @Override + public Long zrank(final String key, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zrank(key, member); + } + }.run(key); + } + + @Override + public Long zrevrank(final String key, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zrevrank(key, member); + } + }.run(key); + } + + @Override + public Set zrevrange(final String key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrange(key, start, stop); + } + }.run(key); + } + + @Override + public Set zrangeWithScores(final String key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeWithScores(key, start, stop); + } + }.run(key); + } + + @Override + public Set zrevrangeWithScores(final String key, final long start, final long stop) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeWithScores(key, start, stop); + } + }.run(key); + } + + @Override + public Long zcard(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zcard(key); + } + }.run(key); + } + + @Override + public Double zscore(final String key, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.zscore(key, member); + } + }.run(key); + } + + @Override + public List sort(final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.sort(key); + } + }.run(key); + } + + @Override + public List sort(final String key, final SortingParams sortingParameters) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.sort(key, sortingParameters); + } + }.run(key); + } + + @Override + public Long zcount(final String key, final double min, final double max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zcount(key, min, max); + } + }.run(key); + } + + @Override + public Long zcount(final String key, final String min, final String max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zcount(key, min, max); + } + }.run(key); + } + + @Override + public Set zrangeByScore(final String key, final double min, final double max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max); + } + }.run(key); + } + + @Override + public Set zrangeByScore(final String key, final String min, final String max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max); + } + }.run(key); + } + + @Override + public Set zrevrangeByScore(final String key, final double max, final double min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min); + } + }.run(key); + } + + @Override + public Set zrangeByScore(final String key, final double min, final double max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max, offset, count); + } + }.run(key); + } + + @Override + public Set zrevrangeByScore(final String key, final String max, final String min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min); + } + }.run(key); + } + + @Override + public Set zrangeByScore(final String key, final String min, final String max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScore(key, min, max, offset, count); + } + }.run(key); + } + + @Override + public Set zrevrangeByScore(final String key, final double max, final double min, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min, offset, count); + } + }.run(key); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final double min, final double max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max); + } + }.run(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final double max, final double min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min); + } + }.run(key); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final double min, final double max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max, offset, count); + } + }.run(key); + } + + @Override + public Set zrevrangeByScore(final String key, final String max, final String min, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScore(key, max, min, offset, count); + } + }.run(key); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final String min, final String max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max); + } + }.run(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final String max, final String min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min); + } + }.run(key); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final String min, final String max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByScoreWithScores(key, min, max, offset, count); + } + }.run(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final double max, + final double min, final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + }.run(key); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final String max, + final String min, final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + }.run(key); + } + + @Override + public Long zremrangeByRank(final String key, final long start, final long stop) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByRank(key, start, stop); + } + }.run(key); + } + + @Override + public Long zremrangeByScore(final String key, final double min, final double max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByScore(key, min, max); + } + }.run(key); + } + + @Override + public Long zremrangeByScore(final String key, final String min, final String max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByScore(key, min, max); + } + }.run(key); + } + + @Override + public Long zlexcount(final String key, final String min, final String max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zlexcount(key, min, max); + } + }.run(key); + } + + @Override + public Set zrangeByLex(final String key, final String min, final String max) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByLex(key, min, max); + } + }.run(key); + } + + @Override + public Set zrangeByLex(final String key, final String min, final String max, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrangeByLex(key, min, max, offset, count); + } + }.run(key); + } + + @Override + public Set zrevrangeByLex(final String key, final String max, final String min) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByLex(key, max, min); + } + }.run(key); + } + + @Override + public Set zrevrangeByLex(final String key, final String max, final String min, + final int offset, final int count) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.zrevrangeByLex(key, max, min, offset, count); + } + }.run(key); + } + + @Override + public Long zremrangeByLex(final String key, final String min, final String max) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zremrangeByLex(key, min, max); + } + }.run(key); + } + + @Override + public Long linsert(final String key, final ListPosition where, final String pivot, + final String value) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.linsert(key, where, pivot, value); + } + }.run(key); + } + + @Override + public Long lpushx(final String key, final String... string) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.lpushx(key, string); + } + }.run(key); + } + + @Override + public Long rpushx(final String key, final String... string) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.rpushx(key, string); + } + }.run(key); + } + + @Override + public Long del(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.del(key); + } + }.run(key); + } + + @Override + public Long unlink(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.unlink(key); + } + }.run(key); + } + + @Override + public Long unlink(final String... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.unlink(keys); + } + }.run(keys.length, keys); + } + + @Override + public String echo(final String string) { + // note that it'll be run from arbitary node + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.echo(string); + } + }.run(string); + } + + @Override + public Long bitcount(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.bitcount(key); + } + }.run(key); + } + + @Override + public Long bitcount(final String key, final long start, final long end) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.bitcount(key, start, end); + } + }.run(key); + } + + @Override + public Set keys(final String pattern) { + if (pattern == null || pattern.isEmpty()) { + throw new IllegalArgumentException(this.getClass().getSimpleName() + + " only supports KEYS commands with non-empty patterns"); + } + if (!JedisClusterHashTagUtil.isClusterCompliantMatchPattern(pattern)) { + throw new IllegalArgumentException(this.getClass().getSimpleName() + + " only supports KEYS commands with patterns containing hash-tags ( curly-brackets enclosed strings )"); + } + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.keys(pattern); + } + }.run(pattern); + } + + @Override + public ScanResult scan(final String cursor, final ScanParams params) { + + String matchPattern = null; + + if (params == null || (matchPattern = params.match()) == null || matchPattern.isEmpty()) { + throw new IllegalArgumentException(JedisCluster.class.getSimpleName() + + " only supports SCAN commands with non-empty MATCH patterns"); + } + + if (JedisClusterHashTagUtil.isClusterCompliantMatchPattern(matchPattern)) { + throw new IllegalArgumentException(JedisCluster.class.getSimpleName() + + " only supports SCAN commands with MATCH patterns containing hash-tags ( curly-brackets enclosed strings )"); + } + + return new JedisClusterCommand< ScanResult>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.scan(cursor, params); + } + }.run(matchPattern); + } + + @Override + public ScanResult> hscan(final String key, final String cursor) { + return new JedisClusterCommand>>(connectionHandler, + maxAttempts) { + @Override + public ScanResult> execute(Jedis connection) { + return connection.hscan(key, cursor); + } + }.run(key); + } + + @Override + public ScanResult sscan(final String key, final String cursor) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.sscan(key, cursor); + } + }.run(key); + } + + @Override + public ScanResult zscan(final String key, final String cursor) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public ScanResult execute(Jedis connection) { + return connection.zscan(key, cursor); + } + }.run(key); + } + + @Override + public Long pfadd(final String key, final String... elements) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pfadd(key, elements); + } + }.run(key); + } + + @Override + public long pfcount(final String key) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pfcount(key); + } + }.run(key); + } + + @Override + public List blpop(final int timeout, final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.blpop(timeout, key); + } + }.run(key); + } + + @Override + public List brpop(final int timeout, final String key) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.brpop(timeout, key); + } + }.run(key); + } + + @Override + public Long del(final String... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.del(keys); + } + }.run(keys.length, keys); + } + + @Override + public List blpop(final int timeout, final String... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.blpop(timeout, keys); + } + }.run(keys.length, keys); + + } + + @Override + public List brpop(final int timeout, final String... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.brpop(timeout, keys); + } + }.run(keys.length, keys); + } + + @Override + public List mget(final String... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.mget(keys); + } + }.run(keys.length, keys); + } + + @Override + public String mset(final String... keysvalues) { + String[] keys = new String[keysvalues.length / 2]; + + for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) { + keys[keyIdx] = keysvalues[keyIdx * 2]; + } + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.mset(keysvalues); + } + }.run(keys.length, keys); + } + + @Override + public Long msetnx(final String... keysvalues) { + String[] keys = new String[keysvalues.length / 2]; + + for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) { + keys[keyIdx] = keysvalues[keyIdx * 2]; + } + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.msetnx(keysvalues); + } + }.run(keys.length, keys); + } + + @Override + public String rename(final String oldkey, final String newkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.rename(oldkey, newkey); + } + }.run(2, oldkey, newkey); + } + + @Override + public Long renamenx(final String oldkey, final String newkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.renamenx(oldkey, newkey); + } + }.run(2, oldkey, newkey); + } + + @Override + public String rpoplpush(final String srckey, final String dstkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.rpoplpush(srckey, dstkey); + } + }.run(2, srckey, dstkey); + } + + @Override + public Set sdiff(final String... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.sdiff(keys); + } + }.run(keys.length, keys); + } + + @Override + public Long sdiffstore(final String dstkey, final String... keys) { + String[] mergedKeys = KeyMergeUtil.merge(dstkey, keys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sdiffstore(dstkey, keys); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public Set sinter(final String... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.sinter(keys); + } + }.run(keys.length, keys); + } + + @Override + public Long sinterstore(final String dstkey, final String... keys) { + String[] mergedKeys = KeyMergeUtil.merge(dstkey, keys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sinterstore(dstkey, keys); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public Long smove(final String srckey, final String dstkey, final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.smove(srckey, dstkey, member); + } + }.run(2, srckey, dstkey); + } + + @Override + public Long sort(final String key, final SortingParams sortingParameters, final String dstkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sort(key, sortingParameters, dstkey); + } + }.run(2, key, dstkey); + } + + @Override + public Long sort(final String key, final String dstkey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sort(key, dstkey); + } + }.run(2, key, dstkey); + } + + @Override + public Set sunion(final String... keys) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public Set execute(Jedis connection) { + return connection.sunion(keys); + } + }.run(keys.length, keys); + } + + @Override + public Long sunionstore(final String dstkey, final String... keys) { + String[] wholeKeys = KeyMergeUtil.merge(dstkey, keys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.sunionstore(dstkey, keys); + } + }.run(wholeKeys.length, wholeKeys); + } + + @Override + public Long zinterstore(final String dstkey, final String... sets) { + String[] wholeKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zinterstore(dstkey, sets); + } + }.run(wholeKeys.length, wholeKeys); + } + + @Override + public Long zinterstore(final String dstkey, final ZParams params, final String... sets) { + String[] mergedKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zinterstore(dstkey, params, sets); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public Long zunionstore(final String dstkey, final String... sets) { + String[] mergedKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zunionstore(dstkey, sets); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public Long zunionstore(final String dstkey, final ZParams params, final String... sets) { + String[] mergedKeys = KeyMergeUtil.merge(dstkey, sets); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.zunionstore(dstkey, params, sets); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public String brpoplpush(final String source, final String destination, final int timeout) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.brpoplpush(source, destination, timeout); + } + }.run(2, source, destination); + } + + @Override + public Long publish(final String channel, final String message) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.publish(channel, message); + } + }.runWithAnyNode(); + } + + @Override + public void subscribe(final JedisPubSub jedisPubSub, final String... channels) { + new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Integer execute(Jedis connection) { + connection.subscribe(jedisPubSub, channels); + return 0; + } + }.runWithAnyNode(); + } + + @Override + public void psubscribe(final JedisPubSub jedisPubSub, final String... patterns) { + new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Integer execute(Jedis connection) { + connection.psubscribe(jedisPubSub, patterns); + return 0; + } + }.runWithAnyNode(); + } + + @Override + public Long bitop(final BitOP op, final String destKey, final String... srcKeys) { + String[] mergedKeys = KeyMergeUtil.merge(destKey, srcKeys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.bitop(op, destKey, srcKeys); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public String pfmerge(final String destkey, final String... sourcekeys) { + String[] mergedKeys = KeyMergeUtil.merge(destkey, sourcekeys); + + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.pfmerge(destkey, sourcekeys); + } + }.run(mergedKeys.length, mergedKeys); + } + + @Override + public long pfcount(final String... keys) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.pfcount(keys); + } + }.run(keys.length, keys); + } + + @Override + public Object eval(final String script, final int keyCount, final String... params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script, keyCount, params); + } + }.run(keyCount, params); + } + + @Override + public Object eval(final String script, final String sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script); + } + }.run(sampleKey); + } + + @Override + public Object eval(final String script, final List keys, final List args) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.eval(script, keys, args); + } + }.run(keys.size(), keys.toArray(new String[keys.size()])); + } + + @Override + public Object evalsha(final String sha1, final int keyCount, final String... params) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.evalsha(sha1, keyCount, params); + } + }.run(keyCount, params); + } + + @Override + public Object evalsha(final String sha1, final List keys, final List args) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.evalsha(sha1, keys, args); + } + }.run(keys.size(), keys.toArray(new String[keys.size()])); + } + + @Override + public Object evalsha(final String sha1, final String sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Object execute(Jedis connection) { + return connection.evalsha(sha1); + } + }.run(sampleKey); + } + + @Override + public Boolean scriptExists(final String sha1, final String sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Boolean execute(Jedis connection) { + return connection.scriptExists(sha1); + } + }.run(sampleKey); + } + + @Override + public List scriptExists(final String sampleKey, final String... sha1) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.scriptExists(sha1); + } + }.run(sampleKey); + } + + @Override + public String scriptLoad(final String script, final String sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.scriptLoad(script); + } + }.run(sampleKey); + } + + @Override + public String scriptFlush(final String sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.scriptFlush(); + } + }.run(sampleKey); + } + + @Override + public String scriptKill(final String sampleKey) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.scriptKill(); + } + }.run(sampleKey); + } + + @Override + public Long geoadd(final String key, final double longitude, final double latitude, + final String member) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.geoadd(key, longitude, latitude, member); + } + }.run(key); + } + + @Override + public Long geoadd(final String key, final Map memberCoordinateMap) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.geoadd(key, memberCoordinateMap); + } + }.run(key); + } + + @Override + public Double geodist(final String key, final String member1, final String member2) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.geodist(key, member1, member2); + } + }.run(key); + } + + @Override + public Double geodist(final String key, final String member1, final String member2, + final GeoUnit unit) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Double execute(Jedis connection) { + return connection.geodist(key, member1, member2, unit); + } + }.run(key); + } + + @Override + public List geohash(final String key, final String... members) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.geohash(key, members); + } + }.run(key); + } + + @Override + public List geopos(final String key, final String... members) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.geopos(key, members); + } + }.run(key); + } + + @Override + public List georadius(final String key, final double longitude, + final double latitude, final double radius, final GeoUnit unit) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadius(key, longitude, latitude, radius, unit); + } + }.run(key); + } + + @Override + public List georadius(final String key, final double longitude, + final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadius(key, longitude, latitude, radius, unit, param); + } + }.run(key); + } + + @Override + public List georadiusByMember(final String key, final String member, + final double radius, final GeoUnit unit) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadiusByMember(key, member, radius, unit); + } + }.run(key); + } + + @Override + public List georadiusByMember(final String key, final String member, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.georadiusByMember(key, member, radius, unit, param); + } + }.run(key); + } + + @Override + public List bitfield(final String key, final String... arguments) { + return new JedisClusterCommand>(connectionHandler, maxAttempts) { + @Override + public List execute(Jedis connection) { + return connection.bitfield(key, arguments); + } + }.run(key); + } + + @Override + public Long hstrlen(final String key, final String field) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public Long execute(Jedis connection) { + return connection.hstrlen(key, field); + } + }.run(key); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterCommand.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterCommand.java new file mode 100755 index 000000000..d8120e211 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterCommand.java @@ -0,0 +1,170 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.exceptions.JedisAskDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisClusterMaxAttemptsException; +import com.fr.third.redis.clients.jedis.exceptions.JedisClusterOperationException; +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisMovedDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisNoReachableClusterNodeException; +import com.fr.third.redis.clients.jedis.exceptions.JedisRedirectionException; +import com.fr.third.redis.clients.jedis.util.JedisClusterCRC16; + +public abstract class JedisClusterCommand { + + private static final String NO_DISPATCH_MESSAGE = "No way to dispatch this command to Redis Cluster."; + + private final JedisClusterConnectionHandler connectionHandler; + private final int maxAttempts; + private final ThreadLocal askConnection = new ThreadLocal(); + + public JedisClusterCommand(JedisClusterConnectionHandler connectionHandler, int maxAttempts) { + this.connectionHandler = connectionHandler; + this.maxAttempts = maxAttempts; + } + + public abstract T execute(Jedis connection); + + public T run(String key) { + if (key == null) { + throw new JedisClusterOperationException(NO_DISPATCH_MESSAGE); + } + + return runWithRetries(JedisClusterCRC16.getSlot(key), this.maxAttempts, false, false); + } + + public T run(int keyCount, String... keys) { + if (keys == null || keys.length == 0) { + throw new JedisClusterOperationException(NO_DISPATCH_MESSAGE); + } + + // For multiple keys, only execute if they all share the same connection slot. + int slot = JedisClusterCRC16.getSlot(keys[0]); + if (keys.length > 1) { + for (int i = 1; i < keyCount; i++) { + int nextSlot = JedisClusterCRC16.getSlot(keys[i]); + if (slot != nextSlot) { + throw new JedisClusterOperationException("No way to dispatch this command to Redis " + + "Cluster because keys have different slots."); + } + } + } + + return runWithRetries(slot, this.maxAttempts, false, false); + } + + public T runBinary(byte[] key) { + if (key == null) { + throw new JedisClusterOperationException(NO_DISPATCH_MESSAGE); + } + + return runWithRetries(JedisClusterCRC16.getSlot(key), this.maxAttempts, false, false); + } + + public T runBinary(int keyCount, byte[]... keys) { + if (keys == null || keys.length == 0) { + throw new JedisClusterOperationException(NO_DISPATCH_MESSAGE); + } + + // For multiple keys, only execute if they all share the same connection slot. + int slot = JedisClusterCRC16.getSlot(keys[0]); + if (keys.length > 1) { + for (int i = 1; i < keyCount; i++) { + int nextSlot = JedisClusterCRC16.getSlot(keys[i]); + if (slot != nextSlot) { + throw new JedisClusterOperationException("No way to dispatch this command to Redis " + + "Cluster because keys have different slots."); + } + } + } + + return runWithRetries(slot, this.maxAttempts, false, false); + } + + public T runWithAnyNode() { + Jedis connection = null; + try { + connection = connectionHandler.getConnection(); + return execute(connection); + } catch (JedisConnectionException e) { + throw e; + } finally { + releaseConnection(connection); + } + } + + private T runWithRetries(final int slot, int attempts, boolean tryRandomNode, boolean asking) { + if (attempts <= 0) { + throw new JedisClusterMaxAttemptsException("No more cluster attempts left."); + } + + Jedis connection = null; + try { + + if (asking) { + // TODO: Pipeline asking with the original command to make it + // faster.... + connection = askConnection.get(); + connection.asking(); + + // if asking success, reset asking flag + asking = false; + } else { + if (tryRandomNode) { + connection = connectionHandler.getConnection(); + } else { + connection = connectionHandler.getConnectionFromSlot(slot); + } + } + + return execute(connection); + + } catch (JedisNoReachableClusterNodeException jnrcne) { + throw jnrcne; + } catch (JedisConnectionException jce) { + // release current connection before recursion + releaseConnection(connection); + connection = null; + + if (attempts <= 1) { + //We need this because if node is not reachable anymore - we need to finally initiate slots renewing, + //or we can stuck with cluster state without one node in opposite case. + //But now if maxAttempts = 1 or 2 we will do it too often. For each time-outed request. + //TODO make tracking of successful/unsuccessful operations for node - do renewing only + //if there were no successful responses from this node last few seconds + this.connectionHandler.renewSlotCache(); + } + + return runWithRetries(slot, attempts - 1, tryRandomNode, asking); + } catch (JedisRedirectionException jre) { + // if MOVED redirection occurred, + if (jre instanceof JedisMovedDataException) { + // it rebuilds cluster's slot cache + // recommended by Redis cluster specification + this.connectionHandler.renewSlotCache(connection); + } + + // release current connection before recursion or renewing + releaseConnection(connection); + connection = null; + + if (jre instanceof JedisAskDataException) { + asking = true; + askConnection.set(this.connectionHandler.getConnectionFromNode(jre.getTargetNode())); + } else if (jre instanceof JedisMovedDataException) { + } else { + throw new JedisClusterOperationException(jre); + } + + return runWithRetries(slot, attempts - 1, false, asking); + } finally { + releaseConnection(connection); + } + } + + private void releaseConnection(Jedis connection) { + if (connection != null) { + connection.close(); + } + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterConnectionHandler.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterConnectionHandler.java new file mode 100755 index 000000000..e89dee505 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterConnectionHandler.java @@ -0,0 +1,73 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.Closeable; +import java.util.Map; +import java.util.Set; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; + +public abstract class JedisClusterConnectionHandler implements Closeable { + protected final JedisClusterInfoCache cache; + + public JedisClusterConnectionHandler(Set nodes, + final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password) { + this(nodes, poolConfig, connectionTimeout, soTimeout, password, null); + } + + public JedisClusterConnectionHandler(Set nodes, + final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName) { + this.cache = new JedisClusterInfoCache(poolConfig, connectionTimeout, soTimeout, password, clientName); + initializeSlotsCache(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName); +} + + abstract Jedis getConnection(); + + abstract Jedis getConnectionFromSlot(int slot); + + public Jedis getConnectionFromNode(HostAndPort node) { + return cache.setupNodeIfNotExist(node).getResource(); + } + + public Map getNodes() { + return cache.getNodes(); + } + + private void initializeSlotsCache(Set startNodes, GenericObjectPoolConfig poolConfig, + int connectionTimeout, int soTimeout, String password, String clientName) { + for (HostAndPort hostAndPort : startNodes) { + Jedis jedis = null; + try { + jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout); + if (password != null) { + jedis.auth(password); + } + if (clientName != null) { + jedis.clientSetname(clientName); + } + cache.discoverClusterNodesAndSlots(jedis); + break; + } catch (JedisConnectionException e) { + // try next nodes + } finally { + if (jedis != null) { + jedis.close(); + } + } + } + } + + public void renewSlotCache() { + cache.renewClusterSlots(null); + } + + public void renewSlotCache(Jedis jedis) { + cache.renewClusterSlots(jedis); + } + + @Override + public void close() { + cache.reset(); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterInfoCache.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterInfoCache.java new file mode 100755 index 000000000..25dd21753 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisClusterInfoCache.java @@ -0,0 +1,268 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class JedisClusterInfoCache { + private final Map nodes = new HashMap(); + private final Map slots = new HashMap(); + + private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + private final Lock r = rwl.readLock(); + private final Lock w = rwl.writeLock(); + private volatile boolean rediscovering; + private final GenericObjectPoolConfig poolConfig; + + private int connectionTimeout; + private int soTimeout; + private String password; + private String clientName; + + private static final int MASTER_NODE_INDEX = 2; + + public JedisClusterInfoCache(final GenericObjectPoolConfig poolConfig, int timeout) { + this(poolConfig, timeout, timeout, null, null); + } + + public JedisClusterInfoCache(final GenericObjectPoolConfig poolConfig, + final int connectionTimeout, final int soTimeout, final String password, final String clientName) { + this.poolConfig = poolConfig; + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + this.password = password; + this.clientName = clientName; + } + + public void discoverClusterNodesAndSlots(Jedis jedis) { + w.lock(); + + try { + reset(); + List slots = jedis.clusterSlots(); + + for (Object slotInfoObj : slots) { + List slotInfo = (List) slotInfoObj; + + if (slotInfo.size() <= MASTER_NODE_INDEX) { + continue; + } + + List slotNums = getAssignedSlotArray(slotInfo); + + // hostInfos + int size = slotInfo.size(); + for (int i = MASTER_NODE_INDEX; i < size; i++) { + List hostInfos = (List) slotInfo.get(i); + if (hostInfos.size() <= 0) { + continue; + } + + HostAndPort targetNode = generateHostAndPort(hostInfos); + setupNodeIfNotExist(targetNode); + if (i == MASTER_NODE_INDEX) { + assignSlotsToNode(slotNums, targetNode); + } + } + } + } finally { + w.unlock(); + } + } + + public void renewClusterSlots(Jedis jedis) { + //If rediscovering is already in process - no need to start one more same rediscovering, just return + if (!rediscovering) { + try { + w.lock(); + rediscovering = true; + + if (jedis != null) { + try { + discoverClusterSlots(jedis); + return; + } catch (JedisException e) { + //try nodes from all pools + } + } + + for (JedisPool jp : getShuffledNodesPool()) { + Jedis j = null; + try { + j = jp.getResource(); + discoverClusterSlots(j); + return; + } catch (JedisConnectionException e) { + // try next nodes + } finally { + if (j != null) { + j.close(); + } + } + } + } finally { + rediscovering = false; + w.unlock(); + } + } + } + + private void discoverClusterSlots(Jedis jedis) { + List slots = jedis.clusterSlots(); + this.slots.clear(); + + for (Object slotInfoObj : slots) { + List slotInfo = (List) slotInfoObj; + + if (slotInfo.size() <= MASTER_NODE_INDEX) { + continue; + } + + List slotNums = getAssignedSlotArray(slotInfo); + + // hostInfos + List hostInfos = (List) slotInfo.get(MASTER_NODE_INDEX); + if (hostInfos.isEmpty()) { + continue; + } + + // at this time, we just use master, discard slave information + HostAndPort targetNode = generateHostAndPort(hostInfos); + assignSlotsToNode(slotNums, targetNode); + } + } + + private HostAndPort generateHostAndPort(List hostInfos) { + return new HostAndPort(SafeEncoder.encode((byte[]) hostInfos.get(0)), + ((Long) hostInfos.get(1)).intValue()); + } + + public JedisPool setupNodeIfNotExist(HostAndPort node) { + w.lock(); + try { + String nodeKey = getNodeKey(node); + JedisPool existingPool = nodes.get(nodeKey); + if (existingPool != null) return existingPool; + + JedisPool nodePool = new JedisPool(poolConfig, node.getHost(), node.getPort(), + connectionTimeout, soTimeout, password, 0, clientName, false, null, null, null); + nodes.put(nodeKey, nodePool); + return nodePool; + } finally { + w.unlock(); + } + } + + public void assignSlotToNode(int slot, HostAndPort targetNode) { + w.lock(); + try { + JedisPool targetPool = setupNodeIfNotExist(targetNode); + slots.put(slot, targetPool); + } finally { + w.unlock(); + } + } + + public void assignSlotsToNode(List targetSlots, HostAndPort targetNode) { + w.lock(); + try { + JedisPool targetPool = setupNodeIfNotExist(targetNode); + for (Integer slot : targetSlots) { + slots.put(slot, targetPool); + } + } finally { + w.unlock(); + } + } + + public JedisPool getNode(String nodeKey) { + r.lock(); + try { + return nodes.get(nodeKey); + } finally { + r.unlock(); + } + } + + public JedisPool getSlotPool(int slot) { + r.lock(); + try { + return slots.get(slot); + } finally { + r.unlock(); + } + } + + public Map getNodes() { + r.lock(); + try { + return new HashMap(nodes); + } finally { + r.unlock(); + } + } + + public List getShuffledNodesPool() { + r.lock(); + try { + List pools = new ArrayList(nodes.values()); + Collections.shuffle(pools); + return pools; + } finally { + r.unlock(); + } + } + + /** + * Clear discovered nodes collections and gently release allocated resources + */ + public void reset() { + w.lock(); + try { + for (JedisPool pool : nodes.values()) { + try { + if (pool != null) { + pool.destroy(); + } + } catch (Exception e) { + // pass + } + } + nodes.clear(); + slots.clear(); + } finally { + w.unlock(); + } + } + + public static String getNodeKey(HostAndPort hnp) { + return hnp.getHost() + ":" + hnp.getPort(); + } + + public static String getNodeKey(Client client) { + return client.getHost() + ":" + client.getPort(); + } + + public static String getNodeKey(Jedis jedis) { + return getNodeKey(jedis.getClient()); + } + + private List getAssignedSlotArray(List slotInfo) { + List slotNums = new ArrayList(); + for (int slot = ((Long) slotInfo.get(0)).intValue(); slot <= ((Long) slotInfo.get(1)) + .intValue(); slot++) { + slotNums.add(slot); + } + return slotNums; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisFactory.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisFactory.java new file mode 100755 index 000000000..c61bb17ca --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisFactory.java @@ -0,0 +1,157 @@ +package com.fr.third.redis.clients.jedis; + +import java.net.URI; +import java.util.concurrent.atomic.AtomicReference; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; +import com.fr.third.org.apache.commons.pool2.impl.DefaultPooledObject; + +import com.fr.third.redis.clients.jedis.exceptions.InvalidURIException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.util.JedisURIHelper; + +/** + * PoolableObjectFactory custom impl. + */ +class JedisFactory implements PooledObjectFactory { + private final AtomicReference hostAndPort = new AtomicReference(); + private final int connectionTimeout; + private final int soTimeout; + private final String password; + private final int database; + private final String clientName; + private final boolean ssl; + private final SSLSocketFactory sslSocketFactory; + private final SSLParameters sslParameters; + private final HostnameVerifier hostnameVerifier; + + JedisFactory(final String host, final int port, final int connectionTimeout, + final int soTimeout, final String password, final int database, final String clientName) { + this(host, port, connectionTimeout, soTimeout, password, database, clientName, + false, null, null, null); + } + + JedisFactory(final String host, final int port, final int connectionTimeout, + final int soTimeout, final String password, final int database, final String clientName, + final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this.hostAndPort.set(new HostAndPort(host, port)); + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + this.password = password; + this.database = database; + this.clientName = clientName; + this.ssl = ssl; + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + } + + JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout, + final String clientName) { + this(uri, connectionTimeout, soTimeout, clientName, null, null, null); + } + + JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout, + final String clientName, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + if (!JedisURIHelper.isValid(uri)) { + throw new InvalidURIException(String.format( + "Cannot open Redis connection due invalid URI. %s", uri.toString())); + } + + this.hostAndPort.set(new HostAndPort(uri.getHost(), uri.getPort())); + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + this.password = JedisURIHelper.getPassword(uri); + this.database = JedisURIHelper.getDBIndex(uri); + this.clientName = clientName; + this.ssl = JedisURIHelper.isRedisSSLScheme(uri); + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + } + + public void setHostAndPort(final HostAndPort hostAndPort) { + this.hostAndPort.set(hostAndPort); + } + + @Override + public void activateObject(PooledObject pooledJedis) throws Exception { + final BinaryJedis jedis = pooledJedis.getObject(); + if (jedis.getDB() != database) { + jedis.select(database); + } + + } + + @Override + public void destroyObject(PooledObject pooledJedis) throws Exception { + final BinaryJedis jedis = pooledJedis.getObject(); + if (jedis.isConnected()) { + try { + try { + jedis.quit(); + } catch (Exception e) { + } + jedis.disconnect(); + } catch (Exception e) { + + } + } + + } + + @Override + public PooledObject makeObject() throws Exception { + final HostAndPort hostAndPort = this.hostAndPort.get(); + final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, + soTimeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + + try { + jedis.connect(); + if (password != null) { + jedis.auth(password); + } + if (database != 0) { + jedis.select(database); + } + if (clientName != null) { + jedis.clientSetname(clientName); + } + } catch (JedisException je) { + jedis.close(); + throw je; + } + + return new DefaultPooledObject(jedis); + + } + + @Override + public void passivateObject(PooledObject pooledJedis) throws Exception { + // TODO maybe should select db 0? Not sure right now. + } + + @Override + public boolean validateObject(PooledObject pooledJedis) { + final BinaryJedis jedis = pooledJedis.getObject(); + try { + HostAndPort hostAndPort = this.hostAndPort.get(); + + String connectionHost = jedis.getClient().getHost(); + int connectionPort = jedis.getClient().getPort(); + + return hostAndPort.getHost().equals(connectionHost) + && hostAndPort.getPort() == connectionPort && jedis.isConnected() + && jedis.ping().equals("PONG"); + } catch (final Exception e) { + return false; + } + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisMonitor.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisMonitor.java new file mode 100755 index 000000000..7648115ea --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisMonitor.java @@ -0,0 +1,16 @@ +package com.fr.third.redis.clients.jedis; + +public abstract class JedisMonitor { + protected Client client; + + public void proceed(Client client) { + this.client = client; + this.client.setTimeoutInfinite(); + do { + String command = client.getBulkReply(); + onCommand(command); + } while (client.isConnected()); + } + + public abstract void onCommand(String command); +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPool.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPool.java new file mode 100755 index 000000000..84235702e --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPool.java @@ -0,0 +1,258 @@ +package com.fr.third.redis.clients.jedis; + +import java.net.URI; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPool; +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.util.JedisURIHelper; + +public class JedisPool extends JedisPoolAbstract { + + public JedisPool() { + this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host) { + this(poolConfig, host, Protocol.DEFAULT_PORT); + } + + public JedisPool(String host, int port) { + this(new GenericObjectPoolConfig(), host, port); + } + + public JedisPool(final String host) { + URI uri = URI.create(host); + if (JedisURIHelper.isValid(uri)) { + this.internalPool = new GenericObjectPool(new JedisFactory(uri, + Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null), new GenericObjectPoolConfig()); + } else { + this.internalPool = new GenericObjectPool(new JedisFactory(host, + Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, + Protocol.DEFAULT_DATABASE, null), new GenericObjectPoolConfig()); + } + } + + public JedisPool(final String host, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + URI uri = URI.create(host); + if (JedisURIHelper.isValid(uri)) { + this.internalPool = new GenericObjectPool(new JedisFactory(uri, + Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, sslSocketFactory, sslParameters, + hostnameVerifier), new GenericObjectPoolConfig()); + } else { + this.internalPool = new GenericObjectPool(new JedisFactory(host, + Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, + Protocol.DEFAULT_DATABASE, null, false, null, null, null), new GenericObjectPoolConfig()); + } + } + + public JedisPool(final URI uri) { + this(new GenericObjectPoolConfig(), uri); + } + + public JedisPool(final URI uri, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + this(new GenericObjectPoolConfig(), uri, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public JedisPool(final URI uri, final int timeout) { + this(new GenericObjectPoolConfig(), uri, timeout); + } + + public JedisPool(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + this(new GenericObjectPoolConfig(), uri, timeout, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password) { + this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final boolean ssl) { + this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, ssl); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, ssl, + sslSocketFactory, sslParameters, hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port) { + this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, + final boolean ssl) { + this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, ssl); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, + final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, ssl, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, + final int timeout) { + this(poolConfig, host, port, timeout, null); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, + final int timeout, final boolean ssl) { + this(poolConfig, host, port, timeout, null, ssl); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, + final int timeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + this(poolConfig, host, port, timeout, null, ssl, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final int database) { + this(poolConfig, host, port, timeout, password, database, null); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final int database, final boolean ssl) { + this(poolConfig, host, port, timeout, password, database, null, ssl); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final int database, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(poolConfig, host, port, timeout, password, database, null, ssl, sslSocketFactory, + sslParameters, hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final int database, final String clientName) { + this(poolConfig, host, port, timeout, timeout, password, database, clientName); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final int database, final String clientName, + final boolean ssl) { + this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + int timeout, final String password, final int database, final String clientName, + final boolean ssl, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl, + sslSocketFactory, sslParameters, hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + final int connectionTimeout, final int soTimeout, final String password, final int database, + final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + super(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, password, + database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier)); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig) { + this(poolConfig, Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); + } + + public JedisPool(final String host, final int port, final boolean ssl) { + this(new GenericObjectPoolConfig(), host, port, ssl); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, + final int connectionTimeout, final int soTimeout, final String password, final int database, + final String clientName) { + super(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, password, + database, clientName)); + } + + public JedisPool(final String host, final int port, final boolean ssl, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(new GenericObjectPoolConfig(), host, port, ssl, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, + final int connectionTimeout, final int soTimeout, final String password, final int database, + final String clientName, final boolean ssl) { + this(poolConfig, host, port, connectionTimeout, soTimeout, password, database, clientName, ssl, + null, null, null); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri) { + this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout) { + this(poolConfig, uri, timeout, timeout); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout, + final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, + final HostnameVerifier hostnameVerifier) { + this(poolConfig, uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, + final int connectionTimeout, final int soTimeout) { + super(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, null)); + } + + public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, + final int connectionTimeout, final int soTimeout, final SSLSocketFactory sslSocketFactory, + final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { + super(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, null, sslSocketFactory, + sslParameters, hostnameVerifier)); + } + + @Override + public Jedis getResource() { + Jedis jedis = super.getResource(); + jedis.setDataSource(this); + return jedis; + } + + @Override + protected void returnBrokenResource(final Jedis resource) { + if (resource != null) { + returnBrokenResourceObject(resource); + } + } + + @Override + protected void returnResource(final Jedis resource) { + if (resource != null) { + try { + resource.resetState(); + returnResourceObject(resource); + } catch (Exception e) { + returnBrokenResource(resource); + throw new JedisException("Could not return the resource to the pool", e); + } + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolAbstract.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolAbstract.java new file mode 100755 index 000000000..f60001524 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolAbstract.java @@ -0,0 +1,27 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.util.Pool; + +public class JedisPoolAbstract extends Pool { + + public JedisPoolAbstract() { + super(); + } + + public JedisPoolAbstract(GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { + super(poolConfig, factory); + } + + @Override + protected void returnBrokenResource(Jedis resource) { + super.returnBrokenResource(resource); + } + + @Override + protected void returnResource(Jedis resource) { + super.returnResource(resource); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolConfig.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolConfig.java new file mode 100755 index 000000000..e72ebb809 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPoolConfig.java @@ -0,0 +1,13 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +public class JedisPoolConfig extends GenericObjectPoolConfig { + public JedisPoolConfig() { + // defaults to make your life with connection pool easier :) + setTestWhileIdle(true); + setMinEvictableIdleTimeMillis(60000); + setTimeBetweenEvictionRunsMillis(30000); + setNumTestsPerEvictionRun(-1); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPubSub.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPubSub.java new file mode 100755 index 000000000..4520bb9af --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisPubSub.java @@ -0,0 +1,179 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.MESSAGE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PMESSAGE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PSUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PUNSUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.SUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.UNSUBSCRIBE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.PONG; + +import java.util.Arrays; +import java.util.List; + +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public abstract class JedisPubSub { + + private static final String JEDIS_SUBSCRIPTION_MESSAGE = "JedisPubSub is not subscribed to a Jedis instance."; + private int subscribedChannels = 0; + private volatile Client client; + + public void onMessage(String channel, String message) { + } + + public void onPMessage(String pattern, String channel, String message) { + } + + public void onSubscribe(String channel, int subscribedChannels) { + } + + public void onUnsubscribe(String channel, int subscribedChannels) { + } + + public void onPUnsubscribe(String pattern, int subscribedChannels) { + } + + public void onPSubscribe(String pattern, int subscribedChannels) { + } + + public void onPong(String pattern) { + + } + + public void unsubscribe() { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.unsubscribe(); + client.flush(); + } + + public void unsubscribe(String... channels) { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.unsubscribe(channels); + client.flush(); + } + + public void subscribe(String... channels) { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.subscribe(channels); + client.flush(); + } + + public void psubscribe(String... patterns) { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.psubscribe(patterns); + client.flush(); + } + + public void punsubscribe() { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.punsubscribe(); + client.flush(); + } + + public void punsubscribe(String... patterns) { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.punsubscribe(patterns); + client.flush(); + } + + public void ping() { + if (client == null) { + throw new JedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); + } + client.ping(); + client.flush(); + } + + public boolean isSubscribed() { + return subscribedChannels > 0; + } + + public void proceedWithPatterns(Client client, String... patterns) { + this.client = client; + client.psubscribe(patterns); + client.flush(); + process(client); + } + + public void proceed(Client client, String... channels) { + this.client = client; + client.subscribe(channels); + client.flush(); + process(client); + } + + private void process(Client client) { + + do { + List reply = client.getRawObjectMultiBulkReply(); + final Object firstObj = reply.get(0); + if (!(firstObj instanceof byte[])) { + throw new JedisException("Unknown message type: " + firstObj); + } + final byte[] resp = (byte[]) firstObj; + if (Arrays.equals(SUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bchannel = (byte[]) reply.get(1); + final String strchannel = (bchannel == null) ? null : SafeEncoder.encode(bchannel); + onSubscribe(strchannel, subscribedChannels); + } else if (Arrays.equals(UNSUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bchannel = (byte[]) reply.get(1); + final String strchannel = (bchannel == null) ? null : SafeEncoder.encode(bchannel); + onUnsubscribe(strchannel, subscribedChannels); + } else if (Arrays.equals(MESSAGE.raw, resp)) { + final byte[] bchannel = (byte[]) reply.get(1); + final byte[] bmesg = (byte[]) reply.get(2); + final String strchannel = (bchannel == null) ? null : SafeEncoder.encode(bchannel); + final String strmesg = (bmesg == null) ? null : SafeEncoder.encode(bmesg); + onMessage(strchannel, strmesg); + } else if (Arrays.equals(PMESSAGE.raw, resp)) { + final byte[] bpattern = (byte[]) reply.get(1); + final byte[] bchannel = (byte[]) reply.get(2); + final byte[] bmesg = (byte[]) reply.get(3); + final String strpattern = (bpattern == null) ? null : SafeEncoder.encode(bpattern); + final String strchannel = (bchannel == null) ? null : SafeEncoder.encode(bchannel); + final String strmesg = (bmesg == null) ? null : SafeEncoder.encode(bmesg); + onPMessage(strpattern, strchannel, strmesg); + } else if (Arrays.equals(PSUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bpattern = (byte[]) reply.get(1); + final String strpattern = (bpattern == null) ? null : SafeEncoder.encode(bpattern); + onPSubscribe(strpattern, subscribedChannels); + } else if (Arrays.equals(PUNSUBSCRIBE.raw, resp)) { + subscribedChannels = ((Long) reply.get(2)).intValue(); + final byte[] bpattern = (byte[]) reply.get(1); + final String strpattern = (bpattern == null) ? null : SafeEncoder.encode(bpattern); + onPUnsubscribe(strpattern, subscribedChannels); + } else if (Arrays.equals(PONG.raw, resp)) { + final byte[] bpattern = (byte[]) reply.get(1); + final String strpattern = (bpattern == null) ? null : SafeEncoder.encode(bpattern); + onPong(strpattern); + } else { + throw new JedisException("Unknown message type: " + firstObj); + } + } while (isSubscribed()); + + /* Invalidate instance since this thread is no longer listening */ + this.client = null; + } + + public int getSubscribedChannels() { + return subscribedChannels; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSentinelPool.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSentinelPool.java new file mode 100755 index 000000000..da2748cd6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSentinelPool.java @@ -0,0 +1,348 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; + +public class JedisSentinelPool extends JedisPoolAbstract { + + protected GenericObjectPoolConfig poolConfig; + + protected int connectionTimeout = Protocol.DEFAULT_TIMEOUT; + protected int soTimeout = Protocol.DEFAULT_TIMEOUT; + + protected String password; + + protected int database = Protocol.DEFAULT_DATABASE; + + protected String clientName; + + protected Set masterListeners = new HashSet(); + + protected Logger log = LoggerFactory.getLogger(getClass().getName()); + + private volatile JedisFactory factory; + private volatile HostAndPort currentHostMaster; + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig) { + this(masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, null, + Protocol.DEFAULT_DATABASE); + } + + public JedisSentinelPool(String masterName, Set sentinels) { + this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, null, + Protocol.DEFAULT_DATABASE); + } + + public JedisSentinelPool(String masterName, Set sentinels, String password) { + this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, password); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, int timeout, final String password) { + this(masterName, sentinels, poolConfig, timeout, password, Protocol.DEFAULT_DATABASE); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, final int timeout) { + this(masterName, sentinels, poolConfig, timeout, null, Protocol.DEFAULT_DATABASE); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, final String password) { + this(masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, password); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, int timeout, final String password, + final int database) { + this(masterName, sentinels, poolConfig, timeout, timeout, password, database); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, int timeout, final String password, + final int database, final String clientName) { + this(masterName, sentinels, poolConfig, timeout, timeout, password, database, clientName); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, final int timeout, final int soTimeout, + final String password, final int database) { + this(masterName, sentinels, poolConfig, timeout, soTimeout, password, database, null); + } + + public JedisSentinelPool(String masterName, Set sentinels, + final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, + final String password, final int database, final String clientName) { + this.poolConfig = poolConfig; + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + this.password = password; + this.database = database; + this.clientName = clientName; + + HostAndPort master = initSentinels(sentinels, masterName); + initPool(master); + } + + @Override + public void destroy() { + for (MasterListener m : masterListeners) { + m.shutdown(); + } + + super.destroy(); + } + + public HostAndPort getCurrentHostMaster() { + return currentHostMaster; + } + + private void initPool(HostAndPort master) { + if (!master.equals(currentHostMaster)) { + currentHostMaster = master; + if (factory == null) { + factory = new JedisFactory(master.getHost(), master.getPort(), connectionTimeout, + soTimeout, password, database, clientName); + initPool(poolConfig, factory); + } else { + factory.setHostAndPort(currentHostMaster); + // although we clear the pool, we still have to check the + // returned object + // in getResource, this call only clears idle instances, not + // borrowed instances + internalPool.clear(); + } + + log.info("Created JedisPool to master at " + master); + } + } + + private HostAndPort initSentinels(Set sentinels, final String masterName) { + + HostAndPort master = null; + boolean sentinelAvailable = false; + + log.info("Trying to find master from available Sentinels..."); + + for (String sentinel : sentinels) { + final HostAndPort hap = HostAndPort.parseString(sentinel); + + log.debug("Connecting to Sentinel {}", hap); + + Jedis jedis = null; + try { + jedis = new Jedis(hap); + + List masterAddr = jedis.sentinelGetMasterAddrByName(masterName); + + // connected to sentinel... + sentinelAvailable = true; + + if (masterAddr == null || masterAddr.size() != 2) { + log.warn("Can not get master addr, master name: {}. Sentinel: {}", masterName, hap); + continue; + } + + master = toHostAndPort(masterAddr); + log.debug("Found Redis master at {}", master); + break; + } catch (JedisException e) { + // resolves #1036, it should handle JedisException there's another chance + // of raising JedisDataException + log.warn( + "Cannot get master address from sentinel running @ {}. Reason: {}. Trying next one.", hap, + e.toString()); + } finally { + if (jedis != null) { + jedis.close(); + } + } + } + + if (master == null) { + if (sentinelAvailable) { + // can connect to sentinel, but master name seems to not + // monitored + throw new JedisException("Can connect to sentinel, but " + masterName + + " seems to be not monitored..."); + } else { + throw new JedisConnectionException("All sentinels down, cannot determine where is " + + masterName + " master is running..."); + } + } + + log.info("Redis master running at " + master + ", starting Sentinel listeners..."); + + for (String sentinel : sentinels) { + final HostAndPort hap = HostAndPort.parseString(sentinel); + MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort()); + // whether MasterListener threads are alive or not, process can be stopped + masterListener.setDaemon(true); + masterListeners.add(masterListener); + masterListener.start(); + } + + return master; + } + + private HostAndPort toHostAndPort(List getMasterAddrByNameResult) { + String host = getMasterAddrByNameResult.get(0); + int port = Integer.parseInt(getMasterAddrByNameResult.get(1)); + + return new HostAndPort(host, port); + } + + @Override + public Jedis getResource() { + while (true) { + Jedis jedis = super.getResource(); + jedis.setDataSource(this); + + // get a reference because it can change concurrently + final HostAndPort master = currentHostMaster; + final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient() + .getPort()); + + if (master.equals(connection)) { + // connected to the correct master + return jedis; + } else { + returnBrokenResource(jedis); + } + } + } + + @Override + protected void returnBrokenResource(final Jedis resource) { + if (resource != null) { + returnBrokenResourceObject(resource); + } + } + + @Override + protected void returnResource(final Jedis resource) { + if (resource != null) { + resource.resetState(); + returnResourceObject(resource); + } + } + + protected class MasterListener extends Thread { + + protected String masterName; + protected String host; + protected int port; + protected long subscribeRetryWaitTimeMillis = 5000; + protected volatile Jedis j; + protected AtomicBoolean running = new AtomicBoolean(false); + + protected MasterListener() { + } + + public MasterListener(String masterName, String host, int port) { + super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port)); + this.masterName = masterName; + this.host = host; + this.port = port; + } + + public MasterListener(String masterName, String host, int port, + long subscribeRetryWaitTimeMillis) { + this(masterName, host, port); + this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; + } + + @Override + public void run() { + + running.set(true); + + while (running.get()) { + + j = new Jedis(host, port); + + try { + // double check that it is not being shutdown + if (!running.get()) { + break; + } + + /* + * Added code for active refresh + */ + List masterAddr = j.sentinelGetMasterAddrByName(masterName); + if (masterAddr == null || masterAddr.size() != 2) { + log.warn("Can not get master addr, master name: {}. Sentinel: {}:{}.",masterName,host,port); + }else{ + initPool(toHostAndPort(masterAddr)); + } + + j.subscribe(new JedisPubSub() { + @Override + public void onMessage(String channel, String message) { + log.debug("Sentinel {}:{} published: {}.", host, port, message); + + String[] switchMasterMsg = message.split(" "); + + if (switchMasterMsg.length > 3) { + + if (masterName.equals(switchMasterMsg[0])) { + initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4]))); + } else { + log.debug( + "Ignoring message on +switch-master for master name {}, our master name is {}", + switchMasterMsg[0], masterName); + } + + } else { + log.error( + "Invalid message received on Sentinel {}:{} on channel +switch-master: {}", host, + port, message); + } + } + }, "+switch-master"); + + } catch (JedisException e) { + + if (running.get()) { + log.error("Lost connection to Sentinel at {}:{}. Sleeping 5000ms and retrying.", host, + port, e); + try { + Thread.sleep(subscribeRetryWaitTimeMillis); + } catch (InterruptedException e1) { + log.error("Sleep interrupted: ", e1); + } + } else { + log.debug("Unsubscribing from Sentinel at {}:{}", host, port); + } + } finally { + j.close(); + } + } + } + + public void shutdown() { + try { + log.debug("Shutting down listener on {}:{}", host, port); + running.set(false); + // This isn't good, the Jedis object is not thread safe + if (j != null) { + j.disconnect(); + } + } catch (Exception e) { + log.error("Caught exception while shutting down: ", e); + } + } + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisShardInfo.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisShardInfo.java new file mode 100755 index 000000000..bc6ae6897 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisShardInfo.java @@ -0,0 +1,252 @@ +package com.fr.third.redis.clients.jedis; + +import java.net.URI; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import com.fr.third.redis.clients.jedis.exceptions.InvalidURIException; +import com.fr.third.redis.clients.jedis.util.JedisURIHelper; +import com.fr.third.redis.clients.jedis.util.ShardInfo; +import com.fr.third.redis.clients.jedis.util.Sharded; + +public class JedisShardInfo extends ShardInfo { + + private int connectionTimeout; + private int soTimeout; + private String host; + private int port; + private String password = null; + private String name = null; + // Default Redis DB + private int db = 0; + private boolean ssl; + private SSLSocketFactory sslSocketFactory; + private SSLParameters sslParameters; + private HostnameVerifier hostnameVerifier; + + public JedisShardInfo(String host) { + super(Sharded.DEFAULT_WEIGHT); + URI uri = URI.create(host); + if (JedisURIHelper.isValid(uri)) { + this.host = uri.getHost(); + this.port = uri.getPort(); + this.password = JedisURIHelper.getPassword(uri); + this.db = JedisURIHelper.getDBIndex(uri); + this.ssl = JedisURIHelper.isRedisSSLScheme(uri); + } else { + this.host = host; + this.port = Protocol.DEFAULT_PORT; + } + } + + public JedisShardInfo(String host, SSLSocketFactory sslSocketFactory, + SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { + this(host); + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + } + + public JedisShardInfo(String host, String name) { + this(host, Protocol.DEFAULT_PORT, name); + } + + public JedisShardInfo(HostAndPort hp) { + this(hp.getHost(), hp.getPort()); + } + + public JedisShardInfo(String host, int port) { + this(host, port, Protocol.DEFAULT_TIMEOUT); + } + + public JedisShardInfo(String host, int port, boolean ssl) { + this(host, port, Protocol.DEFAULT_TIMEOUT, ssl); + } + + public JedisShardInfo(String host, int port, boolean ssl, SSLSocketFactory sslSocketFactory, + SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { + this(host, port, Protocol.DEFAULT_TIMEOUT, ssl, sslSocketFactory, sslParameters, + hostnameVerifier); + } + + public JedisShardInfo(String host, int port, String name) { + this(host, port, Protocol.DEFAULT_TIMEOUT, name); + } + + public JedisShardInfo(String host, int port, String name, boolean ssl) { + this(host, port, Protocol.DEFAULT_TIMEOUT, name, ssl); + } + + public JedisShardInfo(String host, int port, String name, boolean ssl, SSLSocketFactory sslSocketFactory, + SSLParameters sslParameters, HostnameVerifier hostnameVerifier) { + this(host, port, Protocol.DEFAULT_TIMEOUT, name, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + } + + public JedisShardInfo(String host, int port, int timeout) { + this(host, port, timeout, timeout, Sharded.DEFAULT_WEIGHT); + } + + public JedisShardInfo(String host, int port, int timeout, boolean ssl) { + this(host, port, timeout, timeout, Sharded.DEFAULT_WEIGHT, ssl); + } + + public JedisShardInfo(String host, int port, int timeout, boolean ssl, + SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier) { + this(host, port, timeout, timeout, Sharded.DEFAULT_WEIGHT, ssl, sslSocketFactory, + sslParameters, hostnameVerifier); + } + + public JedisShardInfo(String host, int port, int timeout, String name) { + this(host, port, timeout, timeout, Sharded.DEFAULT_WEIGHT); + this.name = name; + } + + public JedisShardInfo(String host, int port, int timeout, String name, boolean ssl) { + this(host, port, timeout, timeout, Sharded.DEFAULT_WEIGHT, ssl); + this.name = name; + } + + public JedisShardInfo(String host, int port, int timeout, String name, boolean ssl, + SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier) { + this(host, port, timeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + this.name = name; + } + + public JedisShardInfo(String host, int port, int connectionTimeout, int soTimeout, int weight) { + super(weight); + this.host = host; + this.port = port; + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + } + + public JedisShardInfo(String host, int port, int connectionTimeout, int soTimeout, int weight, + boolean ssl) { + super(weight); + this.host = host; + this.port = port; + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + this.ssl = ssl; + } + + public JedisShardInfo(String host, int port, int connectionTimeout, int soTimeout, int weight, + boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier) { + this(host, port, connectionTimeout, soTimeout, weight, ssl); + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + } + + public JedisShardInfo(String host, String name, int port, int timeout, int weight) { + this(host, port, timeout, timeout, weight); + this.name = name; + } + + public JedisShardInfo(String host, String name, int port, int timeout, int weight, boolean ssl) { + this(host, port, timeout, timeout, weight, ssl); + this.name = name; + } + + public JedisShardInfo(String host, String name, int port, int timeout, int weight, + boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier) { + this(host, port, timeout, timeout, weight, ssl, sslSocketFactory, sslParameters, hostnameVerifier); + this.name = name; + } + + public JedisShardInfo(URI uri) { + super(Sharded.DEFAULT_WEIGHT); + if (!JedisURIHelper.isValid(uri)) { + throw new InvalidURIException(String.format( + "Cannot open Redis connection due invalid URI. %s", uri.toString())); + } + + this.host = uri.getHost(); + this.port = uri.getPort(); + this.password = JedisURIHelper.getPassword(uri); + this.db = JedisURIHelper.getDBIndex(uri); + this.ssl = JedisURIHelper.isRedisSSLScheme(uri); + } + + public JedisShardInfo(URI uri, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier) { + this(uri); + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + } + + @Override + public String toString() { + return host + ":" + port + "*" + getWeight(); + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getPassword() { + return password; + } + + public void setPassword(String auth) { + this.password = auth; + } + + public int getConnectionTimeout() { + return connectionTimeout; + } + + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public int getSoTimeout() { + return soTimeout; + } + + public void setSoTimeout(int soTimeout) { + this.soTimeout = soTimeout; + } + + @Override + public String getName() { + return name; + } + + public int getDb() { + return db; + } + + public boolean getSsl() { + return ssl; + } + + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + public SSLParameters getSslParameters() { + return sslParameters; + } + + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + @Override + public Jedis createResource() { + return new Jedis(this); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSlotBasedConnectionHandler.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSlotBasedConnectionHandler.java new file mode 100755 index 000000000..11fac77db --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/JedisSlotBasedConnectionHandler.java @@ -0,0 +1,82 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.List; +import java.util.Set; + +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.exceptions.JedisNoReachableClusterNodeException; + +public class JedisSlotBasedConnectionHandler extends JedisClusterConnectionHandler { + + public JedisSlotBasedConnectionHandler(Set nodes, + final GenericObjectPoolConfig poolConfig, int timeout) { + this(nodes, poolConfig, timeout, timeout); + } + + public JedisSlotBasedConnectionHandler(Set nodes, + final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout) { + super(nodes, poolConfig, connectionTimeout, soTimeout, null); + } + + public JedisSlotBasedConnectionHandler(Set nodes, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password) { + super(nodes, poolConfig, connectionTimeout, soTimeout, password); + } + + public JedisSlotBasedConnectionHandler(Set nodes, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName) { + super(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName); + } + + @Override + public Jedis getConnection() { + // In antirez's redis-rb-cluster implementation, + // getRandomConnection always return valid connection (able to + // ping-pong) + // or exception if all connections are invalid + + List pools = cache.getShuffledNodesPool(); + + for (JedisPool pool : pools) { + Jedis jedis = null; + try { + jedis = pool.getResource(); + + if (jedis == null) { + continue; + } + + String result = jedis.ping(); + + if (result.equalsIgnoreCase("pong")) return jedis; + + jedis.close(); + } catch (JedisException ex) { + if (jedis != null) { + jedis.close(); + } + } + } + + throw new JedisNoReachableClusterNodeException("No reachable node in cluster"); + } + + @Override + public Jedis getConnectionFromSlot(int slot) { + JedisPool connectionPool = cache.getSlotPool(slot); + if (connectionPool != null) { + // It can't guaranteed to get valid connection because of node + // assignment + return connectionPool.getResource(); + } else { + renewSlotCache(); //It's abnormal situation for cluster mode, that we have just nothing for slot, try to rediscover state + connectionPool = cache.getSlotPool(slot); + if (connectionPool != null) { + return connectionPool.getResource(); + } else { + //no choice, fallback to new connection to random node + return getConnection(); + } + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ListPosition.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ListPosition.java new file mode 100755 index 000000000..c9908dc41 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ListPosition.java @@ -0,0 +1,12 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public enum ListPosition { + BEFORE, AFTER; + public final byte[] raw; + + private ListPosition() { + raw = SafeEncoder.encode(name()); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Module.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Module.java new file mode 100755 index 000000000..bf86e7b9e --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Module.java @@ -0,0 +1,41 @@ +package com.fr.third.redis.clients.jedis; + +public class Module { + private String name; + private int version; + + public Module(String name, int version) { + this.name = name; + this.version = version; + } + + + public String getName() { + return name; + } + + public int getVersion() { + return version; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Module module = (Module) o; + + if (version != module.version) return false; + return !(name != null ? !name.equals(module.name) : module.name != null); + + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + version; + return result; + } + + +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/MultiKeyPipelineBase.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/MultiKeyPipelineBase.java new file mode 100755 index 000000000..439645207 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/MultiKeyPipelineBase.java @@ -0,0 +1,692 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.commands.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class MultiKeyPipelineBase extends PipelineBase implements + MultiKeyBinaryRedisPipeline, MultiKeyCommandsPipeline, ClusterPipeline, + BinaryScriptingCommandsPipeline, ScriptingCommandsPipeline, BasicRedisPipeline { + + protected Client client = null; + + @Override + public Response> brpop(String... args) { + client.brpop(args); + return getResponse(BuilderFactory.STRING_LIST); + } + + public Response> brpop(int timeout, String... keys) { + client.brpop(timeout, keys); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> blpop(String... args) { + client.blpop(args); + return getResponse(BuilderFactory.STRING_LIST); + } + + public Response> blpop(int timeout, String... keys) { + client.blpop(timeout, keys); + return getResponse(BuilderFactory.STRING_LIST); + } + + public Response> blpopMap(int timeout, String... keys) { + client.blpop(timeout, keys); + return getResponse(BuilderFactory.STRING_MAP); + } + + @Override + public Response> brpop(byte[]... args) { + client.brpop(args); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + public Response> brpop(int timeout, byte[]... keys) { + client.brpop(timeout, keys); + return getResponse(BuilderFactory.STRING_LIST); + } + + public Response> brpopMap(int timeout, String... keys) { + client.blpop(timeout, keys); + return getResponse(BuilderFactory.STRING_MAP); + } + + @Override + public Response> blpop(byte[]... args) { + client.blpop(args); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + public Response> blpop(int timeout, byte[]... keys) { + client.blpop(timeout, keys); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response del(String... keys) { + client.del(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response del(byte[]... keys) { + client.del(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response unlink(String... keys) { + client.unlink(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response unlink(byte[]... keys) { + client.unlink(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response exists(String... keys) { + client.exists(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response exists(byte[]... keys) { + client.exists(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> keys(String pattern) { + getClient(pattern).keys(pattern); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response> keys(byte[] pattern) { + getClient(pattern).keys(pattern); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> mget(String... keys) { + client.mget(keys); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> mget(byte[]... keys) { + client.mget(keys); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response mset(String... keysvalues) { + client.mset(keysvalues); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response mset(byte[]... keysvalues) { + client.mset(keysvalues); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response msetnx(String... keysvalues) { + client.msetnx(keysvalues); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response msetnx(byte[]... keysvalues) { + client.msetnx(keysvalues); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response rename(String oldkey, String newkey) { + client.rename(oldkey, newkey); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response rename(byte[] oldkey, byte[] newkey) { + client.rename(oldkey, newkey); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response renamenx(String oldkey, String newkey) { + client.renamenx(oldkey, newkey); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response renamenx(byte[] oldkey, byte[] newkey) { + client.renamenx(oldkey, newkey); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response rpoplpush(String srckey, String dstkey) { + client.rpoplpush(srckey, dstkey); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response rpoplpush(byte[] srckey, byte[] dstkey) { + client.rpoplpush(srckey, dstkey); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response> sdiff(String... keys) { + client.sdiff(keys); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response> sdiff(byte[]... keys) { + client.sdiff(keys); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response sdiffstore(String dstkey, String... keys) { + client.sdiffstore(dstkey, keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sdiffstore(byte[] dstkey, byte[]... keys) { + client.sdiffstore(dstkey, keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> sinter(String... keys) { + client.sinter(keys); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response> sinter(byte[]... keys) { + client.sinter(keys); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response sinterstore(String dstkey, String... keys) { + client.sinterstore(dstkey, keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sinterstore(byte[] dstkey, byte[]... keys) { + client.sinterstore(dstkey, keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response smove(String srckey, String dstkey, String member) { + client.smove(srckey, dstkey, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response smove(byte[] srckey, byte[] dstkey, byte[] member) { + client.smove(srckey, dstkey, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sort(String key, SortingParams sortingParameters, String dstkey) { + client.sort(key, sortingParameters, dstkey); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sort(byte[] key, SortingParams sortingParameters, byte[] dstkey) { + client.sort(key, sortingParameters, dstkey); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sort(String key, String dstkey) { + client.sort(key, dstkey); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sort(byte[] key, byte[] dstkey) { + client.sort(key, dstkey); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> sunion(String... keys) { + client.sunion(keys); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response> sunion(byte[]... keys) { + client.sunion(keys); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response sunionstore(String dstkey, String... keys) { + client.sunionstore(dstkey, keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sunionstore(byte[] dstkey, byte[]... keys) { + client.sunionstore(dstkey, keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response watch(String... keys) { + client.watch(keys); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response watch(byte[]... keys) { + client.watch(keys); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response zinterstore(String dstkey, String... sets) { + client.zinterstore(dstkey, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zinterstore(byte[] dstkey, byte[]... sets) { + client.zinterstore(dstkey, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zinterstore(String dstkey, ZParams params, String... sets) { + client.zinterstore(dstkey, params, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zinterstore(byte[] dstkey, ZParams params, byte[]... sets) { + client.zinterstore(dstkey, params, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zunionstore(String dstkey, String... sets) { + client.zunionstore(dstkey, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zunionstore(byte[] dstkey, byte[]... sets) { + client.zunionstore(dstkey, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zunionstore(String dstkey, ZParams params, String... sets) { + client.zunionstore(dstkey, params, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zunionstore(byte[] dstkey, ZParams params, byte[]... sets) { + client.zunionstore(dstkey, params, sets); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response bgrewriteaof() { + client.bgrewriteaof(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response bgsave() { + client.bgsave(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response> configGet(String pattern) { + client.configGet(pattern); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response configSet(String parameter, String value) { + client.configSet(parameter, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response brpoplpush(String source, String destination, int timeout) { + client.brpoplpush(source, destination, timeout); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response brpoplpush(byte[] source, byte[] destination, int timeout) { + client.brpoplpush(source, destination, timeout); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response configResetStat() { + client.configResetStat(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response save() { + client.save(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response lastsave() { + client.lastsave(); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response publish(String channel, String message) { + client.publish(channel, message); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response publish(byte[] channel, byte[] message) { + client.publish(channel, message); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response randomKey() { + client.randomKey(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response randomKeyBinary() { + client.randomKey(); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response flushDB() { + client.flushDB(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response flushAll() { + client.flushAll(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response info() { + client.info(); + return getResponse(BuilderFactory.STRING); + } + + public Response info(final String section) { + client.info(section); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response dbSize() { + client.dbSize(); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response shutdown() { + client.shutdown(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response ping() { + client.ping(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response select(int index) { + client.select(index); + Response response = getResponse(BuilderFactory.STRING); + client.setDb(index); + + return response; + } + + @Override + public Response swapDB(int index1, int index2) { + client.swapDB(index1, index2); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response bitop(BitOP op, byte[] destKey, byte[]... srcKeys) { + client.bitop(op, destKey, srcKeys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response bitop(BitOP op, String destKey, String... srcKeys) { + client.bitop(op, destKey, srcKeys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response clusterNodes() { + client.clusterNodes(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response clusterMeet(final String ip, final int port) { + client.clusterMeet(ip, port); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response clusterAddSlots(final int... slots) { + client.clusterAddSlots(slots); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response clusterDelSlots(final int... slots) { + client.clusterDelSlots(slots); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response clusterInfo() { + client.clusterInfo(); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response> clusterGetKeysInSlot(final int slot, final int count) { + client.clusterGetKeysInSlot(slot, count); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response clusterSetSlotNode(final int slot, final String nodeId) { + client.clusterSetSlotNode(slot, nodeId); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response clusterSetSlotMigrating(final int slot, final String nodeId) { + client.clusterSetSlotMigrating(slot, nodeId); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response clusterSetSlotImporting(final int slot, final String nodeId) { + client.clusterSetSlotImporting(slot, nodeId); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response eval(String script) { + return this.eval(script, 0, new String[0]); + } + + @Override + public Response eval(String script, List keys, List args) { + String[] argv = Jedis.getParams(keys, args); + return this.eval(script, keys.size(), argv); + } + + @Override + public Response eval(String script, int keyCount, String... params) { + getClient(script).eval(script, keyCount, params); + return getResponse(BuilderFactory.EVAL_RESULT); + } + + @Override + public Response evalsha(String sha1) { + return this.evalsha(sha1, 0, new String[0]); + } + + @Override + public Response evalsha(String sha1, List keys, List args) { + String[] argv = Jedis.getParams(keys, args); + return this.evalsha(sha1, keys.size(), argv); + } + + @Override + public Response evalsha(String sha1, int keyCount, String... params) { + getClient(sha1).evalsha(sha1, keyCount, params); + return getResponse(BuilderFactory.EVAL_RESULT); + } + + @Override + public Response eval(byte[] script) { + return this.eval(script, 0); + } + + @Override + public Response eval(byte[] script, byte[] keyCount, byte[]... params) { + getClient(script).eval(script, keyCount, params); + return getResponse(BuilderFactory.EVAL_BINARY_RESULT); + } + + @Override + public Response eval(byte[] script, List keys, List args) { + byte[][] argv = BinaryJedis.getParamsWithBinary(keys, args); + return this.eval(script, keys.size(), argv); + } + + @Override + public Response eval(byte[] script, int keyCount, byte[]... params) { + getClient(script).eval(script, keyCount, params); + return getResponse(BuilderFactory.EVAL_BINARY_RESULT); + } + + @Override + public Response evalsha(byte[] sha1) { + return this.evalsha(sha1, 0); + } + + @Override + public Response evalsha(byte[] sha1, List keys, List args) { + byte[][] argv = BinaryJedis.getParamsWithBinary(keys, args); + return this.evalsha(sha1, keys.size(), argv); + } + + @Override + public Response evalsha(byte[] sha1, int keyCount, byte[]... params) { + getClient(sha1).evalsha(sha1, keyCount, params); + return getResponse(BuilderFactory.EVAL_BINARY_RESULT); + } + + @Override + public Response pfcount(String... keys) { + client.pfcount(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfcount(final byte[]... keys) { + client.pfcount(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfmerge(byte[] destkey, byte[]... sourcekeys) { + client.pfmerge(destkey, sourcekeys); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response pfmerge(String destkey, String... sourcekeys) { + client.pfmerge(destkey, sourcekeys); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response> time() { + client.time(); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response touch(String... keys) { + client.touch(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response touch(byte[]... keys) { + client.touch(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response moduleUnload(String name) { + client.moduleUnload(name); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response> moduleList() { + client.moduleList(); + return getResponse(BuilderFactory.MODULE_LIST); + } + + @Override + public Response moduleLoad(String path) { + client.moduleLoad(path); + return getResponse(BuilderFactory.STRING); + } + + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Pipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Pipeline.java new file mode 100755 index 000000000..f720ce94b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Pipeline.java @@ -0,0 +1,160 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; + +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; + +public class Pipeline extends MultiKeyPipelineBase implements Closeable { + + private MultiResponseBuilder currentMulti; + + private class MultiResponseBuilder extends Builder> { + private List> responses = new ArrayList>(); + + @Override + public List build(Object data) { + @SuppressWarnings("unchecked") + List list = (List) data; + List values = new ArrayList(); + + if (list.size() != responses.size()) { + throw new JedisDataException("Expected data size " + responses.size() + " but was " + + list.size()); + } + + for (int i = 0; i < list.size(); i++) { + Response response = responses.get(i); + response.set(list.get(i)); + Object builtResponse; + try { + builtResponse = response.get(); + } catch (JedisDataException e) { + builtResponse = e; + } + values.add(builtResponse); + } + return values; + } + + public void setResponseDependency(Response dependency) { + for (Response response : responses) { + response.setDependency(dependency); + } + } + + public void addResponse(Response response) { + responses.add(response); + } + } + + @Override + protected Response getResponse(Builder builder) { + if (currentMulti != null) { + super.getResponse(BuilderFactory.STRING); // Expected QUEUED + + Response lr = new Response(builder); + currentMulti.addResponse(lr); + return lr; + } else { + return super.getResponse(builder); + } + } + + public void setClient(Client client) { + this.client = client; + } + + @Override + protected Client getClient(byte[] key) { + return client; + } + + @Override + protected Client getClient(String key) { + return client; + } + + public void clear() { + if (isInMulti()) { + discard(); + } + + sync(); + } + + public boolean isInMulti() { + return currentMulti != null; + } + + /** + * Synchronize pipeline by reading all responses. This operation close the pipeline. In order to + * get return values from pipelined commands, capture the different Response<?> of the + * commands you execute. + */ + public void sync() { + if (getPipelinedResponseLength() > 0) { + List unformatted = client.getMany(getPipelinedResponseLength()); + for (Object o : unformatted) { + generateResponse(o); + } + } + } + + /** + * Synchronize pipeline by reading all responses. This operation close the pipeline. Whenever + * possible try to avoid using this version and use Pipeline.sync() as it won't go through all the + * responses and generate the right response type (usually it is a waste of time). + * @return A list of all the responses in the order you executed them. + */ + public List syncAndReturnAll() { + if (getPipelinedResponseLength() > 0) { + List unformatted = client.getMany(getPipelinedResponseLength()); + List formatted = new ArrayList(); + for (Object o : unformatted) { + try { + formatted.add(generateResponse(o).get()); + } catch (JedisDataException e) { + formatted.add(e); + } + } + return formatted; + } else { + return java.util.Collections. emptyList(); + } + } + + public Response discard() { + if (currentMulti == null) throw new JedisDataException("DISCARD without MULTI"); + client.discard(); + currentMulti = null; + return getResponse(BuilderFactory.STRING); + } + + public Response> exec() { + if (currentMulti == null) throw new JedisDataException("EXEC without MULTI"); + + client.exec(); + Response> response = super.getResponse(currentMulti); + currentMulti.setResponseDependency(response); + currentMulti = null; + return response; + } + + public Response multi() { + if (currentMulti != null) throw new JedisDataException("MULTI calls can not be nested"); + + client.multi(); + Response response = getResponse(BuilderFactory.STRING); // Expecting + // OK + currentMulti = new MultiResponseBuilder(); + return response; + } + + @Override + public void close() { + clear(); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/PipelineBase.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/PipelineBase.java new file mode 100755 index 000000000..2dfd85eed --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/PipelineBase.java @@ -0,0 +1,1692 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fr.third.redis.clients.jedis.commands.BinaryRedisPipeline; +import com.fr.third.redis.clients.jedis.commands.RedisPipeline; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +public abstract class PipelineBase extends Queable implements BinaryRedisPipeline, RedisPipeline { + + protected abstract Client getClient(String key); + + protected abstract Client getClient(byte[] key); + + @Override + public Response append(final String key, final String value) { + getClient(key).append(key, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response append(final byte[] key, final byte[] value) { + getClient(key).append(key, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> blpop(final String key) { + String[] temp = new String[1]; + temp[0] = key; + getClient(key).blpop(temp); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> brpop(final String key) { + String[] temp = new String[1]; + temp[0] = key; + getClient(key).brpop(temp); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> blpop(final byte[] key) { + byte[][] temp = new byte[1][]; + temp[0] = key; + getClient(key).blpop(temp); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response> brpop(final byte[] key) { + byte[][] temp = new byte[1][]; + temp[0] = key; + getClient(key).brpop(temp); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response decr(final String key) { + getClient(key).decr(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response decr(final byte[] key) { + getClient(key).decr(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response decrBy(final String key, final long decrement) { + getClient(key).decrBy(key, decrement); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response decrBy(final byte[] key, final long decrement) { + getClient(key).decrBy(key, decrement); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response del(final String key) { + getClient(key).del(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response del(final byte[] key) { + getClient(key).del(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response unlink(final String key) { + getClient(key).unlink(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response unlink(final byte[] key) { + getClient(key).unlink(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response echo(final String string) { + getClient(string).echo(string); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response echo(final byte[] string) { + getClient(string).echo(string); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response exists(final String key) { + getClient(key).exists(key); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response exists(final byte[] key) { + getClient(key).exists(key); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response expire(final String key, final int seconds) { + getClient(key).expire(key, seconds); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response expire(final byte[] key, final int seconds) { + getClient(key).expire(key, seconds); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response expireAt(final String key, final long unixTime) { + getClient(key).expireAt(key, unixTime); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response expireAt(final byte[] key, final long unixTime) { + getClient(key).expireAt(key, unixTime); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response get(final String key) { + getClient(key).get(key); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response get(final byte[] key) { + getClient(key).get(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response getbit(final String key, final long offset) { + getClient(key).getbit(key, offset); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response getbit(final byte[] key, final long offset) { + getClient(key).getbit(key, offset); + return getResponse(BuilderFactory.BOOLEAN); + } + + public Response bitpos(final String key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Response bitpos(final String key, final boolean value, final BitPosParams params) { + getClient(key).bitpos(key, value, params); + return getResponse(BuilderFactory.LONG); + } + + public Response bitpos(final byte[] key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Response bitpos(final byte[] key, final boolean value, final BitPosParams params) { + getClient(key).bitpos(key, value, params); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response getrange(final String key, final long startOffset, final long endOffset) { + getClient(key).getrange(key, startOffset, endOffset); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response getSet(final String key, final String value) { + getClient(key).getSet(key, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response getSet(final byte[] key, final byte[] value) { + getClient(key).getSet(key, value); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response getrange(final byte[] key, final long startOffset, final long endOffset) { + getClient(key).getrange(key, startOffset, endOffset); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response hdel(final String key, final String... field) { + getClient(key).hdel(key, field); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hdel(final byte[] key, final byte[]... field) { + getClient(key).hdel(key, field); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hexists(final String key, final String field) { + getClient(key).hexists(key, field); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response hexists(final byte[] key, final byte[] field) { + getClient(key).hexists(key, field); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response hget(final String key, final String field) { + getClient(key).hget(key, field); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response hget(final byte[] key, final byte[] field) { + getClient(key).hget(key, field); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response> hgetAll(final String key) { + getClient(key).hgetAll(key); + return getResponse(BuilderFactory.STRING_MAP); + } + + @Override + public Response> hgetAll(final byte[] key) { + getClient(key).hgetAll(key); + return getResponse(BuilderFactory.BYTE_ARRAY_MAP); + } + + @Override + public Response hincrBy(final String key, final String field, final long value) { + getClient(key).hincrBy(key, field, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hincrBy(final byte[] key, final byte[] field, final long value) { + getClient(key).hincrBy(key, field, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> hkeys(final String key) { + getClient(key).hkeys(key); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response> hkeys(final byte[] key) { + getClient(key).hkeys(key); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response hlen(final String key) { + getClient(key).hlen(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hlen(final byte[] key) { + getClient(key).hlen(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> hmget(final String key, final String... fields) { + getClient(key).hmget(key, fields); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> hmget(final byte[] key, final byte[]... fields) { + getClient(key).hmget(key, fields); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response hmset(final String key, final Map hash) { + getClient(key).hmset(key, hash); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response hmset(final byte[] key, final Map hash) { + getClient(key).hmset(key, hash); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response hset(final String key, final String field, final String value) { + getClient(key).hset(key, field, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hset(final byte[] key, final byte[] field, final byte[] value) { + getClient(key).hset(key, field, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hset(final String key, final Map hash) { + getClient(key).hset(key, hash); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hset(final byte[] key, final Map hash) { + getClient(key).hset(key, hash); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hsetnx(final String key, final String field, final String value) { + getClient(key).hsetnx(key, field, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hsetnx(final byte[] key, final byte[] field, final byte[] value) { + getClient(key).hsetnx(key, field, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> hvals(final String key) { + getClient(key).hvals(key); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> hvals(final byte[] key) { + getClient(key).hvals(key); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response incr(final String key) { + getClient(key).incr(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response incr(final byte[] key) { + getClient(key).incr(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response incrBy(final String key, final long increment) { + getClient(key).incrBy(key, increment); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response incrBy(final byte[] key, final long increment) { + getClient(key).incrBy(key, increment); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lindex(final String key, final long index) { + getClient(key).lindex(key, index); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response lindex(final byte[] key, final long index) { + getClient(key).lindex(key, index); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response linsert(final String key, final ListPosition where, final String pivot, final String value) { + getClient(key).linsert(key, where, pivot, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response linsert(final byte[] key, final ListPosition where, final byte[] pivot, final byte[] value) { + getClient(key).linsert(key, where, pivot, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response llen(final String key) { + getClient(key).llen(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response llen(final byte[] key) { + getClient(key).llen(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lpop(final String key) { + getClient(key).lpop(key); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response lpop(final byte[] key) { + getClient(key).lpop(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response lpush(final String key, final String... string) { + getClient(key).lpush(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lpush(final byte[] key, final byte[]... string) { + getClient(key).lpush(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lpushx(final String key, final String... string) { + getClient(key).lpushx(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lpushx(final byte[] key, final byte[]... bytes) { + getClient(key).lpushx(key, bytes); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> lrange(final String key, final long start, final long stop) { + getClient(key).lrange(key, start, stop); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> lrange(final byte[] key, final long start, final long stop) { + getClient(key).lrange(key, start, stop); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response lrem(final String key, final long count, final String value) { + getClient(key).lrem(key, count, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lrem(final byte[] key, final long count, final byte[] value) { + getClient(key).lrem(key, count, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response lset(final String key, final long index, final String value) { + getClient(key).lset(key, index, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response lset(final byte[] key, final long index, final byte[] value) { + getClient(key).lset(key, index, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response ltrim(final String key, final long start, final long stop) { + getClient(key).ltrim(key, start, stop); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response ltrim(final byte[] key, final long start, final long stop) { + getClient(key).ltrim(key, start, stop); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response move(final String key, final int dbIndex) { + getClient(key).move(key, dbIndex); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response move(final byte[] key, final int dbIndex) { + getClient(key).move(key, dbIndex); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response persist(final String key) { + getClient(key).persist(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response persist(final byte[] key) { + getClient(key).persist(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response rpop(final String key) { + getClient(key).rpop(key); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response rpop(final byte[] key) { + getClient(key).rpop(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response rpush(final String key, final String... string) { + getClient(key).rpush(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response rpush(final byte[] key, final byte[]... string) { + getClient(key).rpush(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response rpushx(final String key, final String... string) { + getClient(key).rpushx(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response rpushx(final byte[] key, final byte[]... string) { + getClient(key).rpushx(key, string); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sadd(final String key, final String... member) { + getClient(key).sadd(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sadd(final byte[] key, final byte[]... member) { + getClient(key).sadd(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response scard(final String key) { + getClient(key).scard(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response scard(final byte[] key) { + getClient(key).scard(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response set(final String key, final String value) { + getClient(key).set(key, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response set(final byte[] key, final byte[] value) { + getClient(key).set(key, value); + return getResponse(BuilderFactory.STRING); + } + + public Response set(final String key, final String value, SetParams params) { + getClient(key).set(key, value, params); + return getResponse(BuilderFactory.STRING); + } + + public Response set(final byte[] key, final byte[] value, SetParams params) { + getClient(key).set(key, value, params); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response setbit(final String key, final long offset, boolean value) { + getClient(key).setbit(key, offset, value); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response setbit(final byte[] key, final long offset, final byte[] value) { + getClient(key).setbit(key, offset, value); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response setex(final String key, final int seconds, final String value) { + getClient(key).setex(key, seconds, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response setex(final byte[] key, final int seconds, final byte[] value) { + getClient(key).setex(key, seconds, value); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response setnx(final String key, final String value) { + getClient(key).setnx(key, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response setnx(final byte[] key, final byte[] value) { + getClient(key).setnx(key, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response setrange(final String key, final long offset, final String value) { + getClient(key).setrange(key, offset, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response setrange(final byte[] key, final long offset, final byte[] value) { + getClient(key).setrange(key, offset, value); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response sismember(final String key, final String member) { + getClient(key).sismember(key, member); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response sismember(final byte[] key, final byte[] member) { + getClient(key).sismember(key, member); + return getResponse(BuilderFactory.BOOLEAN); + } + + @Override + public Response> smembers(final String key) { + getClient(key).smembers(key); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response> smembers(final byte[] key) { + getClient(key).smembers(key); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> sort(final String key) { + getClient(key).sort(key); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> sort(final byte[] key) { + getClient(key).sort(key); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response> sort(final String key, final SortingParams sortingParameters) { + getClient(key).sort(key, sortingParameters); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> sort(final byte[] key, final SortingParams sortingParameters) { + getClient(key).sort(key, sortingParameters); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response spop(final String key) { + getClient(key).spop(key); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response> spop(final String key, final long count) { + getClient(key).spop(key, count); + return getResponse(BuilderFactory.STRING_SET); + } + + @Override + public Response spop(final byte[] key) { + getClient(key).spop(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response> spop(final byte[] key, final long count) { + getClient(key).spop(key, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response srandmember(final String key) { + getClient(key).srandmember(key); + return getResponse(BuilderFactory.STRING); + } + + public Response> srandmember(final String key, final int count) { + getClient(key).srandmember(key, count); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response srandmember(final byte[] key) { + getClient(key).srandmember(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + public Response> srandmember(final byte[] key, final int count) { + getClient(key).srandmember(key, count); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response srem(final String key, final String... member) { + getClient(key).srem(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response srem(final byte[] key, final byte[]... member) { + getClient(key).srem(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response strlen(final String key) { + getClient(key).strlen(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response strlen(final byte[] key) { + getClient(key).strlen(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response substr(final String key, final int start, final int end) { + getClient(key).substr(key, start, end); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response substr(final byte[] key, final int start, final int end) { + getClient(key).substr(key, start, end); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response touch(final String key) { + getClient(key).touch(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response touch(final byte[] key) { + getClient(key).touch(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response ttl(final String key) { + getClient(key).ttl(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response ttl(final byte[] key) { + getClient(key).ttl(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response type(final String key) { + getClient(key).type(key); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response type(final byte[] key) { + getClient(key).type(key); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response zadd(final String key, final double score, final String member) { + getClient(key).zadd(key, score, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final String key, final double score, final String member, final ZAddParams params) { + getClient(key).zadd(key, score, member, params); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final String key, final Map scoreMembers) { + getClient(key).zadd(key, scoreMembers); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final String key, final Map scoreMembers, final ZAddParams params) { + getClient(key).zadd(key, scoreMembers, params); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final byte[] key, final double score, final byte[] member) { + getClient(key).zadd(key, score, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final byte[] key, final double score, final byte[] member, final ZAddParams params) { + getClient(key).zadd(key, score, member, params); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final byte[] key, final Map scoreMembers) { + getClient(key).zadd(key, scoreMembers); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zadd(final byte[] key, final Map scoreMembers, final ZAddParams params) { + getClient(key).zadd(key, scoreMembers, params); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zcard(final String key) { + getClient(key).zcard(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zcard(final byte[] key) { + getClient(key).zcard(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zcount(final String key, final double min, final double max) { + getClient(key).zcount(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zcount(final String key, final String min, final String max) { + getClient(key).zcount(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zcount(final byte[] key, final double min, final double max) { + getClient(key).zcount(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zcount(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zcount(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zincrby(final String key, final double increment, final String member) { + getClient(key).zincrby(key, increment, member); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response zincrby(final String key, final double increment, final String member, ZIncrByParams params) { + getClient(key).zincrby(key, increment, member, params); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response zincrby(final byte[] key, final double increment, final byte[] member) { + getClient(key).zincrby(key, increment, member); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response zincrby(final byte[] key, final double increment, final byte[] member, ZIncrByParams params) { + getClient(key).zincrby(key, increment, member); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response> zrange(final String key, final long start, final long stop) { + getClient(key).zrange(key, start, stop); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrange(final byte[] key, final long start, final long stop) { + getClient(key).zrange(key, start, stop); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByScore(final String key, final double min, final double max) { + getClient(key).zrangeByScore(key, min, max); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrangeByScore(final byte[] key, final double min, final double max) { + getClient(key).zrangeByScore(key, min, max); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByScore(final String key, final String min, final String max) { + getClient(key).zrangeByScore(key, min, max); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zrangeByScore(key, min, max); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByScore(final String key, final double min, final double max, final int offset, + final int count) { + getClient(key).zrangeByScore(key, min, max, offset, count); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrangeByScore(final String key, final String min, final String max, final int offset, + final int count) { + getClient(key).zrangeByScore(key, min, max, offset, count); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrangeByScore(final byte[] key, final double min, final double max, final int offset, + final int count) { + getClient(key).zrangeByScore(key, min, max, offset, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByScore(final byte[] key, final byte[] min, final byte[] max, final int offset, + final int count) { + getClient(key).zrangeByScore(key, min, max, offset, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByScoreWithScores(final String key, final double min, final double max) { + getClient(key).zrangeByScoreWithScores(key, min, max); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + public Response> zrangeByScoreWithScores(final String key, final String min, final String max) { + getClient(key).zrangeByScoreWithScores(key, min, max); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeByScoreWithScores(final byte[] key, final double min, final double max) { + getClient(key).zrangeByScoreWithScores(key, min, max); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zrangeByScoreWithScores(key, min, max); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeByScoreWithScores(final String key, final double min, final double max, + final int offset, final int count) { + getClient(key).zrangeByScoreWithScores(key, min, max, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + public Response> zrangeByScoreWithScores(final String key, final String min, final String max, + final int offset, final int count) { + getClient(key).zrangeByScoreWithScores(key, min, max, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeByScoreWithScores(final byte[] key, final double min, final double max, + final int offset, final int count) { + getClient(key).zrangeByScoreWithScores(key, min, max, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + getClient(key).zrangeByScoreWithScores(key, min, max, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScore(final String key, final double max, final double min) { + getClient(key).zrevrangeByScore(key, max, min); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrangeByScore(final byte[] key, final double max, final double min) { + getClient(key).zrevrangeByScore(key, max, min); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeByScore(final String key, final String max, final String min) { + getClient(key).zrevrangeByScore(key, max, min); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min) { + getClient(key).zrevrangeByScore(key, max, min); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeByScore(final String key, final double max, final double min, final int offset, + final int count) { + getClient(key).zrevrangeByScore(key, max, min, offset, count); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrangeByScore(final String key, final String max, final String min, final int offset, + final int count) { + getClient(key).zrevrangeByScore(key, max, min, offset, count); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrangeByScore(final byte[] key, final double max, final double min, final int offset, + final int count) { + getClient(key).zrevrangeByScore(key, max, min, offset, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min, final int offset, + final int count) { + getClient(key).zrevrangeByScore(key, max, min, offset, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final String key, final double max, final double min) { + getClient(key).zrevrangeByScoreWithScores(key, max, min); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final String key, final String max, final String min) { + getClient(key).zrevrangeByScoreWithScores(key, max, min); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final byte[] key, final double max, final double min) { + getClient(key).zrevrangeByScoreWithScores(key, max, min); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min) { + getClient(key).zrevrangeByScoreWithScores(key, max, min); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final String key, final double max, final double min, + final int offset, final int count) { + getClient(key).zrevrangeByScoreWithScores(key, max, min, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final String key, final String max, final String min, + final int offset, final int count) { + getClient(key).zrevrangeByScoreWithScores(key, max, min, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final byte[] key, final double max, final double min, + final int offset, final int count) { + getClient(key).zrevrangeByScoreWithScores(key, max, min, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + getClient(key).zrevrangeByScoreWithScores(key, max, min, offset, count); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeWithScores(final String key, final long start, final long stop) { + getClient(key).zrangeWithScores(key, start, stop); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrangeWithScores(final byte[] key, final long start, final long stop) { + getClient(key).zrangeWithScores(key, start, stop); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response zrank(final String key, final String member) { + getClient(key).zrank(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zrank(final byte[] key, final byte[] member) { + getClient(key).zrank(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zrem(final String key, final String... members) { + getClient(key).zrem(key, members); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zrem(final byte[] key, final byte[]... members) { + getClient(key).zrem(key, members); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByRank(final String key, final long start, final long stop) { + getClient(key).zremrangeByRank(key, start, stop); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByRank(final byte[] key, final long start, final long stop) { + getClient(key).zremrangeByRank(key, start, stop); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByScore(final String key, final double min, final double max) { + getClient(key).zremrangeByScore(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByScore(final String key, final String min, final String max) { + getClient(key).zremrangeByScore(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByScore(final byte[] key, final double min, final double max) { + getClient(key).zremrangeByScore(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByScore(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zremrangeByScore(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> zrevrange(final String key, final long start, final long stop) { + getClient(key).zrevrange(key, start, stop); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrange(final byte[] key, final long start, final long stop) { + getClient(key).zrevrange(key, start, stop); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeWithScores(final String key, final long start, final long stop) { + getClient(key).zrevrangeWithScores(key, start, stop); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response> zrevrangeWithScores(final byte[] key, final long start, final long stop) { + getClient(key).zrevrangeWithScores(key, start, stop); + return getResponse(BuilderFactory.TUPLE_ZSET); + } + + @Override + public Response zrevrank(final String key, final String member) { + getClient(key).zrevrank(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zrevrank(final byte[] key, final byte[] member) { + getClient(key).zrevrank(key, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zscore(final String key, final String member) { + getClient(key).zscore(key, member); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response zscore(final byte[] key, final byte[] member) { + getClient(key).zscore(key, member); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response zlexcount(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zlexcount(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zlexcount(final String key, final String min, final String max) { + getClient(key).zlexcount(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response> zrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zrangeByLex(key, min, max); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByLex(final String key, final String min, final String max) { + getClient(key).zrangeByLex(key, min, max); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrangeByLex(final byte[] key, final byte[] min, final byte[] max, + final int offset, final int count) { + getClient(key).zrangeByLex(key, min, max, offset, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrangeByLex(final String key, final String min, final String max, + final int offset, final int count) { + getClient(key).zrangeByLex(key, min, max, offset, count); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min) { + getClient(key).zrevrangeByLex(key, max, min); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeByLex(final String key, final String max, final String min) { + getClient(key).zrevrangeByLex(key, max, min); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response> zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min, + final int offset, final int count) { + getClient(key).zrevrangeByLex(key, max, min, offset, count); + return getResponse(BuilderFactory.BYTE_ARRAY_ZSET); + } + + @Override + public Response> zrevrangeByLex(final String key, final String max, final String min, + final int offset, final int count) { + getClient(key).zrevrangeByLex(key, max, min, offset, count); + return getResponse(BuilderFactory.STRING_ZSET); + } + + @Override + public Response zremrangeByLex(final byte[] key, final byte[] min, final byte[] max) { + getClient(key).zremrangeByLex(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response zremrangeByLex(final String key, final String min, final String max) { + getClient(key).zremrangeByLex(key, min, max); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response bitcount(final String key) { + getClient(key).bitcount(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response bitcount(final String key, final long start, final long end) { + getClient(key).bitcount(key, start, end); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response bitcount(final byte[] key) { + getClient(key).bitcount(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response bitcount(final byte[] key, final long start, final long end) { + getClient(key).bitcount(key, start, end); + return getResponse(BuilderFactory.LONG); + } + + public Response dump(final String key) { + getClient(key).dump(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + public Response dump(final byte[] key) { + getClient(key).dump(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + public Response migrate(final String host, final int port, final String key, final int destinationDb, final int timeout) { + getClient(key).migrate(host, port, key, destinationDb, timeout); + return getResponse(BuilderFactory.STRING); + } + + public Response migrate(final byte[] host, final int port, final byte[] key, final int destinationDb, final int timeout) { + getClient(key).migrate(host, port, key, destinationDb, timeout); + return getResponse(BuilderFactory.STRING); + } + + public Response objectRefcount(final String key) { + getClient(key).objectRefcount(key); + return getResponse(BuilderFactory.LONG); + } + + public Response objectRefcount(final byte[] key) { + getClient(key).objectRefcount(key); + return getResponse(BuilderFactory.LONG); + } + + public Response objectEncoding(final String key) { + getClient(key).objectEncoding(key); + return getResponse(BuilderFactory.STRING); + } + + public Response objectEncoding(final byte[] key) { + getClient(key).objectEncoding(key); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + public Response objectIdletime(final String key) { + getClient(key).objectIdletime(key); + return getResponse(BuilderFactory.LONG); + } + + public Response objectIdletime(final byte[] key) { + getClient(key).objectIdletime(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pexpire(final String key, final long milliseconds) { + getClient(key).pexpire(key, milliseconds); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pexpire(final byte[] key, final long milliseconds) { + getClient(key).pexpire(key, milliseconds); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pexpireAt(final String key, final long millisecondsTimestamp) { + getClient(key).pexpireAt(key, millisecondsTimestamp); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pexpireAt(final byte[] key, final long millisecondsTimestamp) { + getClient(key).pexpireAt(key, millisecondsTimestamp); + return getResponse(BuilderFactory.LONG); + } + + public Response pttl(final String key) { + getClient(key).pttl(key); + return getResponse(BuilderFactory.LONG); + } + + public Response pttl(final byte[] key) { + getClient(key).pttl(key); + return getResponse(BuilderFactory.LONG); + } + + public Response restore(final String key, final int ttl, final byte[] serializedValue) { + getClient(key).restore(key, ttl, serializedValue); + return getResponse(BuilderFactory.STRING); + } + + public Response restore(final byte[] key, final int ttl, final byte[] serializedValue) { + getClient(key).restore(key, ttl, serializedValue); + return getResponse(BuilderFactory.STRING); + } + + public Response incrByFloat(final String key, final double increment) { + getClient(key).incrByFloat(key, increment); + return getResponse(BuilderFactory.DOUBLE); + } + + public Response incrByFloat(final byte[] key, final double increment) { + getClient(key).incrByFloat(key, increment); + return getResponse(BuilderFactory.DOUBLE); + } + + public Response psetex(final String key, final long milliseconds, final String value) { + getClient(key).psetex(key, milliseconds, value); + return getResponse(BuilderFactory.STRING); + } + + public Response psetex(final byte[] key, final long milliseconds, final byte[] value) { + getClient(key).psetex(key, milliseconds, value); + return getResponse(BuilderFactory.STRING); + } + + public Response hincrByFloat(final String key, final String field, final double increment) { + getClient(key).hincrByFloat(key, field, increment); + return getResponse(BuilderFactory.DOUBLE); + } + + public Response hincrByFloat(final byte[] key, final byte[] field, final double increment) { + getClient(key).hincrByFloat(key, field, increment); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response pfadd(final byte[] key, final byte[]... elements) { + getClient(key).pfadd(key, elements); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfcount(final byte[] key) { + getClient(key).pfcount(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfadd(final String key, final String... elements) { + getClient(key).pfadd(key, elements); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfcount(final String key) { + getClient(key).pfcount(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response geoadd(final byte[] key, final double longitude, final double latitude, final byte[] member) { + getClient(key).geoadd(key, longitude, latitude, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response geoadd(final byte[] key, final Map memberCoordinateMap) { + getClient(key).geoadd(key, memberCoordinateMap); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response geoadd(final String key, final double longitude, final double latitude, final String member) { + getClient(key).geoadd(key, longitude, latitude, member); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response geoadd(final String key, final Map memberCoordinateMap) { + getClient(key).geoadd(key, memberCoordinateMap); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response geodist(final byte[] key, final byte[] member1, final byte[] member2) { + getClient(key).geodist(key, member1, member2); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response geodist(final byte[] key, final byte[] member1, final byte[] member2, final GeoUnit unit) { + getClient(key).geodist(key, member1, member2, unit); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response geodist(final String key, final String member1, final String member2) { + getClient(key).geodist(key, member1, member2); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response geodist(final String key, final String member1, final String member2, final GeoUnit unit) { + getClient(key).geodist(key, member1, member2); + return getResponse(BuilderFactory.DOUBLE); + } + + @Override + public Response> geohash(final byte[] key, final byte[]... members) { + getClient(key).geohash(key, members); + return getResponse(BuilderFactory.BYTE_ARRAY_LIST); + } + + @Override + public Response> geohash(final String key, final String... members) { + getClient(key).geohash(key, members); + return getResponse(BuilderFactory.STRING_LIST); + } + + @Override + public Response> geopos(final byte[] key, final byte[]... members) { + getClient(key).geopos(key, members); + return getResponse(BuilderFactory.GEO_COORDINATE_LIST); + } + + @Override + public Response> geopos(final String key, final String... members) { + getClient(key).geopos(key, members); + return getResponse(BuilderFactory.GEO_COORDINATE_LIST); + } + + @Override + public Response> georadius(final byte[] key, final double longitude, final double latitude, + final double radius, final GeoUnit unit) { + getClient(key).georadius(key, longitude, latitude, radius, unit); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadius(final byte[] key, final double longitude, final double latitude, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + getClient(key).georadius(key, longitude, latitude, radius, unit, param); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadius(final String key, final double longitude, final double latitude, + final double radius, final GeoUnit unit) { + getClient(key).georadius(key, longitude, latitude, radius, unit); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadius(final String key, final double longitude, final double latitude, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + getClient(key).georadius(key, longitude, latitude, radius, unit, param); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadiusByMember(final byte[] key, final byte[] member, + final double radius, final GeoUnit unit) { + getClient(key).georadiusByMember(key, member, radius, unit); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadiusByMember(final byte[] key, final byte[] member, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + getClient(key).georadiusByMember(key, member, radius, unit, param); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadiusByMember(final String key, final String member, + final double radius, final GeoUnit unit) { + getClient(key).georadiusByMember(key, member, radius, unit); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> georadiusByMember(final String key, final String member, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + getClient(key).georadiusByMember(key, member, radius, unit, param); + return getResponse(BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); + } + + @Override + public Response> bitfield(final String key, final String... elements) { + getClient(key).bitfield(key, elements); + return getResponse(BuilderFactory.LONG_LIST); + } + + @Override + public Response> bitfield(final byte[] key, final byte[]... elements) { + getClient(key).bitfield(key, elements); + return getResponse(BuilderFactory.LONG_LIST); + } + + @Override + public Response hstrlen(final String key, final String field) { + getClient(key).hstrlen(key, field); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response hstrlen(final byte[] key, final byte[] field) { + getClient(key).hstrlen(key, field); + return getResponse(BuilderFactory.LONG); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Protocol.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Protocol.java new file mode 100755 index 000000000..2de438733 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Protocol.java @@ -0,0 +1,281 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import com.fr.third.redis.clients.jedis.commands.ProtocolCommand; +import com.fr.third.redis.clients.jedis.exceptions.JedisAskDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisBusyException; +import com.fr.third.redis.clients.jedis.exceptions.JedisClusterException; +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisMovedDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisNoScriptException; +import com.fr.third.redis.clients.jedis.util.RedisInputStream; +import com.fr.third.redis.clients.jedis.util.RedisOutputStream; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public final class Protocol { + + private static final String ASK_RESPONSE = "ASK"; + private static final String MOVED_RESPONSE = "MOVED"; + private static final String CLUSTERDOWN_RESPONSE = "CLUSTERDOWN"; + private static final String BUSY_RESPONSE = "BUSY"; + private static final String NOSCRIPT_RESPONSE = "NOSCRIPT"; + + public static final String DEFAULT_HOST = "localhost"; + public static final int DEFAULT_PORT = 6379; + public static final int DEFAULT_SENTINEL_PORT = 26379; + public static final int DEFAULT_TIMEOUT = 2000; + public static final int DEFAULT_DATABASE = 0; + + public static final String CHARSET = "UTF-8"; + + public static final byte DOLLAR_BYTE = '$'; + public static final byte ASTERISK_BYTE = '*'; + public static final byte PLUS_BYTE = '+'; + public static final byte MINUS_BYTE = '-'; + public static final byte COLON_BYTE = ':'; + + public static final String SENTINEL_MASTERS = "masters"; + public static final String SENTINEL_GET_MASTER_ADDR_BY_NAME = "get-master-addr-by-name"; + public static final String SENTINEL_RESET = "reset"; + public static final String SENTINEL_SLAVES = "slaves"; + public static final String SENTINEL_FAILOVER = "failover"; + public static final String SENTINEL_MONITOR = "monitor"; + public static final String SENTINEL_REMOVE = "remove"; + public static final String SENTINEL_SET = "set"; + + public static final String CLUSTER_NODES = "nodes"; + public static final String CLUSTER_MEET = "meet"; + public static final String CLUSTER_RESET = "reset"; + public static final String CLUSTER_ADDSLOTS = "addslots"; + public static final String CLUSTER_DELSLOTS = "delslots"; + public static final String CLUSTER_INFO = "info"; + public static final String CLUSTER_GETKEYSINSLOT = "getkeysinslot"; + public static final String CLUSTER_SETSLOT = "setslot"; + public static final String CLUSTER_SETSLOT_NODE = "node"; + public static final String CLUSTER_SETSLOT_MIGRATING = "migrating"; + public static final String CLUSTER_SETSLOT_IMPORTING = "importing"; + public static final String CLUSTER_SETSLOT_STABLE = "stable"; + public static final String CLUSTER_FORGET = "forget"; + public static final String CLUSTER_FLUSHSLOT = "flushslots"; + public static final String CLUSTER_KEYSLOT = "keyslot"; + public static final String CLUSTER_COUNTKEYINSLOT = "countkeysinslot"; + public static final String CLUSTER_SAVECONFIG = "saveconfig"; + public static final String CLUSTER_REPLICATE = "replicate"; + public static final String CLUSTER_SLAVES = "slaves"; + public static final String CLUSTER_FAILOVER = "failover"; + public static final String CLUSTER_SLOTS = "slots"; + public static final String PUBSUB_CHANNELS = "channels"; + public static final String PUBSUB_NUMSUB = "numsub"; + public static final String PUBSUB_NUM_PAT = "numpat"; + + public static final byte[] BYTES_TRUE = toByteArray(1); + public static final byte[] BYTES_FALSE = toByteArray(0); + + public static final byte[] POSITIVE_INFINITY_BYTES = "+inf".getBytes(); + public static final byte[] NEGATIVE_INFINITY_BYTES = "-inf".getBytes(); + + private Protocol() { + // this prevent the class from instantiation + } + + public static void sendCommand(final RedisOutputStream os, final ProtocolCommand command, + final byte[]... args) { + sendCommand(os, command.getRaw(), args); + } + + private static void sendCommand(final RedisOutputStream os, final byte[] command, + final byte[]... args) { + try { + os.write(ASTERISK_BYTE); + os.writeIntCrLf(args.length + 1); + os.write(DOLLAR_BYTE); + os.writeIntCrLf(command.length); + os.write(command); + os.writeCrLf(); + + for (final byte[] arg : args) { + os.write(DOLLAR_BYTE); + os.writeIntCrLf(arg.length); + os.write(arg); + os.writeCrLf(); + } + } catch (IOException e) { + throw new JedisConnectionException(e); + } + } + + private static void processError(final RedisInputStream is) { + String message = is.readLine(); + // TODO: I'm not sure if this is the best way to do this. + // Maybe Read only first 5 bytes instead? + if (message.startsWith(MOVED_RESPONSE)) { + String[] movedInfo = parseTargetHostAndSlot(message); + throw new JedisMovedDataException(message, new HostAndPort(movedInfo[1], + Integer.parseInt(movedInfo[2])), Integer.parseInt(movedInfo[0])); + } else if (message.startsWith(ASK_RESPONSE)) { + String[] askInfo = parseTargetHostAndSlot(message); + throw new JedisAskDataException(message, new HostAndPort(askInfo[1], + Integer.parseInt(askInfo[2])), Integer.parseInt(askInfo[0])); + } else if (message.startsWith(CLUSTERDOWN_RESPONSE)) { + throw new JedisClusterException(message); + } else if (message.startsWith(BUSY_RESPONSE)) { + throw new JedisBusyException(message); + } else if (message.startsWith(NOSCRIPT_RESPONSE) ) { + throw new JedisNoScriptException(message); + } + throw new JedisDataException(message); + } + + public static String readErrorLineIfPossible(RedisInputStream is) { + final byte b = is.readByte(); + // if buffer contains other type of response, just ignore. + if (b != MINUS_BYTE) { + return null; + } + return is.readLine(); + } + + private static String[] parseTargetHostAndSlot(String clusterRedirectResponse) { + String[] response = new String[3]; + String[] messageInfo = clusterRedirectResponse.split(" "); + String[] targetHostAndPort = HostAndPort.extractParts(messageInfo[2]); + response[0] = messageInfo[1]; + response[1] = targetHostAndPort[0]; + response[2] = targetHostAndPort[1]; + return response; + } + + private static Object process(final RedisInputStream is) { + + final byte b = is.readByte(); + if (b == PLUS_BYTE) { + return processStatusCodeReply(is); + } else if (b == DOLLAR_BYTE) { + return processBulkReply(is); + } else if (b == ASTERISK_BYTE) { + return processMultiBulkReply(is); + } else if (b == COLON_BYTE) { + return processInteger(is); + } else if (b == MINUS_BYTE) { + processError(is); + return null; + } else { + throw new JedisConnectionException("Unknown reply: " + (char) b); + } + } + + private static byte[] processStatusCodeReply(final RedisInputStream is) { + return is.readLineBytes(); + } + + private static byte[] processBulkReply(final RedisInputStream is) { + final int len = is.readIntCrLf(); + if (len == -1) { + return null; + } + + final byte[] read = new byte[len]; + int offset = 0; + while (offset < len) { + final int size = is.read(read, offset, (len - offset)); + if (size == -1) throw new JedisConnectionException( + "It seems like server has closed the connection."); + offset += size; + } + + // read 2 more bytes for the command delimiter + is.readByte(); + is.readByte(); + + return read; + } + + private static Long processInteger(final RedisInputStream is) { + return is.readLongCrLf(); + } + + private static List processMultiBulkReply(final RedisInputStream is) { + final int num = is.readIntCrLf(); + if (num == -1) { + return null; + } + final List ret = new ArrayList(num); + for (int i = 0; i < num; i++) { + try { + ret.add(process(is)); + } catch (JedisDataException e) { + ret.add(e); + } + } + return ret; + } + + public static Object read(final RedisInputStream is) { + return process(is); + } + + public static final byte[] toByteArray(final boolean value) { + return value ? BYTES_TRUE : BYTES_FALSE; + } + + public static final byte[] toByteArray(final int value) { + return SafeEncoder.encode(String.valueOf(value)); + } + + public static final byte[] toByteArray(final long value) { + return SafeEncoder.encode(String.valueOf(value)); + } + + public static final byte[] toByteArray(final double value) { + if (value == Double.POSITIVE_INFINITY) { + return POSITIVE_INFINITY_BYTES; + } else if (value == Double.NEGATIVE_INFINITY) { + return NEGATIVE_INFINITY_BYTES; + } else { + return SafeEncoder.encode(String.valueOf(value)); + } + } + + public static enum Command implements ProtocolCommand { + PING, SET, GET, QUIT, EXISTS, DEL, UNLINK, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, + DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, + DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, + HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, + RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, + SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, + ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, + SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBSUB, ZCOUNT, ZRANGEBYSCORE, + ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, ZLEXCOUNT, + ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, + INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, + SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, + SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, + SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, PFADD, PFCOUNT, PFMERGE, READONLY, GEOADD, GEODIST, + GEOHASH, GEOPOS, GEORADIUS, GEORADIUSBYMEMBER, MODULE, BITFIELD, HSTRLEN, TOUCH, SWAPDB; + + private final byte[] raw; + + Command() { + raw = SafeEncoder.encode(this.name()); + } + + @Override + public byte[] getRaw() { + return raw; + } + } + + public static enum Keyword { + AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE, PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES, RESETSTAT, REWRITE, RESET, FLUSH, EXISTS, LOAD, KILL, LEN, REFCOUNT, ENCODING, IDLETIME, GETNAME, SETNAME, LIST, MATCH, COUNT, PING, PONG, UNLOAD; + public final byte[] raw; + + Keyword() { + raw = SafeEncoder.encode(this.name().toLowerCase(Locale.ENGLISH)); + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Queable.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Queable.java new file mode 100755 index 000000000..40613e13c --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Queable.java @@ -0,0 +1,34 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.LinkedList; +import java.util.Queue; + +public class Queable { + private Queue> pipelinedResponses = new LinkedList>(); + + protected void clean() { + pipelinedResponses.clear(); + } + + protected Response generateResponse(Object data) { + Response response = pipelinedResponses.poll(); + if (response != null) { + response.set(data); + } + return response; + } + + protected Response getResponse(Builder builder) { + Response lr = new Response(builder); + pipelinedResponses.add(lr); + return lr; + } + + protected boolean hasPipelinedResponse() { + return !pipelinedResponses.isEmpty(); + } + + protected int getPipelinedResponseLength() { + return pipelinedResponses.size(); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Response.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Response.java new file mode 100755 index 000000000..c50736c30 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Response.java @@ -0,0 +1,77 @@ +package com.fr.third.redis.clients.jedis; + +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; + +public class Response { + protected T response = null; + protected JedisDataException exception = null; + + private boolean building = false; + private boolean built = false; + private boolean set = false; + + private Builder builder; + private Object data; + private Response dependency = null; + + public Response(Builder b) { + this.builder = b; + } + + public void set(Object data) { + this.data = data; + set = true; + } + + public T get() { + // if response has dependency response and dependency is not built, + // build it first and no more!! + if (dependency != null && dependency.set && !dependency.built) { + dependency.build(); + } + if (!set) { + throw new JedisDataException( + "Please close pipeline or multi block before calling this method."); + } + if (!built) { + build(); + } + if (exception != null) { + throw exception; + } + return response; + } + + public void setDependency(Response dependency) { + this.dependency = dependency; + } + + private void build() { + // check build state to prevent recursion + if (building) { + return; + } + + building = true; + try { + if (data != null) { + if (data instanceof JedisDataException) { + exception = (JedisDataException) data; + } else { + response = builder.build(data); + } + } + + data = null; + } finally { + building = false; + built = true; + } + } + + @Override + public String toString() { + return "Response " + builder.toString(); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ScanParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ScanParams.java new file mode 100755 index 000000000..7e874f321 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ScanParams.java @@ -0,0 +1,78 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.COUNT; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.MATCH; + +import com.fr.third.redis.clients.jedis.Protocol.Keyword; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class ScanParams { + + private final Map params = new EnumMap(Keyword.class); + + public final static String SCAN_POINTER_START = String.valueOf(0); + public final static byte[] SCAN_POINTER_START_BINARY = SafeEncoder.encode(SCAN_POINTER_START); + + public ScanParams match(final byte[] pattern) { + params.put(MATCH, ByteBuffer.wrap(pattern)); + return this; + } + + /** + * @see MATCH option in Redis documentation + */ + public ScanParams match(final String pattern) { + params.put(MATCH, ByteBuffer.wrap(SafeEncoder.encode(pattern))); + return this; + } + + /** + * @see COUNT option in Redis documentation + */ + public ScanParams count(final Integer count) { + params.put(COUNT, ByteBuffer.wrap(Protocol.toByteArray(count))); + return this; + } + + public Collection getParams() { + List paramsList = new ArrayList(params.size()); + for (Map.Entry param : params.entrySet()) { + paramsList.add(param.getKey().raw); + paramsList.add(param.getValue().array()); + } + return Collections.unmodifiableCollection(paramsList); + } + + byte[] binaryMatch() { + if (params.containsKey(MATCH)) { + return params.get(MATCH).array(); + } else { + return null; + } + } + + String match() { + if (params.containsKey(MATCH)) { + return new String(params.get(MATCH).array()); + } else { + return null; + } + } + + Integer count() { + if (params.containsKey(COUNT)) { + return params.get(COUNT).getInt(); + } else { + return null; + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ScanResult.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ScanResult.java new file mode 100755 index 000000000..c45c9dd7f --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ScanResult.java @@ -0,0 +1,48 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.List; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class ScanResult { + private byte[] cursor; + private List results; + + public ScanResult(String cursor, List results) { + this(SafeEncoder.encode(cursor), results); + } + + public ScanResult(byte[] cursor, List results) { + this.cursor = cursor; + this.results = results; + } + + /** + * Returns the new value of the cursor + * @return the new cursor value. {@link ScanParams#SCAN_POINTER_START} when a complete iteration has finished + */ + public String getCursor() { + return SafeEncoder.encode(cursor); + } + + /** + * Is the iteration complete. I.e. was the complete dataset scanned. + * + * @return true if the iteration is complete + */ + public boolean isCompleteIteration() { + return ScanParams.SCAN_POINTER_START.equals(getCursor()); + } + + public byte[] getCursorAsBytes() { + return cursor; + } + + /** + * The scan results from the current call. + * @return the scan results + */ + public List getResult() { + return results; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedis.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedis.java new file mode 100755 index 000000000..6498330f1 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedis.java @@ -0,0 +1,934 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.Closeable; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Pattern; + +import com.fr.third.redis.clients.jedis.commands.JedisCommands; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; +import com.fr.third.redis.clients.jedis.util.Hashing; + +public class ShardedJedis extends BinaryShardedJedis implements JedisCommands, Closeable { + + protected ShardedJedisPool dataSource = null; + + public ShardedJedis(List shards) { + super(shards); + } + + public ShardedJedis(List shards, Hashing algo) { + super(shards, algo); + } + + public ShardedJedis(List shards, Pattern keyTagPattern) { + super(shards, keyTagPattern); + } + + public ShardedJedis(List shards, Hashing algo, Pattern keyTagPattern) { + super(shards, algo, keyTagPattern); + } + + @Override + public String set(final String key, final String value) { + Jedis j = getShard(key); + return j.set(key, value); + } + + @Override + public String set(final String key, final String value, SetParams params) { + Jedis j = getShard(key); + return j.set(key, value, params); + } + + @Override + public String get(final String key) { + Jedis j = getShard(key); + return j.get(key); + } + + @Override + public String echo(final String string) { + Jedis j = getShard(string); + return j.echo(string); + } + + @Override + public Boolean exists(final String key) { + Jedis j = getShard(key); + return j.exists(key); + } + + @Override + public String type(final String key) { + Jedis j = getShard(key); + return j.type(key); + } + + @Override + public byte[] dump(final String key) { + Jedis j = getShard(key); + return j.dump(key); + } + + @Override + public String restore(final String key, final int ttl, final byte[] serializedValue) { + Jedis j = getShard(key); + return j.restore(key, ttl, serializedValue); + } + + @Override + public Long expire(final String key, final int seconds) { + Jedis j = getShard(key); + return j.expire(key, seconds); + } + + @Override + public Long pexpire(final String key, final long milliseconds) { + Jedis j = getShard(key); + return j.pexpire(key, milliseconds); + } + + @Override + public Long expireAt(final String key, final long unixTime) { + Jedis j = getShard(key); + return j.expireAt(key, unixTime); + } + + @Override + public Long pexpireAt(final String key, final long millisecondsTimestamp) { + Jedis j = getShard(key); + return j.pexpireAt(key, millisecondsTimestamp); + } + + @Override + public Long ttl(final String key) { + Jedis j = getShard(key); + return j.ttl(key); + } + + @Override + public Long pttl(final String key) { + Jedis j = getShard(key); + return j.pttl(key); + } + + @Override + public Boolean setbit(final String key, final long offset, boolean value) { + Jedis j = getShard(key); + return j.setbit(key, offset, value); + } + + @Override + public Boolean setbit(final String key, final long offset, final String value) { + Jedis j = getShard(key); + return j.setbit(key, offset, value); + } + + @Override + public Boolean getbit(final String key, final long offset) { + Jedis j = getShard(key); + return j.getbit(key, offset); + } + + @Override + public Long setrange(final String key, final long offset, final String value) { + Jedis j = getShard(key); + return j.setrange(key, offset, value); + } + + @Override + public String getrange(final String key, final long startOffset, final long endOffset) { + Jedis j = getShard(key); + return j.getrange(key, startOffset, endOffset); + } + + @Override + public String getSet(final String key, final String value) { + Jedis j = getShard(key); + return j.getSet(key, value); + } + + @Override + public Long setnx(final String key, final String value) { + Jedis j = getShard(key); + return j.setnx(key, value); + } + + @Override + public String setex(final String key, final int seconds, final String value) { + Jedis j = getShard(key); + return j.setex(key, seconds, value); + } + + @Override + public String psetex(final String key, final long milliseconds, final String value) { + Jedis j = getShard(key); + return j.psetex(key, milliseconds, value); + } + + public List blpop(final String arg) { + Jedis j = getShard(arg); + return j.blpop(arg); + } + + @Override + public List blpop(final int timeout, final String key) { + Jedis j = getShard(key); + return j.blpop(timeout, key); + } + + public List brpop(final String arg) { + Jedis j = getShard(arg); + return j.brpop(arg); + } + + @Override + public List brpop(final int timeout, final String key) { + Jedis j = getShard(key); + return j.brpop(timeout, key); + } + + @Override + public Long decrBy(final String key, final long decrement) { + Jedis j = getShard(key); + return j.decrBy(key, decrement); + } + + @Override + public Long decr(final String key) { + Jedis j = getShard(key); + return j.decr(key); + } + + @Override + public Long incrBy(final String key, final long increment) { + Jedis j = getShard(key); + return j.incrBy(key, increment); + } + + @Override + public Double incrByFloat(final String key, final double increment) { + Jedis j = getShard(key); + return j.incrByFloat(key, increment); + } + + @Override + public Long incr(final String key) { + Jedis j = getShard(key); + return j.incr(key); + } + + @Override + public Long append(final String key, final String value) { + Jedis j = getShard(key); + return j.append(key, value); + } + + @Override + public String substr(final String key, final int start, final int end) { + Jedis j = getShard(key); + return j.substr(key, start, end); + } + + @Override + public Long hset(final String key, final String field, final String value) { + Jedis j = getShard(key); + return j.hset(key, field, value); + } + + @Override + public Long hset(final String key, final Map hash) { + Jedis j = getShard(key); + return j.hset(key, hash); + } + + @Override + public String hget(final String key, final String field) { + Jedis j = getShard(key); + return j.hget(key, field); + } + + @Override + public Long hsetnx(final String key, final String field, final String value) { + Jedis j = getShard(key); + return j.hsetnx(key, field, value); + } + + @Override + public String hmset(final String key, final Map hash) { + Jedis j = getShard(key); + return j.hmset(key, hash); + } + + @Override + public List hmget(final String key, String... fields) { + Jedis j = getShard(key); + return j.hmget(key, fields); + } + + @Override + public Long hincrBy(final String key, final String field, final long value) { + Jedis j = getShard(key); + return j.hincrBy(key, field, value); + } + + @Override + public Double hincrByFloat(final String key, final String field, final double value) { + Jedis j = getShard(key); + return j.hincrByFloat(key, field, value); + } + + @Override + public Boolean hexists(final String key, final String field) { + Jedis j = getShard(key); + return j.hexists(key, field); + } + + @Override + public Long del(final String key) { + Jedis j = getShard(key); + return j.del(key); + } + + @Override + public Long unlink(final String key) { + Jedis j = getShard(key); + return j.unlink(key); + } + + @Override + public Long hdel(final String key, String... fields) { + Jedis j = getShard(key); + return j.hdel(key, fields); + } + + @Override + public Long hlen(final String key) { + Jedis j = getShard(key); + return j.hlen(key); + } + + @Override + public Set hkeys(final String key) { + Jedis j = getShard(key); + return j.hkeys(key); + } + + @Override + public List hvals(final String key) { + Jedis j = getShard(key); + return j.hvals(key); + } + + @Override + public Map hgetAll(final String key) { + Jedis j = getShard(key); + return j.hgetAll(key); + } + + @Override + public Long rpush(final String key, String... strings) { + Jedis j = getShard(key); + return j.rpush(key, strings); + } + + @Override + public Long lpush(final String key, String... strings) { + Jedis j = getShard(key); + return j.lpush(key, strings); + } + + @Override + public Long lpushx(final String key, String... string) { + Jedis j = getShard(key); + return j.lpushx(key, string); + } + + @Override + public Long strlen(final String key) { + Jedis j = getShard(key); + return j.strlen(key); + } + + @Override + public Long move(final String key, final int dbIndex) { + Jedis j = getShard(key); + return j.move(key, dbIndex); + } + + @Override + public Long rpushx(final String key, String... string) { + Jedis j = getShard(key); + return j.rpushx(key, string); + } + + @Override + public Long persist(final String key) { + Jedis j = getShard(key); + return j.persist(key); + } + + @Override + public Long llen(final String key) { + Jedis j = getShard(key); + return j.llen(key); + } + + @Override + public List lrange(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.lrange(key, start, stop); + } + + @Override + public String ltrim(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.ltrim(key, start, stop); + } + + @Override + public String lindex(final String key, final long index) { + Jedis j = getShard(key); + return j.lindex(key, index); + } + + @Override + public String lset(final String key, final long index, final String value) { + Jedis j = getShard(key); + return j.lset(key, index, value); + } + + @Override + public Long lrem(final String key, final long count, final String value) { + Jedis j = getShard(key); + return j.lrem(key, count, value); + } + + @Override + public String lpop(final String key) { + Jedis j = getShard(key); + return j.lpop(key); + } + + @Override + public String rpop(final String key) { + Jedis j = getShard(key); + return j.rpop(key); + } + + @Override + public Long sadd(final String key, String... members) { + Jedis j = getShard(key); + return j.sadd(key, members); + } + + @Override + public Set smembers(final String key) { + Jedis j = getShard(key); + return j.smembers(key); + } + + @Override + public Long srem(final String key, String... members) { + Jedis j = getShard(key); + return j.srem(key, members); + } + + @Override + public String spop(final String key) { + Jedis j = getShard(key); + return j.spop(key); + } + + @Override + public Set spop(final String key, final long count) { + Jedis j = getShard(key); + return j.spop(key, count); + } + + @Override + public Long scard(final String key) { + Jedis j = getShard(key); + return j.scard(key); + } + + @Override + public Boolean sismember(final String key, final String member) { + Jedis j = getShard(key); + return j.sismember(key, member); + } + + @Override + public String srandmember(final String key) { + Jedis j = getShard(key); + return j.srandmember(key); + } + + @Override + public List srandmember(final String key, final int count) { + Jedis j = getShard(key); + return j.srandmember(key, count); + } + + @Override + public Long zadd(final String key, final double score, final String member) { + Jedis j = getShard(key); + return j.zadd(key, score, member); + } + + @Override + public Long zadd(final String key, final double score, final String member, final ZAddParams params) { + Jedis j = getShard(key); + return j.zadd(key, score, member, params); + } + + @Override + public Long zadd(final String key, final Map scoreMembers) { + Jedis j = getShard(key); + return j.zadd(key, scoreMembers); + } + + @Override + public Long zadd(final String key, final Map scoreMembers, final ZAddParams params) { + Jedis j = getShard(key); + return j.zadd(key, scoreMembers, params); + } + + @Override + public Set zrange(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrange(key, start, stop); + } + + @Override + public Long zrem(final String key, String... members) { + Jedis j = getShard(key); + return j.zrem(key, members); + } + + @Override + public Double zincrby(final String key, final double increment, final String member) { + Jedis j = getShard(key); + return j.zincrby(key, increment, member); + } + + @Override + public Double zincrby(final String key, final double increment, final String member, ZIncrByParams params) { + Jedis j = getShard(key); + return j.zincrby(key, increment, member, params); + } + + @Override + public Long zrank(final String key, final String member) { + Jedis j = getShard(key); + return j.zrank(key, member); + } + + @Override + public Long zrevrank(final String key, final String member) { + Jedis j = getShard(key); + return j.zrevrank(key, member); + } + + @Override + public Set zrevrange(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrevrange(key, start, stop); + } + + @Override + public Set zrangeWithScores(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrangeWithScores(key, start, stop); + } + + @Override + public Set zrevrangeWithScores(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zrevrangeWithScores(key, start, stop); + } + + @Override + public Long zcard(final String key) { + Jedis j = getShard(key); + return j.zcard(key); + } + + @Override + public Double zscore(final String key, final String member) { + Jedis j = getShard(key); + return j.zscore(key, member); + } + + @Override + public List sort(final String key) { + Jedis j = getShard(key); + return j.sort(key); + } + + @Override + public List sort(final String key, final SortingParams sortingParameters) { + Jedis j = getShard(key); + return j.sort(key, sortingParameters); + } + + @Override + public Long zcount(final String key, final double min, final double max) { + Jedis j = getShard(key); + return j.zcount(key, min, max); + } + + @Override + public Long zcount(final String key, final String min, final String max) { + Jedis j = getShard(key); + return j.zcount(key, min, max); + } + + @Override + public Set zrangeByScore(final String key, final double min, final double max) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max); + } + + @Override + public Set zrevrangeByScore(final String key, final double max, final double min) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min); + } + + @Override + public Set zrangeByScore(final String key, final double min, final double max, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByScore(final String key, final double max, final double min, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min, offset, count); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final double min, final double max) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final double max, final double min) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final double min, final double max, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final double max, final double min, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + + @Override + public Set zrangeByScore(final String key, final String min, final String max) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max); + } + + @Override + public Set zrevrangeByScore(final String key, final String max, final String min) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min); + } + + @Override + public Set zrangeByScore(final String key, final String min, final String max, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrangeByScore(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByScore(final String key, final String max, final String min, final int offset, final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScore(key, max, min, offset, count); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final String min, final String max) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final String max, final String min) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min); + } + + @Override + public Set zrangeByScoreWithScores(final String key, final String min, final String max, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrangeByScoreWithScores(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByScoreWithScores(final String key, final String max, final String min, final int offset, + final int count) { + Jedis j = getShard(key); + return j.zrevrangeByScoreWithScores(key, max, min, offset, count); + } + + @Override + public Long zremrangeByRank(final String key, final long start, final long stop) { + Jedis j = getShard(key); + return j.zremrangeByRank(key, start, stop); + } + + @Override + public Long zremrangeByScore(final String key, final double min, final double max) { + Jedis j = getShard(key); + return j.zremrangeByScore(key, min, max); + } + + @Override + public Long zremrangeByScore(final String key, final String min, final String max) { + Jedis j = getShard(key); + return j.zremrangeByScore(key, min, max); + } + + @Override + public Long zlexcount(final String key, final String min, final String max) { + return getShard(key).zlexcount(key, min, max); + } + + @Override + public Set zrangeByLex(final String key, final String min, final String max) { + return getShard(key).zrangeByLex(key, min, max); + } + + @Override + public Set zrangeByLex(final String key, final String min, final String max, + final int offset, final int count) { + return getShard(key).zrangeByLex(key, min, max, offset, count); + } + + @Override + public Set zrevrangeByLex(final String key, final String max, final String min) { + return getShard(key).zrevrangeByLex(key, max, min); + } + + @Override + public Set zrevrangeByLex(final String key, final String max, final String min, final int offset, final int count) { + return getShard(key).zrevrangeByLex(key, max, min, offset, count); + } + + @Override + public Long zremrangeByLex(final String key, final String min, final String max) { + return getShard(key).zremrangeByLex(key, min, max); + } + + @Override + public Long linsert(final String key, final ListPosition where, final String pivot, final String value) { + Jedis j = getShard(key); + return j.linsert(key, where, pivot, value); + } + + @Override + public Long bitcount(final String key) { + Jedis j = getShard(key); + return j.bitcount(key); + } + + @Override + public Long bitcount(final String key, final long start, final long end) { + Jedis j = getShard(key); + return j.bitcount(key, start, end); + } + + @Override + public Long bitpos(final String key, final boolean value) { + Jedis j = getShard(key); + return j.bitpos(key, value); + } + + @Override + public Long bitpos(final String key, boolean value, final BitPosParams params) { + Jedis j = getShard(key); + return j.bitpos(key, value, params); + } + + @Override + public ScanResult> hscan(final String key, final String cursor) { + Jedis j = getShard(key); + return j.hscan(key, cursor); + } + + @Override + public ScanResult> hscan(final String key, final String cursor, final ScanParams params) { + Jedis j = getShard(key); + return j.hscan(key, cursor, params); + } + + @Override + public ScanResult sscan(final String key, final String cursor) { + Jedis j = getShard(key); + return j.sscan(key, cursor); + } + + @Override + public ScanResult zscan(final String key, final String cursor) { + Jedis j = getShard(key); + return j.zscan(key, cursor); + } + + @Override + public ScanResult zscan(final String key, final String cursor, final ScanParams params) { + Jedis j = getShard(key); + return j.zscan(key, cursor, params); + } + + @Override + public ScanResult sscan(final String key, final String cursor, final ScanParams params) { + Jedis j = getShard(key); + return j.sscan(key, cursor, params); + } + + @Override + public void close() { + if (dataSource != null) { + boolean broken = false; + + for (Jedis jedis : getAllShards()) { + if (jedis.getClient().isBroken()) { + broken = true; + break; + } + } + + if (broken) { + dataSource.returnBrokenResource(this); + } else { + dataSource.returnResource(this); + } + + } else { + disconnect(); + } + } + + public void setDataSource(ShardedJedisPool shardedJedisPool) { + this.dataSource = shardedJedisPool; + } + + public void resetState() { + for (Jedis jedis : getAllShards()) { + jedis.resetState(); + } + } + + @Override + public Long pfadd(final String key, final String... elements) { + Jedis j = getShard(key); + return j.pfadd(key, elements); + } + + @Override + public long pfcount(final String key) { + Jedis j = getShard(key); + return j.pfcount(key); + } + + @Override + public Long touch(final String key) { + Jedis j = getShard(key); + return j.touch(key); + } + + @Override + public Long geoadd(final String key, final double longitude, final double latitude, final String member) { + Jedis j = getShard(key); + return j.geoadd(key, longitude, latitude, member); + } + + @Override + public Long geoadd(final String key, final Map memberCoordinateMap) { + Jedis j = getShard(key); + return j.geoadd(key, memberCoordinateMap); + } + + @Override + public Double geodist(final String key, final String member1, final String member2) { + Jedis j = getShard(key); + return j.geodist(key, member1, member2); + } + + @Override + public Double geodist(final String key, final String member1, final String member2, final GeoUnit unit) { + Jedis j = getShard(key); + return j.geodist(key, member1, member2, unit); + } + + @Override + public List geohash(final String key, final String... members) { + Jedis j = getShard(key); + return j.geohash(key, members); + } + + @Override + public List geopos(final String key, final String... members) { + Jedis j = getShard(key); + return j.geopos(key, members); + } + + @Override + public List georadius(final String key, final double longitude, final double latitude, + final double radius, final GeoUnit unit) { + Jedis j = getShard(key); + return j.georadius(key, longitude, latitude, radius, unit); + } + + @Override + public List georadius(final String key, final double longitude, final double latitude, + final double radius, final GeoUnit unit, final GeoRadiusParam param) { + Jedis j = getShard(key); + return j.georadius(key, longitude, latitude, radius, unit, param); + } + + @Override + public List georadiusByMember(final String key, final String member, final double radius, + final GeoUnit unit) { + Jedis j = getShard(key); + return j.georadiusByMember(key, member, radius, unit); + } + + @Override + public List georadiusByMember(final String key, final String member, final double radius, + final GeoUnit unit, final GeoRadiusParam param) { + Jedis j = getShard(key); + return j.georadiusByMember(key, member, radius, unit, param); + } + + @Override + public List bitfield(final String key, final String... arguments) { + Jedis j = getShard(key); + return j.bitfield(key, arguments); + } + + @Override + public Long hstrlen(final String key, final String field) { + Jedis j = getShard(key); + return j.hstrlen(key, field); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPipeline.java new file mode 100755 index 000000000..cc1e00f57 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPipeline.java @@ -0,0 +1,77 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class ShardedJedisPipeline extends PipelineBase { + private BinaryShardedJedis jedis; + private List results = new ArrayList(); + private Queue clients = new LinkedList(); + + private static class FutureResult { + private Client client; + + public FutureResult(Client client) { + this.client = client; + } + + public Object get() { + return client.getOne(); + } + } + + public void setShardedJedis(BinaryShardedJedis jedis) { + this.jedis = jedis; + } + + public List getResults() { + List r = new ArrayList(); + for (FutureResult fr : results) { + r.add(fr.get()); + } + return r; + } + + /** + * Syncronize pipeline by reading all responses. This operation closes the pipeline. In order to + * get return values from pipelined commands, capture the different Response<?> of the + * commands you execute. + */ + public void sync() { + for (Client client : clients) { + generateResponse(client.getOne()); + } + } + + /** + * Syncronize pipeline by reading all responses. This operation closes the pipeline. Whenever + * possible try to avoid using this version and use ShardedJedisPipeline.sync() as it won't go + * through all the responses and generate the right response type (usually it is a waste of time). + * @return A list of all the responses in the order you executed them. + */ + public List syncAndReturnAll() { + List formatted = new ArrayList(); + for (Client client : clients) { + formatted.add(generateResponse(client.getOne()).get()); + } + return formatted; + } + + @Override + protected Client getClient(String key) { + Client client = jedis.getShard(key).getClient(); + clients.add(client); + results.add(new FutureResult(client)); + return client; + } + + @Override + protected Client getClient(byte[] key) { + Client client = jedis.getShard(key).getClient(); + clients.add(client); + results.add(new FutureResult(client)); + return client; + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPool.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPool.java new file mode 100755 index 000000000..19186345a --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ShardedJedisPool.java @@ -0,0 +1,120 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.List; +import java.util.regex.Pattern; + +import com.fr.third.org.apache.commons.pool2.PooledObject; +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; +import com.fr.third.org.apache.commons.pool2.impl.DefaultPooledObject; +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.util.Hashing; +import com.fr.third.redis.clients.jedis.util.Pool; + +public class ShardedJedisPool extends Pool { + public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, List shards) { + this(poolConfig, shards, Hashing.MURMUR_HASH); + } + + public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, List shards, + Hashing algo) { + this(poolConfig, shards, algo, null); + } + + public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, List shards, + Pattern keyTagPattern) { + this(poolConfig, shards, Hashing.MURMUR_HASH, keyTagPattern); + } + + public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, List shards, + Hashing algo, Pattern keyTagPattern) { + super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern)); + } + + @Override + public ShardedJedis getResource() { + ShardedJedis jedis = super.getResource(); + jedis.setDataSource(this); + return jedis; + } + + @Override + protected void returnBrokenResource(final ShardedJedis resource) { + if (resource != null) { + returnBrokenResourceObject(resource); + } + } + + @Override + protected void returnResource(final ShardedJedis resource) { + if (resource != null) { + resource.resetState(); + returnResourceObject(resource); + } + } + + /** + * PoolableObjectFactory custom impl. + */ + private static class ShardedJedisFactory implements PooledObjectFactory { + private List shards; + private Hashing algo; + private Pattern keyTagPattern; + + public ShardedJedisFactory(List shards, Hashing algo, Pattern keyTagPattern) { + this.shards = shards; + this.algo = algo; + this.keyTagPattern = keyTagPattern; + } + + @Override + public PooledObject makeObject() throws Exception { + ShardedJedis jedis = new ShardedJedis(shards, algo, keyTagPattern); + return new DefaultPooledObject(jedis); + } + + @Override + public void destroyObject(PooledObject pooledShardedJedis) throws Exception { + final ShardedJedis shardedJedis = pooledShardedJedis.getObject(); + for (Jedis jedis : shardedJedis.getAllShards()) { + if (jedis.isConnected()) { + try { + try { + jedis.quit(); + } catch (Exception e) { + + } + jedis.disconnect(); + } catch (Exception e) { + + } + } + } + } + + @Override + public boolean validateObject(PooledObject pooledShardedJedis) { + try { + ShardedJedis jedis = pooledShardedJedis.getObject(); + for (Jedis shard : jedis.getAllShards()) { + if (!shard.ping().equals("PONG")) { + return false; + } + } + return true; + } catch (Exception ex) { + return false; + } + } + + @Override + public void activateObject(PooledObject p) throws Exception { + + } + + @Override + public void passivateObject(PooledObject p) throws Exception { + + } + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/SortingParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/SortingParams.java new file mode 100755 index 000000000..9c4bec212 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/SortingParams.java @@ -0,0 +1,159 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.ALPHA; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.ASC; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.BY; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.DESC; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.GET; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.LIMIT; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.NOSORT; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +/** + * Builder Class for {@link Jedis#sort(String, SortingParams) SORT} Parameters. + */ +public class SortingParams { + private List params = new ArrayList(); + + /** + * Sort by weight in keys. + *

+ * Takes a pattern that is used in order to generate the key names of the weights used for + * sorting. Weight key names are obtained substituting the first occurrence of * with the actual + * value of the elements on the list. + *

+ * The pattern for a normal key/value pair is "field*" and for a value in a hash + * "field*->fieldname". + * @param pattern + * @return the SortingParams Object + */ + public SortingParams by(final String pattern) { + return by(SafeEncoder.encode(pattern)); + } + + /** + * Sort by weight in keys. + *

+ * Takes a pattern that is used in order to generate the key names of the weights used for + * sorting. Weight key names are obtained substituting the first occurrence of * with the actual + * value of the elements on the list. + *

+ * The pattern for a normal key/value pair is "field*" and for a value in a hash + * "field*->fieldname". + * @param pattern + * @return the SortingParams Object + */ + public SortingParams by(final byte[] pattern) { + params.add(BY.raw); + params.add(pattern); + return this; + } + + /** + * No sorting. + *

+ * This is useful if you want to retrieve a external key (using {@link #get(String...) GET}) but + * you don't want the sorting overhead. + * @return the SortingParams Object + */ + public SortingParams nosort() { + params.add(BY.raw); + params.add(NOSORT.raw); + return this; + } + + public Collection getParams() { + return Collections.unmodifiableCollection(params); + } + + /** + * Get the Sorting in Descending Order. + * @return the sortingParams Object + */ + public SortingParams desc() { + params.add(DESC.raw); + return this; + } + + /** + * Get the Sorting in Ascending Order. This is the default order. + * @return the SortingParams Object + */ + public SortingParams asc() { + params.add(ASC.raw); + return this; + } + + /** + * Limit the Numbers of returned Elements. + * @param start is zero based + * @param count + * @return the SortingParams Object + */ + public SortingParams limit(final int start, final int count) { + params.add(LIMIT.raw); + params.add(Protocol.toByteArray(start)); + params.add(Protocol.toByteArray(count)); + return this; + } + + /** + * Sort lexicographicaly. Note that Redis is utf-8 aware assuming you set the right value for the + * LC_COLLATE environment variable. + * @return the SortingParams Object + */ + public SortingParams alpha() { + params.add(ALPHA.raw); + return this; + } + + /** + * Retrieving external keys from the result of the search. + *

+ * Takes a pattern that is used in order to generate the key names of the result of sorting. The + * key names are obtained substituting the first occurrence of * with the actual value of the + * elements on the list. + *

+ * The pattern for a normal key/value pair is "field*" and for a value in a hash + * "field*->fieldname". + *

+ * To get the list itself use the char # as pattern. + * @param patterns + * @return the SortingParams Object + */ + public SortingParams get(String... patterns) { + for (final String pattern : patterns) { + params.add(GET.raw); + params.add(SafeEncoder.encode(pattern)); + } + return this; + } + + /** + * Retrieving external keys from the result of the search. + *

+ * Takes a pattern that is used in order to generate the key names of the result of sorting. The + * key names are obtained substituting the first occurrence of * with the actual value of the + * elements on the list. + *

+ * The pattern for a normal key/value pair is "field*" and for a value in a hash + * "field*->fieldname". + *

+ * To get the list itself use the char # as pattern. + * @param patterns + * @return the SortingParams Object + */ + public SortingParams get(byte[]... patterns) { + for (final byte[] pattern : patterns) { + params.add(GET.raw); + params.add(pattern); + } + return this; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Transaction.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Transaction.java new file mode 100755 index 000000000..dca70e7ea --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Transaction.java @@ -0,0 +1,94 @@ +package com.fr.third.redis.clients.jedis; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; + +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; + +/** + * Transaction is nearly identical to Pipeline, only differences are the multi/discard behaviors + */ +public class Transaction extends MultiKeyPipelineBase implements Closeable { + + protected boolean inTransaction = true; + + protected Transaction() { + // client will be set later in transaction block + } + + public Transaction(final Client client) { + this.client = client; + } + + @Override + protected Client getClient(String key) { + return client; + } + + @Override + protected Client getClient(byte[] key) { + return client; + } + + public void clear() { + if (inTransaction) { + discard(); + } + } + + public List exec() { + // Discard QUEUED or ERROR + client.getMany(getPipelinedResponseLength()); + client.exec(); + inTransaction = false; + + List unformatted = client.getObjectMultiBulkReply(); + if (unformatted == null) { + return null; + } + List formatted = new ArrayList(); + for (Object o : unformatted) { + try { + formatted.add(generateResponse(o).get()); + } catch (JedisDataException e) { + formatted.add(e); + } + } + return formatted; + } + + public List> execGetResponse() { + // Discard QUEUED or ERROR + client.getMany(getPipelinedResponseLength()); + client.exec(); + inTransaction = false; + + List unformatted = client.getObjectMultiBulkReply(); + if (unformatted == null) { + return null; + } + List> response = new ArrayList>(); + for (Object o : unformatted) { + response.add(generateResponse(o)); + } + return response; + } + + public String discard() { + client.getMany(getPipelinedResponseLength()); + client.discard(); + inTransaction = false; + clean(); + return client.getStatusCodeReply(); + } + + public void setClient(Client client) { + this.client = client; + } + + @Override + public void close() { + clear(); + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/Tuple.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/Tuple.java new file mode 100755 index 000000000..a2b7575f2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/Tuple.java @@ -0,0 +1,80 @@ +package com.fr.third.redis.clients.jedis; + +import java.util.Arrays; +import java.util.Objects; + +import com.fr.third.redis.clients.jedis.util.ByteArrayComparator; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class Tuple implements Comparable { + private byte[] element; + private Double score; + + public Tuple(String element, Double score) { + this(SafeEncoder.encode(element), score); + } + + public Tuple(byte[] element, Double score) { + super(); + this.element = element; + this.score = score; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result; + if (null != element) { + for (final byte b : element) { + result = prime * result + b; + } + } + long temp = Double.doubleToLongBits(score); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Tuple other = (Tuple) obj; + if (!Arrays.equals(element, other.element)) return false; + return Objects.equals(score, other.score); + } + + @Override + public int compareTo(Tuple other) { + return compare(this, other); + } + + public static int compare(Tuple t1, Tuple t2) { + int compScore = Double.compare(t1.score, t2.score); + if(compScore != 0) return compScore; + + return ByteArrayComparator.compare(t1.element, t2.element); + } + + public String getElement() { + if (null != element) { + return SafeEncoder.encode(element); + } else { + return null; + } + } + + public byte[] getBinaryElement() { + return element; + } + + public double getScore() { + return score; + } + + @Override + public String toString() { + return '[' + SafeEncoder.encode(element) + ',' + score + ']'; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/ZParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/ZParams.java new file mode 100755 index 000000000..3fe28bd6a --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/ZParams.java @@ -0,0 +1,48 @@ +package com.fr.third.redis.clients.jedis; + +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.AGGREGATE; +import static com.fr.third.redis.clients.jedis.Protocol.Keyword.WEIGHTS; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class ZParams { + public enum Aggregate { + SUM, MIN, MAX; + + public final byte[] raw; + + Aggregate() { + raw = SafeEncoder.encode(name()); + } + } + + private List params = new ArrayList(); + + /** + * Set weights. + * @param weights weights. + */ + public ZParams weights(final double... weights) { + params.add(WEIGHTS.raw); + for (final double weight : weights) { + params.add(Protocol.toByteArray(weight)); + } + + return this; + } + + public Collection getParams() { + return Collections.unmodifiableCollection(params); + } + + public ZParams aggregate(final Aggregate aggregate) { + params.add(AGGREGATE.raw); + params.add(aggregate.raw); + return this; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedBinaryJedisCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedBinaryJedisCommands.java new file mode 100755 index 000000000..16c818a5f --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedBinaryJedisCommands.java @@ -0,0 +1,24 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +public interface AdvancedBinaryJedisCommands { + + List configGet(byte[] pattern); + + byte[] configSet(byte[] parameter, byte[] value); + + String slowlogReset(); + + Long slowlogLen(); + + List slowlogGetBinary(); + + List slowlogGetBinary(long entries); + + Long objectRefcount(byte[] key); + + byte[] objectEncoding(byte[] key); + + Long objectIdletime(byte[] key); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedJedisCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedJedisCommands.java new file mode 100755 index 000000000..75b3e9f18 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/AdvancedJedisCommands.java @@ -0,0 +1,25 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +import com.fr.third.redis.clients.jedis.util.Slowlog; + +public interface AdvancedJedisCommands { + List configGet(String pattern); + + String configSet(String parameter, String value); + + String slowlogReset(); + + Long slowlogLen(); + + List slowlogGet(); + + List slowlogGet(long entries); + + Long objectRefcount(String key); + + String objectEncoding(String key); + + Long objectIdletime(String key); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicCommands.java new file mode 100755 index 000000000..f2ac6e9dc --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicCommands.java @@ -0,0 +1,142 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.DebugParams; + +public interface BasicCommands { + + /** + * This command is often used to test if a connection is still alive, or to measure latency. + * + * @return PONG + */ + String ping(); + + /** + * Ask the server to close the connection. The connection is closed as soon as all pending replies have been written to the client. + * @return OK + */ + String quit(); + + /** + * Delete all the keys of the currently selected DB. This command never fails. + The time-complexity for this operation is O(N), N being the number of keys in the database. + * @return OK + */ + String flushDB(); + + /** + * Return the number of keys in the currently-selected database. + * @return the number of key in the curr currently-selected database. + */ + Long dbSize(); + + /** + * Select the DB with having the specified zero-based numeric index. + * @param index the index + * @return a simple string reply OK + */ + String select(int index); + + /** + * This command swaps two Redis databases, so that immediately all the clients connected to a + * given database will see the data of the other database, and the other way around. + * @param index1 + * @param index2 + * @return Simple string reply: OK if SWAPDB was executed correctly. + */ + String swapDB(int index1, int index2); + + /** + * Delete all the keys of all the existing databases, not just the currently selected one. + * @return a simple string reply (OK) + */ + String flushAll(); + + /** + * Request for authentication in a password-protected Redis server. Redis can be instructed to require a password before allowing clients to execute commands. This is done using the requirepass directive in the configuration file. + If password matches the password in the configuration file, the server replies with the OK status code and starts accepting commands. Otherwise, an error is returned and the clients needs to try a new password. + * @param password + * @return the result of the auth + */ + String auth(String password); + + /** + * The SAVE commands performs a synchronous save of the dataset producing a point in time snapshot of all the data inside the Redis instance, in the form of an RDB file. + You almost never want to call SAVE in production environments where it will block all the other clients. Instead usually BGSAVE is used. However in case of issues preventing Redis to create the background saving child (for instance errors in the fork(2) system call), the SAVE command can be a good last resort to perform the dump of the latest dataset. + * @return result of the save + */ + String save(); + + /** + * Save the DB in background. The OK code is immediately returned. Redis forks, the parent continues to serve the clients, the child saves the DB on disk then exits. A client may be able to check if the operation succeeded using the LASTSAVE command. + * @return ok + */ + String bgsave(); + + /** + * Instruct Redis to start an Append Only File rewrite process. The rewrite will create a small optimized version of the current Append Only File + * If BGREWRITEAOF fails, no data gets lost as the old AOF will be untouched. + The rewrite will be only triggered by Redis if there is not already a background process doing persistence. Specifically: + If a Redis child is creating a snapshot on disk, the AOF rewrite is scheduled but not started until the saving child producing the RDB file terminates. In this case the BGREWRITEAOF will still return an OK code, but with an appropriate message. You can check if an AOF rewrite is scheduled looking at the INFO command as of Redis 2.6. + If an AOF rewrite is already in progress the command returns an error and no AOF rewrite will be scheduled for a later time. + Since Redis 2.4 the AOF rewrite is automatically triggered by Redis, however the BGREWRITEAOF command can be used to trigger a rewrite at any time. + * @return the response of the command + */ + String bgrewriteaof(); + + /** + * Return the UNIX TIME of the last DB save executed with success. + * @return the unix latest save + */ + Long lastsave(); + + /** + * Stop all the client. Perform a SAVE (if one save point is configured). + * Flush the append only file if AOF is enabled + * quit the server + * @return only in case of error. + */ + String shutdown(); + + /** + * The INFO command returns information and statistics about the server in a format that is simple to parse by computers and easy to read by humans. + * @return information on the server + */ + String info(); + + /** + * The INFO command returns information and statistics about the server in a format that is simple to parse by computers and easy to read by humans. + * @param section (all: Return all sections, default: Return only the default set of sections, server: General information about the Redis server, clients: Client connections section, memory: Memory consumption related information, persistence: RDB and AOF related information, stats: General statistics, replication: Master/slave replication information, cpu: CPU consumption statistics, commandstats: Redis command statistics, cluster: Redis Cluster section, keyspace: Database related statistics) + * @return + */ + String info(String section); + + /** + * The SLAVEOF command can change the replication settings of a slave on the fly. In the proper form SLAVEOF hostname port will make the server a slave of another server listening at the specified hostname and port. + * If a server is already a slave of some master, SLAVEOF hostname port will stop the replication against the old server and start the synchronization against the new one, discarding the old dataset. + * @param host, listening at the specified hostname + * @param port, server listening at the specified port + * @return result of the commmand. + */ + String slaveof(String host, int port); + + /** + * SLAVEOF NO ONE will stop replication, turning the server into a MASTER, but will not discard the replication. So, if the old master stops working, it is possible to turn the slave into a master and set the application to use this new master in read/write. Later when the other Redis server is fixed, it can be reconfigured to work as a slave. + * @return result of the command + */ + String slaveofNoOne(); + + /** + * Return the index of the current database + * @return the int of the index database. + */ + int getDB(); + + String debug(DebugParams params); + + String configResetStat(); + + String configRewrite(); + + Long waitReplicas(int replicas, long timeout); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicRedisPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicRedisPipeline.java new file mode 100755 index 000000000..4677d75e5 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BasicRedisPipeline.java @@ -0,0 +1,51 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.Module; +import com.fr.third.redis.clients.jedis.Response; + +import java.util.List; + +/** + * Pipelined responses for all of the low level, non key related commands + */ +public interface BasicRedisPipeline { + + Response bgrewriteaof(); + + Response bgsave(); + + Response> configGet(String pattern); + + Response configSet(String parameter, String value); + + Response configResetStat(); + + Response save(); + + Response lastsave(); + + Response flushDB(); + + Response flushAll(); + + Response info(); + + Response> time(); + + Response dbSize(); + + Response shutdown(); + + Response ping(); + + Response select(int index); + + Response swapDB(int index1, int index2); + + Response moduleLoad(String path); + + Response> moduleList(); + + Response moduleUnload(String name); + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisClusterCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisClusterCommands.java new file mode 100755 index 000000000..8b3c6e57b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisClusterCommands.java @@ -0,0 +1,306 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.*; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface BinaryJedisClusterCommands { + String set(byte[] key, byte[] value); + + String set(byte[] key, byte[] value, SetParams params); + + byte[] get(byte[] key); + + Boolean exists(byte[] key); + + Long persist(byte[] key); + + String type(byte[] key); + + byte[] dump(byte[] key); + + String restore(byte[] key, int ttl, byte[] serializedValue); + + Long expire(byte[] key, int seconds); + + Long pexpire(byte[] key, long milliseconds); + + Long expireAt(byte[] key, long unixTime); + + Long pexpireAt(byte[] key, long millisecondsTimestamp); + + Long ttl(byte[] key); + + Long pttl(byte[] key); + + Long touch(byte[] key); + + Boolean setbit(byte[] key, long offset, boolean value); + + Boolean setbit(byte[] key, long offset, byte[] value); + + Boolean getbit(byte[] key, long offset); + + Long setrange(byte[] key, long offset, byte[] value); + + byte[] getrange(byte[] key, long startOffset, long endOffset); + + byte[] getSet(byte[] key, byte[] value); + + Long setnx(byte[] key, byte[] value); + + String setex(byte[] key, int seconds, byte[] value); + + String psetex(byte[] key, long milliseconds, byte[] value); + + Long decrBy(byte[] key, long decrement); + + Long decr(byte[] key); + + Long incrBy(byte[] key, long increment); + + Double incrByFloat(byte[] key, double increment); + + Long incr(byte[] key); + + Long append(byte[] key, byte[] value); + + byte[] substr(byte[] key, int start, int end); + + Long hset(byte[] key, byte[] field, byte[] value); + + Long hset(byte[] key, Map hash); + + byte[] hget(byte[] key, byte[] field); + + Long hsetnx(byte[] key, byte[] field, byte[] value); + + String hmset(byte[] key, Map hash); + + List hmget(byte[] key, byte[]... fields); + + Long hincrBy(byte[] key, byte[] field, long value); + + Double hincrByFloat(byte[] key, byte[] field, double value); + + Boolean hexists(byte[] key, byte[] field); + + Long hdel(byte[] key, byte[]... field); + + Long hlen(byte[] key); + + Set hkeys(byte[] key); + + Collection hvals(byte[] key); + + Map hgetAll(byte[] key); + + Long rpush(byte[] key, byte[]... args); + + Long lpush(byte[] key, byte[]... args); + + Long llen(byte[] key); + + List lrange(byte[] key, long start, long stop); + + String ltrim(byte[] key, long start, long stop); + + byte[] lindex(byte[] key, long index); + + String lset(byte[] key, long index, byte[] value); + + Long lrem(byte[] key, long count, byte[] value); + + byte[] lpop(byte[] key); + + byte[] rpop(byte[] key); + + Long sadd(byte[] key, byte[]... member); + + Set smembers(byte[] key); + + Long srem(byte[] key, byte[]... member); + + byte[] spop(byte[] key); + + Set spop(byte[] key, long count); + + Long scard(byte[] key); + + Boolean sismember(byte[] key, byte[] member); + + byte[] srandmember(byte[] key); + + List srandmember(byte[] key, int count); + + Long strlen(byte[] key); + + Long zadd(byte[] key, double score, byte[] member); + + Long zadd(byte[] key, double score, byte[] member, ZAddParams params); + + Long zadd(byte[] key, Map scoreMembers); + + Long zadd(byte[] key, Map scoreMembers, ZAddParams params); + + Set zrange(byte[] key, long start, long stop); + + Long zrem(byte[] key, byte[]... members); + + Double zincrby(byte[] key, double increment, byte[] member); + + Double zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params); + + Long zrank(byte[] key, byte[] member); + + Long zrevrank(byte[] key, byte[] member); + + Set zrevrange(byte[] key, long start, long stop); + + Set zrangeWithScores(byte[] key, long start, long stop); + + Set zrevrangeWithScores(byte[] key, long start, long stop); + + Long zcard(byte[] key); + + Double zscore(byte[] key, byte[] member); + + List sort(byte[] key); + + List sort(byte[] key, SortingParams sortingParameters); + + Long zcount(byte[] key, double min, double max); + + Long zcount(byte[] key, byte[] min, byte[] max); + + Set zrangeByScore(byte[] key, double min, double max); + + Set zrangeByScore(byte[] key, byte[] min, byte[] max); + + Set zrevrangeByScore(byte[] key, double max, double min); + + Set zrangeByScore(byte[] key, double min, double max, int offset, int count); + + Set zrevrangeByScore(byte[] key, byte[] max, byte[] min); + + Set zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count); + + Set zrevrangeByScore(byte[] key, double max, double min, int offset, int count); + + Set zrangeByScoreWithScores(byte[] key, double min, double max); + + Set zrevrangeByScoreWithScores(byte[] key, double max, double min); + + Set zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count); + + Set zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count); + + Set zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max); + + Set zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min); + + Set zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count); + + Set zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count); + + Set zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count); + + Long zremrangeByRank(byte[] key, long start, long stop); + + Long zremrangeByScore(byte[] key, double min, double max); + + Long zremrangeByScore(byte[] key, byte[] min, byte[] max); + + Long zlexcount(byte[] key, byte[] min, byte[] max); + + Set zrangeByLex(byte[] key, byte[] min, byte[] max); + + Set zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, + int count); + + Set zrevrangeByLex(byte[] key, byte[] max, byte[] min); + + Set zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, + int count); + + Long zremrangeByLex(byte[] key, byte[] min, byte[] max); + + Long linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value); + + Long lpushx(byte[] key, byte[]... arg); + + Long rpushx(byte[] key, byte[]... arg); + + Long del(byte[] key); + + Long unlink(byte[] key); + + byte[] echo(byte[] arg); + + Long bitcount(byte[] key); + + Long bitcount(byte[] key, long start, long end); + + Long pfadd(byte[] key, byte[]... elements); + + long pfcount(byte[] key); + + // Geo Commands + + Long geoadd(byte[] key, double longitude, double latitude, byte[] member); + + Long geoadd(byte[] key, Map memberCoordinateMap); + + Double geodist(byte[] key, byte[] member1, byte[] member2); + + Double geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit); + + List geohash(byte[] key, byte[]... members); + + List geopos(byte[] key, byte[]... members); + + List georadius(byte[] key, double longitude, double latitude, double radius, + GeoUnit unit); + + List georadius(byte[] key, double longitude, double latitude, double radius, + GeoUnit unit, GeoRadiusParam param); + + List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit); + + List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, + GeoRadiusParam param); + + ScanResult> hscan(byte[] key, byte[] cursor); + + ScanResult> hscan(byte[] key, byte[] cursor, ScanParams params); + + ScanResult sscan(byte[] key, byte[] cursor); + + ScanResult sscan(byte[] key, byte[] cursor, ScanParams params); + + ScanResult zscan(byte[] key, byte[] cursor); + + ScanResult zscan(byte[] key, byte[] cursor, ScanParams params); + + /** + * Executes BITFIELD Redis command + * @param key + * @param arguments + */ + List bitfield(byte[] key, byte[]... arguments); + + /** + * Used for HSTRLEN Redis command + * @param key + * @param field + */ + Long hstrlen(byte[] key, byte[] field); + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisCommands.java new file mode 100755 index 000000000..a06b4102b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryJedisCommands.java @@ -0,0 +1,312 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fr.third.redis.clients.jedis.*; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +/** + * Common interface for sharded and non-sharded BinaryJedis + */ +public interface BinaryJedisCommands { + String set(byte[] key, byte[] value); + + String set(byte[] key, byte[] value, SetParams params); + + byte[] get(byte[] key); + + Boolean exists(byte[] key); + + Long persist(byte[] key); + + String type(byte[] key); + + byte[] dump(byte[] key); + + String restore(byte[] key, int ttl, byte[] serializedValue); + + Long expire(byte[] key, int seconds); + + Long pexpire(byte[] key, long milliseconds); + + Long expireAt(byte[] key, long unixTime); + + Long pexpireAt(byte[] key, long millisecondsTimestamp); + + Long ttl(byte[] key); + + Long pttl(byte[] key); + + Long touch(byte[] key); + + Boolean setbit(byte[] key, long offset, boolean value); + + Boolean setbit(byte[] key, long offset, byte[] value); + + Boolean getbit(byte[] key, long offset); + + Long setrange(byte[] key, long offset, byte[] value); + + byte[] getrange(byte[] key, long startOffset, long endOffset); + + byte[] getSet(byte[] key, byte[] value); + + Long setnx(byte[] key, byte[] value); + + String setex(byte[] key, int seconds, byte[] value); + + String psetex(byte[] key, long milliseconds, byte[] value); + + Long decrBy(byte[] key, long decrement); + + Long decr(byte[] key); + + Long incrBy(byte[] key, long increment); + + Double incrByFloat(byte[] key, double increment); + + Long incr(byte[] key); + + Long append(byte[] key, byte[] value); + + byte[] substr(byte[] key, int start, int end); + + Long hset(byte[] key, byte[] field, byte[] value); + + Long hset(byte[] key, Map hash); + + byte[] hget(byte[] key, byte[] field); + + Long hsetnx(byte[] key, byte[] field, byte[] value); + + String hmset(byte[] key, Map hash); + + List hmget(byte[] key, byte[]... fields); + + Long hincrBy(byte[] key, byte[] field, long value); + + Double hincrByFloat(byte[] key, byte[] field, double value); + + Boolean hexists(byte[] key, byte[] field); + + Long hdel(byte[] key, byte[]... field); + + Long hlen(byte[] key); + + Set hkeys(byte[] key); + + Collection hvals(byte[] key); + + Map hgetAll(byte[] key); + + Long rpush(byte[] key, byte[]... args); + + Long lpush(byte[] key, byte[]... args); + + Long llen(byte[] key); + + List lrange(byte[] key, long start, long stop); + + String ltrim(byte[] key, long start, long stop); + + byte[] lindex(byte[] key, long index); + + String lset(byte[] key, long index, byte[] value); + + Long lrem(byte[] key, long count, byte[] value); + + byte[] lpop(byte[] key); + + byte[] rpop(byte[] key); + + Long sadd(byte[] key, byte[]... member); + + Set smembers(byte[] key); + + Long srem(byte[] key, byte[]... member); + + byte[] spop(byte[] key); + + Set spop(byte[] key, long count); + + Long scard(byte[] key); + + Boolean sismember(byte[] key, byte[] member); + + byte[] srandmember(byte[] key); + + List srandmember(byte[] key, int count); + + Long strlen(byte[] key); + + Long zadd(byte[] key, double score, byte[] member); + + Long zadd(byte[] key, double score, byte[] member, ZAddParams params); + + Long zadd(byte[] key, Map scoreMembers); + + Long zadd(byte[] key, Map scoreMembers, ZAddParams params); + + Set zrange(byte[] key, long start, long stop); + + Long zrem(byte[] key, byte[]... members); + + Double zincrby(byte[] key, double increment, byte[] member); + + Double zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params); + + Long zrank(byte[] key, byte[] member); + + Long zrevrank(byte[] key, byte[] member); + + Set zrevrange(byte[] key, long start, long stop); + + Set zrangeWithScores(byte[] key, long start, long stop); + + Set zrevrangeWithScores(byte[] key, long start, long stop); + + Long zcard(byte[] key); + + Double zscore(byte[] key, byte[] member); + + List sort(byte[] key); + + List sort(byte[] key, SortingParams sortingParameters); + + Long zcount(byte[] key, double min, double max); + + Long zcount(byte[] key, byte[] min, byte[] max); + + Set zrangeByScore(byte[] key, double min, double max); + + Set zrangeByScore(byte[] key, byte[] min, byte[] max); + + Set zrevrangeByScore(byte[] key, double max, double min); + + Set zrangeByScore(byte[] key, double min, double max, int offset, int count); + + Set zrevrangeByScore(byte[] key, byte[] max, byte[] min); + + Set zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count); + + Set zrevrangeByScore(byte[] key, double max, double min, int offset, int count); + + Set zrangeByScoreWithScores(byte[] key, double min, double max); + + Set zrevrangeByScoreWithScores(byte[] key, double max, double min); + + Set zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count); + + Set zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count); + + Set zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max); + + Set zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min); + + Set zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count); + + Set zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count); + + Set zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count); + + Long zremrangeByRank(byte[] key, long start, long stop); + + Long zremrangeByScore(byte[] key, double min, double max); + + Long zremrangeByScore(byte[] key, byte[] min, byte[] max); + + Long zlexcount(byte[] key, byte[] min, byte[] max); + + Set zrangeByLex(byte[] key, byte[] min, byte[] max); + + Set zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, + int count); + + Set zrevrangeByLex(byte[] key, byte[] max, byte[] min); + + Set zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, + int count); + + Long zremrangeByLex(byte[] key, byte[] min, byte[] max); + + Long linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value); + + Long lpushx(byte[] key, byte[]... arg); + + Long rpushx(byte[] key, byte[]... arg); + + Long del(byte[] key); + + Long unlink(byte[] key); + + byte[] echo(byte[] arg); + + Long move(byte[] key, int dbIndex); + + Long bitcount(byte[] key); + + Long bitcount(byte[] key, long start, long end); + + Long pfadd(byte[] key, byte[]... elements); + + long pfcount(byte[] key); + + // Geo Commands + + Long geoadd(byte[] key, double longitude, double latitude, byte[] member); + + Long geoadd(byte[] key, Map memberCoordinateMap); + + Double geodist(byte[] key, byte[] member1, byte[] member2); + + Double geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit); + + List geohash(byte[] key, byte[]... members); + + List geopos(byte[] key, byte[]... members); + + List georadius(byte[] key, double longitude, double latitude, double radius, + GeoUnit unit); + + List georadius(byte[] key, double longitude, double latitude, double radius, + GeoUnit unit, GeoRadiusParam param); + + List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit); + + List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, + GeoRadiusParam param); + + ScanResult> hscan(byte[] key, byte[] cursor); + + ScanResult> hscan(byte[] key, byte[] cursor, ScanParams params); + + ScanResult sscan(byte[] key, byte[] cursor); + + ScanResult sscan(byte[] key, byte[] cursor, ScanParams params); + + ScanResult zscan(byte[] key, byte[] cursor); + + ScanResult zscan(byte[] key, byte[] cursor, ScanParams params); + + /** + * Executes BITFIELD Redis command + * @param key + * @param arguments + */ + List bitfield(byte[] key, byte[]... arguments); + + /** + * Used for HSTRLEN Redis command + * @param key + * @param field + * @return lenth of the value for key + */ + Long hstrlen(byte[] key, byte[] field); + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryRedisPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryRedisPipeline.java new file mode 100755 index 000000000..4223cf74d --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryRedisPipeline.java @@ -0,0 +1,279 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.*; + +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface BinaryRedisPipeline { + Response append(byte[] key, byte[] value); + + Response> blpop(byte[] arg); + + Response> brpop(byte[] arg); + + Response decr(byte[] key); + + Response decrBy(byte[] key, long decrement); + + Response del(byte[] keys); + + Response unlink(byte[] keys); + + Response echo(byte[] string); + + Response exists(byte[] key); + + Response expire(byte[] key, int seconds); + + Response pexpire(byte[] key, long milliseconds); + + Response expireAt(byte[] key, long unixTime); + + Response pexpireAt(byte[] key, long millisecondsTimestamp); + + Response get(byte[] key); + + Response getbit(byte[] key, long offset); + + Response getSet(byte[] key, byte[] value); + + Response getrange(byte[] key, long startOffset, long endOffset); + + Response hdel(byte[] key, byte[]... field); + + Response hexists(byte[] key, byte[] field); + + Response hget(byte[] key, byte[] field); + + Response> hgetAll(byte[] key); + + Response hincrBy(byte[] key, byte[] field, long value); + + Response> hkeys(byte[] key); + + Response hlen(byte[] key); + + Response> hmget(byte[] key, byte[]... fields); + + Response hmset(byte[] key, Map hash); + + Response hset(byte[] key, byte[] field, byte[] value); + + Response hset(byte[] key, Map hash); + + Response hsetnx(byte[] key, byte[] field, byte[] value); + + Response> hvals(byte[] key); + + Response incr(byte[] key); + + Response incrBy(byte[] key, long increment); + + Response lindex(byte[] key, long index); + + Response linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value); + + Response llen(byte[] key); + + Response lpop(byte[] key); + + Response lpush(byte[] key, byte[]... string); + + Response lpushx(byte[] key, byte[]... bytes); + + Response> lrange(byte[] key, long start, long stop); + + Response lrem(byte[] key, long count, byte[] value); + + Response lset(byte[] key, long index, byte[] value); + + Response ltrim(byte[] key, long start, long stop); + + Response move(byte[] key, int dbIndex); + + Response persist(byte[] key); + + Response rpop(byte[] key); + + Response rpush(byte[] key, byte[]... string); + + Response rpushx(byte[] key, byte[]... string); + + Response sadd(byte[] key, byte[]... member); + + Response scard(byte[] key); + + Response set(byte[] key, byte[] value); + + Response setbit(byte[] key, long offset, byte[] value); + + Response setrange(byte[] key, long offset, byte[] value); + + Response setex(byte[] key, int seconds, byte[] value); + + Response setnx(byte[] key, byte[] value); + + Response setrange(String key, long offset, String value); + + Response> smembers(byte[] key); + + Response sismember(byte[] key, byte[] member); + + Response> sort(byte[] key); + + Response> sort(byte[] key, SortingParams sortingParameters); + + Response spop(byte[] key); + + Response> spop(byte[] key, long count); + + Response srandmember(byte[] key); + + Response srem(byte[] key, byte[]... member); + + Response strlen(byte[] key); + + Response substr(byte[] key, int start, int end); + + Response touch(byte[] keys); + + Response ttl(byte[] key); + + Response pttl(byte[] key); + + Response type(byte[] key); + + Response zadd(byte[] key, double score, byte[] member); + + Response zadd(byte[] key, double score, byte[] member, ZAddParams params); + + Response zadd(byte[] key, Map scoreMembers); + + Response zadd(byte[] key, Map scoreMembers, ZAddParams params); + + Response zcard(byte[] key); + + Response zcount(byte[] key, double min, double max); + + Response zcount(byte[] key, byte[] min, byte[] max); + + Response zincrby(byte[] key, double increment, byte[] member); + + Response zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params); + + Response> zrange(byte[] key, long start, long stop); + + Response> zrangeByScore(byte[] key, double min, double max); + + Response> zrangeByScore(byte[] key, byte[] min, byte[] max); + + Response> zrangeByScore(byte[] key, double min, double max, int offset, int count); + + Response> zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count); + + Response> zrangeByScoreWithScores(byte[] key, double min, double max); + + Response> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max); + + Response> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, + int count); + + Response> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, + int count); + + Response> zrevrangeByScore(byte[] key, double max, double min); + + Response> zrevrangeByScore(byte[] key, byte[] max, byte[] min); + + Response> zrevrangeByScore(byte[] key, double max, double min, int offset, int count); + + Response> zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count); + + Response> zrevrangeByScoreWithScores(byte[] key, double max, double min); + + Response> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min); + + Response> zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, + int count); + + Response> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, + int count); + + Response> zrangeWithScores(byte[] key, long start, long stop); + + Response zrank(byte[] key, byte[] member); + + Response zrem(byte[] key, byte[]... members); + + Response zremrangeByRank(byte[] key, long start, long stop); + + Response zremrangeByScore(byte[] key, double min, double max); + + Response zremrangeByScore(byte[] key, byte[] min, byte[] max); + + Response> zrevrange(byte[] key, long start, long stop); + + Response> zrevrangeWithScores(byte[] key, long start, long stop); + + Response zrevrank(byte[] key, byte[] member); + + Response zscore(byte[] key, byte[] member); + + Response zlexcount(byte[] key, byte[] min, byte[] max); + + Response> zrangeByLex(byte[] key, byte[] min, byte[] max); + + Response> zrangeByLex(byte[] key, byte[] min, byte[] max, + int offset, int count); + + Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min); + + Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min, + int offset, int count); + + Response zremrangeByLex(byte[] key, byte[] min, byte[] max); + + Response bitcount(byte[] key); + + Response bitcount(byte[] key, long start, long end); + + Response pfadd(byte[] key, byte[]... elements); + + Response pfcount(byte[] key); + + // Geo Commands + + Response geoadd(byte[] key, double longitude, double latitude, byte[] member); + + Response geoadd(byte[] key, Map memberCoordinateMap); + + Response geodist(byte[] key, byte[] member1, byte[] member2); + + Response geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit); + + Response> geohash(byte[] key, byte[]... members); + + Response> geopos(byte[] key, byte[]... members); + + Response> georadius(byte[] key, double longitude, double latitude, + double radius, GeoUnit unit); + + Response> georadius(byte[] key, double longitude, double latitude, + double radius, GeoUnit unit, GeoRadiusParam param); + + Response> georadiusByMember(byte[] key, byte[] member, double radius, + GeoUnit unit); + + Response> georadiusByMember(byte[] key, byte[] member, double radius, + GeoUnit unit, GeoRadiusParam param); + + Response> bitfield(byte[] key, byte[]... elements); + + Response hstrlen(byte[] key, byte[] field); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommands.java new file mode 100755 index 000000000..1ec710d9f --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommands.java @@ -0,0 +1,29 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +public interface BinaryScriptingCommands { + + Object eval(byte[] script, byte[] keyCount, byte[]... params); + + Object eval(byte[] script, int keyCount, byte[]... params); + + Object eval(byte[] script, List keys, List args); + + Object eval(byte[] script); + + Object evalsha(byte[] sha1); + + Object evalsha(byte[] sha1, List keys, List args); + + Object evalsha(byte[] sha1, int keyCount, byte[]... params); + + // TODO: should be Boolean, add singular version + List scriptExists(byte[]... sha1); + + byte[] scriptLoad(byte[] script); + + String scriptFlush(); + + String scriptKill(); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommandsPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommandsPipeline.java new file mode 100755 index 000000000..04f02d650 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/BinaryScriptingCommandsPipeline.java @@ -0,0 +1,22 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.Response; + +import java.util.List; + +public interface BinaryScriptingCommandsPipeline { + + Response eval(byte[] script, byte[] keyCount, byte[]... params); + + Response eval(byte[] script, int keyCount, byte[]... params); + + Response eval(byte[] script, List keys, List args); + + Response eval(byte[] script); + + Response evalsha(byte[] sha1); + + Response evalsha(byte[] sha1, List keys, List args); + + Response evalsha(byte[] sha1, int keyCount, byte[]... params); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterCommands.java new file mode 100755 index 000000000..a06ada8f2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterCommands.java @@ -0,0 +1,49 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +import com.fr.third.redis.clients.jedis.ClusterReset; + +public interface ClusterCommands { + String clusterNodes(); + + String clusterMeet(String ip, int port); + + String clusterAddSlots(int... slots); + + String clusterDelSlots(int... slots); + + String clusterInfo(); + + List clusterGetKeysInSlot(int slot, int count); + + String clusterSetSlotNode(int slot, String nodeId); + + String clusterSetSlotMigrating(int slot, String nodeId); + + String clusterSetSlotImporting(int slot, String nodeId); + + String clusterSetSlotStable(int slot); + + String clusterForget(String nodeId); + + String clusterFlushSlots(); + + Long clusterKeySlot(String key); + + Long clusterCountKeysInSlot(int slot); + + String clusterSaveConfig(); + + String clusterReplicate(String nodeId); + + List clusterSlaves(String nodeId); + + String clusterFailover(); + + List clusterSlots(); + + String clusterReset(ClusterReset resetType); + + String readonly(); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterPipeline.java new file mode 100755 index 000000000..03530f6f4 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ClusterPipeline.java @@ -0,0 +1,25 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.Response; + +import java.util.List; + +public interface ClusterPipeline { + Response clusterNodes(); + + Response clusterMeet(String ip, int port); + + Response clusterAddSlots(int... slots); + + Response clusterDelSlots(int... slots); + + Response clusterInfo(); + + Response> clusterGetKeysInSlot(int slot, int count); + + Response clusterSetSlotNode(int slot, String nodeId); + + Response clusterSetSlotMigrating(int slot, String nodeId); + + Response clusterSetSlotImporting(int slot, String nodeId); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/Commands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/Commands.java new file mode 100755 index 000000000..cb92f0d9b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/Commands.java @@ -0,0 +1,333 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.Map; + +import com.fr.third.redis.clients.jedis.BitOP; +import com.fr.third.redis.clients.jedis.ListPosition; +import com.fr.third.redis.clients.jedis.ScanParams; +import com.fr.third.redis.clients.jedis.SortingParams; +import com.fr.third.redis.clients.jedis.ZParams; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +public interface Commands { + + void set(String key, String value); + + void set(String key, String value, SetParams params); + + void get(String key); + + void exists(String... keys); + + void del(String... keys); + + void unlink(String... keys); + + void type(String key); + + void keys(String pattern); + + void rename(String oldkey, String newkey); + + void renamenx(String oldkey, String newkey); + + void expire(String key, int seconds); + + void expireAt(String key, long unixTime); + + void ttl(String key); + + void pttl(String key); + + void touch(String... keys); + + void setbit(String key, long offset, boolean value); + + void setbit(String key, long offset, String value); + + void getbit(String key, long offset); + + void setrange(String key, long offset, String value); + + void getrange(String key, long startOffset, long endOffset); + + void move(String key, int dbIndex); + + void getSet(String key, String value); + + void mget(String... keys); + + void setnx(String key, String value); + + void setex(String key, int seconds, String value); + + void mset(String... keysvalues); + + void msetnx(String... keysvalues); + + void decrBy(String key, long decrement); + + void decr(String key); + + void incrBy(String key, long increment); + + void incrByFloat(String key, double increment); + + void incr(String key); + + void append(String key, String value); + + void substr(String key, int start, int end); + + void hset(String key, String field, String value); + + void hget(String key, String field); + + void hset(String key, Map hash); + + void hsetnx(String key, String field, String value); + + void hmset(String key, Map hash); + + void hmget(String key, String... fields); + + void hincrBy(String key, String field, long value); + + void hincrByFloat(String key, String field, double value); + + void hexists(String key, String field); + + void hdel(String key, String... fields); + + void hlen(String key); + + void hkeys(String key); + + void hvals(String key); + + void hgetAll(String key); + + void rpush(String key, String... strings); + + void lpush(String key, String... strings); + + void llen(String key); + + void lrange(String key, long start, long stop); + + void ltrim(String key, long start, long stop); + + void lindex(String key, long index); + + void lset(String key, long index, String value); + + void lrem(String key, long count, String value); + + void lpop(String key); + + void rpop(String key); + + void rpoplpush(String srckey, String dstkey); + + void sadd(String key, String... members); + + void smembers(String key); + + void srem(String key, String... member); + + void spop(String key); + + void spop(String key, long count); + + void smove(String srckey, String dstkey, String member); + + void scard(String key); + + void sismember(String key, String member); + + void sinter(String... keys); + + void sinterstore(String dstkey, String... keys); + + void sunion(String... keys); + + void sunionstore(String dstkey, String... keys); + + void sdiff(String... keys); + + void sdiffstore(String dstkey, String... keys); + + void srandmember(String key); + + void zadd(String key, double score, String member); + + void zadd(String key, double score, String member, ZAddParams params); + + void zadd(String key, Map scoreMembers); + + void zadd(String key, Map scoreMembers, ZAddParams params); + + void zrange(String key, long start, long stop); + + void zrem(String key, String... members); + + void zincrby(String key, double increment, String member); + + void zincrby(String key, double increment, String member, ZIncrByParams params); + + void zrank(String key, String member); + + void zrevrank(String key, String member); + + void zrevrange(String key, long start, long stop); + + void zrangeWithScores(String key, long start, long stop); + + void zrevrangeWithScores(String key, long start, long stop); + + void zcard(String key); + + void zscore(String key, String member); + + void watch(String... keys); + + void sort(String key); + + void sort(String key, SortingParams sortingParameters); + + void blpop(String[] args); + + void sort(String key, SortingParams sortingParameters, String dstkey); + + void sort(String key, String dstkey); + + void brpop(String[] args); + + void brpoplpush(String source, String destination, int timeout); + + void zcount(String key, double min, double max); + + void zcount(String key, String min, String max); + + void zrangeByScore(String key, double min, double max); + + void zrangeByScore(String key, String min, String max); + + void zrangeByScore(String key, double min, double max, int offset, + int count); + + void zrangeByScore(String key, String min, String max, int offset, int count); + + void zrangeByScoreWithScores(String key, double min, double max); + + void zrangeByScoreWithScores(String key, double min, double max, + int offset, int count); + + void zrangeByScoreWithScores(String key, String min, String max); + + void zrangeByScoreWithScores(String key, String min, String max, + int offset, int count); + + void zrevrangeByScore(String key, double max, double min); + + void zrevrangeByScore(String key, String max, String min); + + void zrevrangeByScore(String key, double max, double min, int offset, + int count); + + void zrevrangeByScore(String key, String max, String min, int offset, int count); + + void zrevrangeByScoreWithScores(String key, double max, double min); + + void zrevrangeByScoreWithScores(String key, double max, double min, + int offset, int count); + + void zrevrangeByScoreWithScores(String key, String max, String min); + + void zrevrangeByScoreWithScores(String key, String max, String min, + int offset, int count); + + void zremrangeByRank(String key, long start, long stop); + + void zremrangeByScore(String key, double min, double max); + + void zremrangeByScore(String key, String min, String max); + + void zunionstore(String dstkey, String... sets); + + void zunionstore(String dstkey, ZParams params, String... sets); + + void zinterstore(String dstkey, String... sets); + + void zinterstore(String dstkey, ZParams params, String... sets); + + void strlen(String key); + + void lpushx(String key, String... string); + + void persist(String key); + + void rpushx(String key, String... string); + + void echo(String string); + + void linsert(String key, ListPosition where, String pivot, String value); + + void bgrewriteaof(); + + void bgsave(); + + void lastsave(); + + void save(); + + void configSet(String parameter, String value); + + void configGet(String pattern); + + void configResetStat(); + + void multi(); + + void exec(); + + void discard(); + + void objectRefcount(String key); + + void objectIdletime(String key); + + void objectEncoding(String key); + + void bitcount(String key); + + void bitcount(String key, long start, long end); + + void bitop(BitOP op, String destKey, String... srcKeys); + + void scan(String cursor, ScanParams params); + + void hscan(String key, String cursor, ScanParams params); + + void sscan(String key, String cursor, ScanParams params); + + void zscan(String key, String cursor, ScanParams params); + + void waitReplicas(int replicas, long timeout); + + /** + * Used for BITFIELD Redis command + * @param key + * @param arguments + */ + void bitfield(String key, String... arguments); + + /** + * Used for HSTRLEN Redis command + * @param key + * @param field + */ + void hstrlen(String key, String field); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterBinaryScriptingCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterBinaryScriptingCommands.java new file mode 100755 index 000000000..d05d58ba2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterBinaryScriptingCommands.java @@ -0,0 +1,55 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +public interface JedisClusterBinaryScriptingCommands { + Object eval(byte[] script, byte[] keyCount, byte[]... params); + + Object eval(byte[] script, int keyCount, byte[]... params); + + Object eval(byte[] script, List keys, List args); + + /** + * @param script + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + Object eval(byte[] script, byte[] sampleKey); + + /** + * @param sha1 + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + Object evalsha(byte[] sha1, byte[] sampleKey); + + Object evalsha(byte[] sha1, List keys, List args); + + Object evalsha(byte[] sha1, int keyCount, byte[]... params); + + /** + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @param sha1 + * @return + */ + List scriptExists(byte[] sampleKey, byte[]... sha1); + + /** + * @param script + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + byte[] scriptLoad(byte[] script, byte[] sampleKey); + + /** + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + String scriptFlush(byte[] sampleKey); + + /** + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + String scriptKill(byte[] sampleKey); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterCommands.java new file mode 100755 index 000000000..aa8f0a286 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterCommands.java @@ -0,0 +1,301 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.*; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface JedisClusterCommands { + String set(String key, String value); + + String set(String key, String value, SetParams params); + + String get(String key); + + Boolean exists(String key); + + Long persist(String key); + + String type(String key); + + byte[] dump(String key); + + String restore(String key, int ttl, byte[] serializedValue); + + Long expire(String key, int seconds); + + Long pexpire(String key, long milliseconds); + + Long expireAt(String key, long unixTime); + + Long pexpireAt(String key, long millisecondsTimestamp); + + Long ttl(String key); + + Long pttl(String key); + + Long touch(String key); + + Boolean setbit(String key, long offset, boolean value); + + Boolean setbit(String key, long offset, String value); + + Boolean getbit(String key, long offset); + + Long setrange(String key, long offset, String value); + + String getrange(String key, long startOffset, long endOffset); + + String getSet(String key, String value); + + Long setnx(String key, String value); + + String setex(String key, int seconds, String value); + + String psetex(String key, long milliseconds, String value); + + Long decrBy(String key, long decrement); + + Long decr(String key); + + Long incrBy(String key, long increment); + + Double incrByFloat(String key, double increment); + + Long incr(String key); + + Long append(String key, String value); + + String substr(String key, int start, int end); + + Long hset(String key, String field, String value); + + Long hset(String key, Map hash); + + String hget(String key, String field); + + Long hsetnx(String key, String field, String value); + + String hmset(String key, Map hash); + + List hmget(String key, String... fields); + + Long hincrBy(String key, String field, long value); + + Boolean hexists(String key, String field); + + Long hdel(String key, String... field); + + Long hlen(String key); + + Set hkeys(String key); + + List hvals(String key); + + Map hgetAll(String key); + + Long rpush(String key, String... string); + + Long lpush(String key, String... string); + + Long llen(String key); + + List lrange(String key, long start, long stop); + + String ltrim(String key, long start, long stop); + + String lindex(String key, long index); + + String lset(String key, long index, String value); + + Long lrem(String key, long count, String value); + + String lpop(String key); + + String rpop(String key); + + Long sadd(String key, String... member); + + Set smembers(String key); + + Long srem(String key, String... member); + + String spop(String key); + + Set spop(String key, long count); + + Long scard(String key); + + Boolean sismember(String key, String member); + + String srandmember(String key); + + List srandmember(String key, int count); + + Long strlen(String key); + + Long zadd(String key, double score, String member); + + Long zadd(String key, double score, String member, ZAddParams params); + + Long zadd(String key, Map scoreMembers); + + Long zadd(String key, Map scoreMembers, ZAddParams params); + + Set zrange(String key, long start, long stop); + + Long zrem(String key, String... members); + + Double zincrby(String key, double increment, String member); + + Double zincrby(String key, double increment, String member, ZIncrByParams params); + + Long zrank(String key, String member); + + Long zrevrank(String key, String member); + + Set zrevrange(String key, long start, long stop); + + Set zrangeWithScores(String key, long start, long stop); + + Set zrevrangeWithScores(String key, long start, long stop); + + Long zcard(String key); + + Double zscore(String key, String member); + + List sort(String key); + + List sort(String key, SortingParams sortingParameters); + + Long zcount(String key, double min, double max); + + Long zcount(String key, String min, String max); + + Set zrangeByScore(String key, double min, double max); + + Set zrangeByScore(String key, String min, String max); + + Set zrevrangeByScore(String key, double max, double min); + + Set zrangeByScore(String key, double min, double max, int offset, int count); + + Set zrevrangeByScore(String key, String max, String min); + + Set zrangeByScore(String key, String min, String max, int offset, int count); + + Set zrevrangeByScore(String key, double max, double min, int offset, int count); + + Set zrangeByScoreWithScores(String key, double min, double max); + + Set zrevrangeByScoreWithScores(String key, double max, double min); + + Set zrangeByScoreWithScores(String key, double min, double max, int offset, int count); + + Set zrevrangeByScore(String key, String max, String min, int offset, int count); + + Set zrangeByScoreWithScores(String key, String min, String max); + + Set zrevrangeByScoreWithScores(String key, String max, String min); + + Set zrangeByScoreWithScores(String key, String min, String max, int offset, int count); + + Set zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count); + + Set zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count); + + Long zremrangeByRank(String key, long start, long stop); + + Long zremrangeByScore(String key, double min, double max); + + Long zremrangeByScore(String key, String min, String max); + + Long zlexcount(String key, String min, String max); + + Set zrangeByLex(String key, String min, String max); + + Set zrangeByLex(String key, String min, String max, int offset, + int count); + + Set zrevrangeByLex(String key, String max, String min); + + Set zrevrangeByLex(String key, String max, String min, + int offset, int count); + + Long zremrangeByLex(String key, String min, String max); + + Long linsert(String key, ListPosition where, String pivot, String value); + + Long lpushx(String key, String... string); + + Long rpushx(String key, String... string); + + List blpop(int timeout, String key); + + List brpop(int timeout, String key); + + Long del(String key); + + Long unlink(String key); + + String echo(String string); + + Long bitcount(String key); + + Long bitcount(String key, long start, long end); + + ScanResult> hscan(String key, String cursor); + + ScanResult sscan(String key, String cursor); + + ScanResult zscan(String key, String cursor); + + Long pfadd(String key, String... elements); + + long pfcount(String key); + + // Geo Commands + + Long geoadd(String key, double longitude, double latitude, String member); + + Long geoadd(String key, Map memberCoordinateMap); + + Double geodist(String key, String member1, String member2); + + Double geodist(String key, String member1, String member2, GeoUnit unit); + + List geohash(String key, String... members); + + List geopos(String key, String... members); + + List georadius(String key, double longitude, double latitude, double radius, + GeoUnit unit); + + List georadius(String key, double longitude, double latitude, double radius, + GeoUnit unit, GeoRadiusParam param); + + List georadiusByMember(String key, String member, double radius, GeoUnit unit); + + List georadiusByMember(String key, String member, double radius, GeoUnit unit, + GeoRadiusParam param); + + /** + * Executes BITFIELD Redis command + * @param key + * @param arguments + */ + List bitfield(String key, String... arguments); + + /** + * Used for HSTRLEN Redis command + * @param key + * @param field + * @return lenth of the value for key + */ + Long hstrlen(String key, String field); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterScriptingCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterScriptingCommands.java new file mode 100755 index 000000000..014a2c9eb --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisClusterScriptingCommands.java @@ -0,0 +1,60 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +public interface JedisClusterScriptingCommands { + Object eval(String script, int keyCount, String... params); + + Object eval(String script, List keys, List args); + + /** + * @param script + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + Object eval(String script, String sampleKey); + + /** + * @param sha1 + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + Object evalsha(String sha1, String sampleKey); + + Object evalsha(String sha1, List keys, List args); + + Object evalsha(String sha1, int keyCount, String... params); + + /** + * @param sha1 + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + Boolean scriptExists(String sha1, String sampleKey); + + /** + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @param sha1 + * @return + */ + List scriptExists(String sampleKey, String... sha1); + + /** + * @param script + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + String scriptLoad(String script, String sampleKey); + + /** + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + String scriptFlush(String sampleKey); + + /** + * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to + * @return + */ + String scriptKill(String sampleKey); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisCommands.java new file mode 100755 index 000000000..398a7c2a4 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/JedisCommands.java @@ -0,0 +1,319 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fr.third.redis.clients.jedis.*; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.SetParams; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +/** + * Common interface for sharded and non-sharded Jedis + */ +public interface JedisCommands { + String set(String key, String value); + + String set(String key, String value, SetParams params); + + String get(String key); + + Boolean exists(String key); + + Long persist(String key); + + String type(String key); + + byte[] dump(String key); + + String restore(String key, int ttl, byte[] serializedValue); + + Long expire(String key, int seconds); + + Long pexpire(String key, long milliseconds); + + Long expireAt(String key, long unixTime); + + Long pexpireAt(String key, long millisecondsTimestamp); + + Long ttl(String key); + + Long pttl(String key); + + Long touch(String key); + + Boolean setbit(String key, long offset, boolean value); + + Boolean setbit(String key, long offset, String value); + + Boolean getbit(String key, long offset); + + Long setrange(String key, long offset, String value); + + String getrange(String key, long startOffset, long endOffset); + + String getSet(String key, String value); + + Long setnx(String key, String value); + + String setex(String key, int seconds, String value); + + String psetex(String key, long milliseconds, String value); + + Long decrBy(String key, long decrement); + + Long decr(String key); + + Long incrBy(String key, long increment); + + Double incrByFloat(String key, double increment); + + Long incr(String key); + + Long append(String key, String value); + + String substr(String key, int start, int end); + + Long hset(String key, String field, String value); + + Long hset(String key, Map hash); + + String hget(String key, String field); + + Long hsetnx(String key, String field, String value); + + String hmset(String key, Map hash); + + List hmget(String key, String... fields); + + Long hincrBy(String key, String field, long value); + + Double hincrByFloat(String key, String field, double value); + + Boolean hexists(String key, String field); + + Long hdel(String key, String... field); + + Long hlen(String key); + + Set hkeys(String key); + + List hvals(String key); + + Map hgetAll(String key); + + Long rpush(String key, String... string); + + Long lpush(String key, String... string); + + Long llen(String key); + + List lrange(String key, long start, long stop); + + String ltrim(String key, long start, long stop); + + String lindex(String key, long index); + + String lset(String key, long index, String value); + + Long lrem(String key, long count, String value); + + String lpop(String key); + + String rpop(String key); + + Long sadd(String key, String... member); + + Set smembers(String key); + + Long srem(String key, String... member); + + String spop(String key); + + Set spop(String key, long count); + + Long scard(String key); + + Boolean sismember(String key, String member); + + String srandmember(String key); + + List srandmember(String key, int count); + + Long strlen(String key); + + Long zadd(String key, double score, String member); + + Long zadd(String key, double score, String member, ZAddParams params); + + Long zadd(String key, Map scoreMembers); + + Long zadd(String key, Map scoreMembers, ZAddParams params); + + Set zrange(String key, long start, long stop); + + Long zrem(String key, String... members); + + Double zincrby(String key, double increment, String member); + + Double zincrby(String key, double increment, String member, ZIncrByParams params); + + Long zrank(String key, String member); + + Long zrevrank(String key, String member); + + Set zrevrange(String key, long start, long stop); + + Set zrangeWithScores(String key, long start, long stop); + + Set zrevrangeWithScores(String key, long start, long stop); + + Long zcard(String key); + + Double zscore(String key, String member); + + List sort(String key); + + List sort(String key, SortingParams sortingParameters); + + Long zcount(String key, double min, double max); + + Long zcount(String key, String min, String max); + + Set zrangeByScore(String key, double min, double max); + + Set zrangeByScore(String key, String min, String max); + + Set zrevrangeByScore(String key, double max, double min); + + Set zrangeByScore(String key, double min, double max, int offset, int count); + + Set zrevrangeByScore(String key, String max, String min); + + Set zrangeByScore(String key, String min, String max, int offset, int count); + + Set zrevrangeByScore(String key, double max, double min, int offset, int count); + + Set zrangeByScoreWithScores(String key, double min, double max); + + Set zrevrangeByScoreWithScores(String key, double max, double min); + + Set zrangeByScoreWithScores(String key, double min, double max, int offset, int count); + + Set zrevrangeByScore(String key, String max, String min, int offset, int count); + + Set zrangeByScoreWithScores(String key, String min, String max); + + Set zrevrangeByScoreWithScores(String key, String max, String min); + + Set zrangeByScoreWithScores(String key, String min, String max, int offset, int count); + + Set zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count); + + Set zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count); + + Long zremrangeByRank(String key, long start, long stop); + + Long zremrangeByScore(String key, double min, double max); + + Long zremrangeByScore(String key, String min, String max); + + Long zlexcount(String key, String min, String max); + + Set zrangeByLex(String key, String min, String max); + + Set zrangeByLex(String key, String min, String max, int offset, + int count); + + Set zrevrangeByLex(String key, String max, String min); + + Set zrevrangeByLex(String key, String max, String min, + int offset, int count); + + Long zremrangeByLex(String key, String min, String max); + + Long linsert(String key, ListPosition where, String pivot, String value); + + Long lpushx(String key, String... string); + + Long rpushx(String key, String... string); + + List blpop(int timeout, String key); + + List brpop(int timeout, String key); + + Long del(String key); + + Long unlink(String key); + + String echo(String string); + + Long move(String key, int dbIndex); + + Long bitcount(String key); + + Long bitcount(String key, long start, long end); + + Long bitpos(String key, boolean value); + + Long bitpos(String key, boolean value, BitPosParams params); + + ScanResult> hscan(String key, String cursor); + + ScanResult> hscan(String key, String cursor, + ScanParams params); + + ScanResult sscan(String key, String cursor); + + ScanResult zscan(String key, String cursor); + + ScanResult zscan(String key, String cursor, ScanParams params); + + ScanResult sscan(String key, String cursor, ScanParams params); + + Long pfadd(String key, String... elements); + + long pfcount(String key); + + // Geo Commands + + Long geoadd(String key, double longitude, double latitude, String member); + + Long geoadd(String key, Map memberCoordinateMap); + + Double geodist(String key, String member1, String member2); + + Double geodist(String key, String member1, String member2, GeoUnit unit); + + List geohash(String key, String... members); + + List geopos(String key, String... members); + + List georadius(String key, double longitude, double latitude, double radius, + GeoUnit unit); + + List georadius(String key, double longitude, double latitude, double radius, + GeoUnit unit, GeoRadiusParam param); + + List georadiusByMember(String key, String member, double radius, GeoUnit unit); + + List georadiusByMember(String key, String member, double radius, GeoUnit unit, + GeoRadiusParam param); + + /** + * Executes BITFIELD Redis command + * @param key + * @param arguments + */ + List bitfield(String key, String... arguments); + + /** + * Used for HSTRLEN Redis command + * @param key + * @param field + * @return lenth of the value for key + */ + Long hstrlen(String key, String field); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ModuleCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ModuleCommands.java new file mode 100755 index 000000000..42498a4f5 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ModuleCommands.java @@ -0,0 +1,11 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.Module; + +import java.util.List; + +public interface ModuleCommands { + String moduleLoad(String path); + String moduleUnload(String name); + List moduleList(); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryCommands.java new file mode 100755 index 000000000..69ac2afad --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryCommands.java @@ -0,0 +1,87 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.BinaryJedisPubSub; +import com.fr.third.redis.clients.jedis.BitOP; +import com.fr.third.redis.clients.jedis.SortingParams; +import com.fr.third.redis.clients.jedis.ZParams; + +import java.util.List; +import java.util.Set; + +public interface MultiKeyBinaryCommands { + Long del(byte[]... keys); + + Long unlink(byte[]... keys); + + Long exists(byte[]... keys); + + List blpop(int timeout, byte[]... keys); + + List brpop(int timeout, byte[]... keys); + + List blpop(byte[]... args); + + List brpop(byte[]... args); + + Set keys(byte[] pattern); + + List mget(byte[]... keys); + + String mset(byte[]... keysvalues); + + Long msetnx(byte[]... keysvalues); + + String rename(byte[] oldkey, byte[] newkey); + + Long renamenx(byte[] oldkey, byte[] newkey); + + byte[] rpoplpush(byte[] srckey, byte[] dstkey); + + Set sdiff(byte[]... keys); + + Long sdiffstore(byte[] dstkey, byte[]... keys); + + Set sinter(byte[]... keys); + + Long sinterstore(byte[] dstkey, byte[]... keys); + + Long smove(byte[] srckey, byte[] dstkey, byte[] member); + + Long sort(byte[] key, SortingParams sortingParameters, byte[] dstkey); + + Long sort(byte[] key, byte[] dstkey); + + Set sunion(byte[]... keys); + + Long sunionstore(byte[] dstkey, byte[]... keys); + + String watch(byte[]... keys); + + String unwatch(); + + Long zinterstore(byte[] dstkey, byte[]... sets); + + Long zinterstore(byte[] dstkey, ZParams params, byte[]... sets); + + Long zunionstore(byte[] dstkey, byte[]... sets); + + Long zunionstore(byte[] dstkey, ZParams params, byte[]... sets); + + byte[] brpoplpush(byte[] source, byte[] destination, int timeout); + + Long publish(byte[] channel, byte[] message); + + void subscribe(BinaryJedisPubSub jedisPubSub, byte[]... channels); + + void psubscribe(BinaryJedisPubSub jedisPubSub, byte[]... patterns); + + byte[] randomBinaryKey(); + + Long bitop(BitOP op, byte[] destKey, byte[]... srcKeys); + + String pfmerge(byte[] destkey, byte[]... sourcekeys); + + Long pfcount(byte[]... keys); + + Long touch(byte[]... keys); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java new file mode 100755 index 000000000..357858564 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java @@ -0,0 +1,81 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.BinaryJedisPubSub; +import com.fr.third.redis.clients.jedis.BitOP; +import com.fr.third.redis.clients.jedis.ScanParams; +import com.fr.third.redis.clients.jedis.ScanResult; +import com.fr.third.redis.clients.jedis.SortingParams; +import com.fr.third.redis.clients.jedis.ZParams; + +import java.util.List; +import java.util.Set; + +public interface MultiKeyBinaryJedisClusterCommands { + Long del(byte[]... keys); + + Long unlink(byte[]... keys); + + Long exists(byte[]... keys); + + List blpop(int timeout, byte[]... keys); + + List brpop(int timeout, byte[]... keys); + + List mget(byte[]... keys); + + String mset(byte[]... keysvalues); + + Long msetnx(byte[]... keysvalues); + + String rename(byte[] oldkey, byte[] newkey); + + Long renamenx(byte[] oldkey, byte[] newkey); + + byte[] rpoplpush(byte[] srckey, byte[] dstkey); + + Set sdiff(byte[]... keys); + + Long sdiffstore(byte[] dstkey, byte[]... keys); + + Set sinter(byte[]... keys); + + Long sinterstore(byte[] dstkey, byte[]... keys); + + Long smove(byte[] srckey, byte[] dstkey, byte[] member); + + Long sort(byte[] key, SortingParams sortingParameters, byte[] dstkey); + + Long sort(byte[] key, byte[] dstkey); + + Set sunion(byte[]... keys); + + Long sunionstore(byte[] dstkey, byte[]... keys); + + Long zinterstore(byte[] dstkey, byte[]... sets); + + Long zinterstore(byte[] dstkey, ZParams params, byte[]... sets); + + Long zunionstore(byte[] dstkey, byte[]... sets); + + Long zunionstore(byte[] dstkey, ZParams params, byte[]... sets); + + byte[] brpoplpush(byte[] source, byte[] destination, int timeout); + + Long publish(byte[] channel, byte[] message); + + void subscribe(BinaryJedisPubSub jedisPubSub, byte[]... channels); + + void psubscribe(BinaryJedisPubSub jedisPubSub, byte[]... patterns); + + Long bitop(BitOP op, byte[] destKey, byte[]... srcKeys); + + String pfmerge(byte[] destkey, byte[]... sourcekeys); + + Long pfcount(byte[]... keys); + + Long touch(byte[]... keys); + + ScanResult scan(byte[] cursor, ScanParams params); + + Set keys(byte[] pattern); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java new file mode 100755 index 000000000..f8aca1953 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java @@ -0,0 +1,81 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.BitOP; +import com.fr.third.redis.clients.jedis.Response; +import com.fr.third.redis.clients.jedis.SortingParams; +import com.fr.third.redis.clients.jedis.ZParams; + +import java.util.List; +import java.util.Set; + +/** + * Multikey related commands (these are split out because they are non-shardable) + */ +public interface MultiKeyBinaryRedisPipeline { + + Response del(byte[]... keys); + + Response unlink(byte[]... keys); + + Response exists(byte[]... keys); + + Response> blpop(byte[]... args); + + Response> brpop(byte[]... args); + + Response> keys(byte[] pattern); + + Response> mget(byte[]... keys); + + Response mset(byte[]... keysvalues); + + Response msetnx(byte[]... keysvalues); + + Response rename(byte[] oldkey, byte[] newkey); + + Response renamenx(byte[] oldkey, byte[] newkey); + + Response rpoplpush(byte[] srckey, byte[] dstkey); + + Response> sdiff(byte[]... keys); + + Response sdiffstore(byte[] dstkey, byte[]... keys); + + Response> sinter(byte[]... keys); + + Response sinterstore(byte[] dstkey, byte[]... keys); + + Response smove(byte[] srckey, byte[] dstkey, byte[] member); + + Response sort(byte[] key, SortingParams sortingParameters, byte[] dstkey); + + Response sort(byte[] key, byte[] dstkey); + + Response> sunion(byte[]... keys); + + Response sunionstore(byte[] dstkey, byte[]... keys); + + Response watch(byte[]... keys); + + Response zinterstore(byte[] dstkey, byte[]... sets); + + Response zinterstore(byte[] dstkey, ZParams params, byte[]... sets); + + Response zunionstore(byte[] dstkey, byte[]... sets); + + Response zunionstore(byte[] dstkey, ZParams params, byte[]... sets); + + Response brpoplpush(byte[] source, byte[] destination, int timeout); + + Response publish(byte[] channel, byte[] message); + + Response randomKeyBinary(); + + Response bitop(BitOP op, byte[] destKey, byte[]... srcKeys); + + Response pfmerge(byte[] destkey, byte[]... sourcekeys); + + Response pfcount(byte[]... keys); + + Response touch(byte[]... keys); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommands.java new file mode 100755 index 000000000..7921b476e --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommands.java @@ -0,0 +1,160 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.*; + +import java.util.List; +import java.util.Set; + +public interface MultiKeyCommands { + Long del(String... keys); + + Long unlink(String... keys); + + Long exists(String... keys); + + List blpop(int timeout, String... keys); + + List brpop(int timeout, String... keys); + + List blpop(String... args); + + List brpop(String... args); + + /** + * Returns all the keys matching the glob-style pattern. For example if + * you have in the database the keys "foo" and "foobar" the command "KEYS foo*" will return + * "foo foobar".
+ * Warning: consider this as a command that should be used in production environments with extreme care. + * It may ruin performance when it is executed against large databases. + * This command is intended for debugging and special operations, such as changing your keyspace layout. + * Don't use it in your regular application code. + * If you're looking for a way to find keys in a subset of your keyspace, consider using {@link #scan(String, ScanParams)} or sets. + *

+ * While the time complexity for this operation is O(N), the constant times are fairly low. + * For example, Redis running on an entry level laptop can scan a 1 million key database in 40 milliseconds. + *

+ * Glob style patterns examples: + *

    + *
  • h?llo will match hello hallo hhllo + *
  • h*llo will match hllo heeeello + *
  • h[ae]llo will match hello and hallo, but not hillo + *
+ *

+ * Use \ to escape special chars if you want to match them verbatim. + *

+ * Time complexity: O(n) (with n being the number of keys in the DB, and assuming keys and pattern + * of limited length) + * @param pattern + * @return Multi bulk reply + * @see Redis KEYS documentation + */ + Set keys(String pattern); + + List mget(String... keys); + + String mset(String... keysvalues); + + Long msetnx(String... keysvalues); + + String rename(String oldkey, String newkey); + + Long renamenx(String oldkey, String newkey); + + String rpoplpush(String srckey, String dstkey); + + Set sdiff(String... keys); + + Long sdiffstore(String dstkey, String... keys); + + Set sinter(String... keys); + + Long sinterstore(String dstkey, String... keys); + + Long smove(String srckey, String dstkey, String member); + + Long sort(String key, SortingParams sortingParameters, String dstkey); + + Long sort(String key, String dstkey); + + Set sunion(String... keys); + + Long sunionstore(String dstkey, String... keys); + + String watch(String... keys); + + String unwatch(); + + Long zinterstore(String dstkey, String... sets); + + Long zinterstore(String dstkey, ZParams params, String... sets); + + Long zunionstore(String dstkey, String... sets); + + Long zunionstore(String dstkey, ZParams params, String... sets); + + String brpoplpush(String source, String destination, int timeout); + + Long publish(String channel, String message); + + void subscribe(JedisPubSub jedisPubSub, String... channels); + + void psubscribe(JedisPubSub jedisPubSub, String... patterns); + + String randomKey(); + + Long bitop(BitOP op, String destKey, String... srcKeys); + + /** + * @see #scan(String, ScanParams) + */ + ScanResult scan(String cursor); + + /** + * Iterates the set of keys in the currently selected Redis database. + *

+ * Since this command allows for incremental iteration, returning only a small number of elements per call, + * it can be used in production without the downside of commands like {@link #keys(String)} or + * {@link JedisCommands#smembers(String)} )} that may block the server for a long time (even several seconds) + * when called against big collections of keys or elements. + *

+ * SCAN basic usage
+ * SCAN is a cursor based iterator. This means that at every call of the command, the server returns an updated cursor + * that the user needs to use as the cursor argument in the next call. + * An iteration starts when the cursor is set to 0, and terminates when the cursor returned by the server is 0. + *

+ * Scan guarantees
+ * The SCAN command, and the other commands in the SCAN family, are able to provide to the user a set of guarantees + * associated to full iterations. + *

    + *
  • A full iteration always retrieves all the elements that were present in the collection from the start to the + * end of a full iteration. This means that if a given element is inside the collection when an iteration is started, + * and is still there when an iteration terminates, then at some point SCAN returned it to the user. + *
  • A full iteration never returns any element that was NOT present in the collection from the start to the end of + * a full iteration. So if an element was removed before the start of an iteration, and is never added back to the + * collection for all the time an iteration lasts, SCAN ensures that this element will never be returned. + *
+ * However because SCAN has very little state associated (just the cursor) it has the following drawbacks: + *
    + *
  • A given element may be returned multiple times. It is up to the application to handle the case of duplicated + * elements, for example only using the returned elements in order to perform operations that are safe when re-applied + * multiple times. + *
  • Elements that were not constantly present in the collection during a full iteration, may be returned or not: + * it is undefined. + *
+ *

+ * Time complexity: O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor + * to return back to 0. N is the number of elements inside the DB. + * + * @param cursor The cursor. + * @param params the scan parameters. For example a glob-style match pattern + * @return the scan result with the results of this iteration and the new position of the cursor + * @see Redis SCAN documentation + */ + ScanResult scan(String cursor, ScanParams params); + + String pfmerge(String destkey, String... sourcekeys); + + long pfcount(String... keys); + + Long touch(String... keys); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java new file mode 100755 index 000000000..0621ce782 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java @@ -0,0 +1,80 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.BitOP; +import com.fr.third.redis.clients.jedis.Response; +import com.fr.third.redis.clients.jedis.SortingParams; +import com.fr.third.redis.clients.jedis.ZParams; + +import java.util.List; +import java.util.Set; + +/** + * Multikey related commands (these are split out because they are non-shardable) + */ +public interface MultiKeyCommandsPipeline { + Response del(String... keys); + + Response unlink(String... keys); + + Response exists(String... keys); + + Response> blpop(String... args); + + Response> brpop(String... args); + + Response> keys(String pattern); + + Response> mget(String... keys); + + Response mset(String... keysvalues); + + Response msetnx(String... keysvalues); + + Response rename(String oldkey, String newkey); + + Response renamenx(String oldkey, String newkey); + + Response rpoplpush(String srckey, String dstkey); + + Response> sdiff(String... keys); + + Response sdiffstore(String dstkey, String... keys); + + Response> sinter(String... keys); + + Response sinterstore(String dstkey, String... keys); + + Response smove(String srckey, String dstkey, String member); + + Response sort(String key, SortingParams sortingParameters, String dstkey); + + Response sort(String key, String dstkey); + + Response> sunion(String... keys); + + Response sunionstore(String dstkey, String... keys); + + Response watch(String... keys); + + Response zinterstore(String dstkey, String... sets); + + Response zinterstore(String dstkey, ZParams params, String... sets); + + Response zunionstore(String dstkey, String... sets); + + Response zunionstore(String dstkey, ZParams params, String... sets); + + Response brpoplpush(String source, String destination, int timeout); + + Response publish(String channel, String message); + + Response randomKey(); + + Response bitop(BitOP op, String destKey, String... srcKeys); + + Response pfmerge(String destkey, String... sourcekeys); + + Response pfcount(String... keys); + + Response touch(String... keys); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java new file mode 100755 index 000000000..e1552bc50 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java @@ -0,0 +1,81 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.BitOP; +import com.fr.third.redis.clients.jedis.JedisPubSub; +import com.fr.third.redis.clients.jedis.ScanParams; +import com.fr.third.redis.clients.jedis.ScanResult; +import com.fr.third.redis.clients.jedis.SortingParams; +import com.fr.third.redis.clients.jedis.ZParams; + +import java.util.List; +import java.util.Set; + +public interface MultiKeyJedisClusterCommands { + Long del(String... keys); + + Long unlink(String... keys); + + Long exists(String... keys); + + List blpop(int timeout, String... keys); + + List brpop(int timeout, String... keys); + + List mget(String... keys); + + String mset(String... keysvalues); + + Long msetnx(String... keysvalues); + + String rename(String oldkey, String newkey); + + Long renamenx(String oldkey, String newkey); + + String rpoplpush(String srckey, String dstkey); + + Set sdiff(String... keys); + + Long sdiffstore(String dstkey, String... keys); + + Set sinter(String... keys); + + Long sinterstore(String dstkey, String... keys); + + Long smove(String srckey, String dstkey, String member); + + Long sort(String key, SortingParams sortingParameters, String dstkey); + + Long sort(String key, String dstkey); + + Set sunion(String... keys); + + Long sunionstore(String dstkey, String... keys); + + Long zinterstore(String dstkey, String... sets); + + Long zinterstore(String dstkey, ZParams params, String... sets); + + Long zunionstore(String dstkey, String... sets); + + Long zunionstore(String dstkey, ZParams params, String... sets); + + String brpoplpush(String source, String destination, int timeout); + + Long publish(String channel, String message); + + void subscribe(JedisPubSub jedisPubSub, String... channels); + + void psubscribe(JedisPubSub jedisPubSub, String... patterns); + + Long bitop(BitOP op, String destKey, String... srcKeys); + + String pfmerge(String destkey, String... sourcekeys); + + long pfcount(String... keys); + + Long touch(String... keys); + + ScanResult scan(String cursor, ScanParams params); + + Set keys(String pattern); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ProtocolCommand.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ProtocolCommand.java new file mode 100755 index 000000000..92b6dd44c --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ProtocolCommand.java @@ -0,0 +1,7 @@ +package com.fr.third.redis.clients.jedis.commands; + +public interface ProtocolCommand { + + byte[] getRaw(); + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/RedisPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/RedisPipeline.java new file mode 100755 index 000000000..5b1615d32 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/RedisPipeline.java @@ -0,0 +1,271 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.*; +import com.fr.third.redis.clients.jedis.params.GeoRadiusParam; +import com.fr.third.redis.clients.jedis.params.ZAddParams; +import com.fr.third.redis.clients.jedis.params.ZIncrByParams; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface RedisPipeline { + Response append(String key, String value); + + Response> blpop(String arg); + + Response> brpop(String arg); + + Response decr(String key); + + Response decrBy(String key, long decrement); + + Response del(String key); + + Response unlink(String key); + + Response echo(String string); + + Response exists(String key); + + Response expire(String key, int seconds); + + Response pexpire(String key, long milliseconds); + + Response expireAt(String key, long unixTime); + + Response pexpireAt(String key, long millisecondsTimestamp); + + Response get(String key); + + Response getbit(String key, long offset); + + Response getrange(String key, long startOffset, long endOffset); + + Response getSet(String key, String value); + + Response hdel(String key, String... field); + + Response hexists(String key, String field); + + Response hget(String key, String field); + + Response> hgetAll(String key); + + Response hincrBy(String key, String field, long value); + + Response> hkeys(String key); + + Response hlen(String key); + + Response> hmget(String key, String... fields); + + Response hmset(String key, Map hash); + + Response hset(String key, String field, String value); + + Response hset(String key, Map hash); + + Response hsetnx(String key, String field, String value); + + Response> hvals(String key); + + Response incr(String key); + + Response incrBy(String key, long increment); + + Response lindex(String key, long index); + + Response linsert(String key, ListPosition where, String pivot, String value); + + Response llen(String key); + + Response lpop(String key); + + Response lpush(String key, String... string); + + Response lpushx(String key, String... string); + + Response> lrange(String key, long start, long stop); + + Response lrem(String key, long count, String value); + + Response lset(String key, long index, String value); + + Response ltrim(String key, long start, long stop); + + Response move(String key, int dbIndex); + + Response persist(String key); + + Response rpop(String key); + + Response rpush(String key, String... string); + + Response rpushx(String key, String... string); + + Response sadd(String key, String... member); + + Response scard(String key); + + Response sismember(String key, String member); + + Response set(String key, String value); + + Response setbit(String key, long offset, boolean value); + + Response setex(String key, int seconds, String value); + + Response setnx(String key, String value); + + Response setrange(String key, long offset, String value); + + Response> smembers(String key); + + Response> sort(String key); + + Response> sort(String key, SortingParams sortingParameters); + + Response spop(String key); + + Response> spop(String key, long count); + + Response srandmember(String key); + + Response srem(String key, String... member); + + Response strlen(String key); + + Response substr(String key, int start, int end); + + Response touch(String key); + + Response ttl(String key); + + Response pttl(String key); + + Response type(String key); + + Response zadd(String key, double score, String member); + + Response zadd(String key, double score, String member, ZAddParams params); + + Response zadd(String key, Map scoreMembers); + + Response zadd(String key, Map scoreMembers, ZAddParams params); + + Response zcard(String key); + + Response zcount(String key, double min, double max); + + Response zcount(String key, String min, String max); + + Response zincrby(String key, double increment, String member); + + Response zincrby(String key, double increment, String member, ZIncrByParams params); + + Response> zrange(String key, long start, long stop); + + Response> zrangeByScore(String key, double min, double max); + + Response> zrangeByScore(String key, String min, String max); + + Response> zrangeByScore(String key, double min, double max, int offset, int count); + + Response> zrangeByScore(String key, String min, String max, int offset, int count); + + Response> zrangeByScoreWithScores(String key, double min, double max); + + Response> zrangeByScoreWithScores(String key, double min, double max, int offset, + int count); + + Response> zrevrangeByScore(String key, double max, double min); + + Response> zrevrangeByScore(String key, String max, String min); + + Response> zrevrangeByScore(String key, double max, double min, int offset, int count); + + Response> zrevrangeByScore(String key, String max, String min, int offset, int count); + + Response> zrevrangeByScoreWithScores(String key, double max, double min); + + Response> zrevrangeByScoreWithScores(String key, String max, String min); + + Response> zrevrangeByScoreWithScores(String key, double max, double min, int offset, + int count); + + Response> zrevrangeByScoreWithScores(String key, String max, String min, int offset, + int count); + + Response> zrangeWithScores(String key, long start, long stop); + + Response zrank(String key, String member); + + Response zrem(String key, String... members); + + Response zremrangeByRank(String key, long start, long stop); + + Response zremrangeByScore(String key, double min, double max); + + Response zremrangeByScore(String key, String min, String max); + + Response> zrevrange(String key, long start, long stop); + + Response> zrevrangeWithScores(String key, long start, long stop); + + Response zrevrank(String key, String member); + + Response zscore(String key, String member); + + Response zlexcount(String key, String min, String max); + + Response> zrangeByLex(String key, String min, String max); + + Response> zrangeByLex(String key, String min, String max, + int offset, int count); + + Response> zrevrangeByLex(String key, String max, String min); + + Response> zrevrangeByLex(String key, String max, String min, + int offset, int count); + + Response zremrangeByLex(String key, String min, String max); + + Response bitcount(String key); + + Response bitcount(String key, long start, long end); + + Response pfadd(String key, String... elements); + + Response pfcount(String key); + + Response> bitfield(String key, String... arguments); + + Response hstrlen(String key, String field); + + // Geo Commands + + Response geoadd(String key, double longitude, double latitude, String member); + + Response geoadd(String key, Map memberCoordinateMap); + + Response geodist(String key, String member1, String member2); + + Response geodist(String key, String member1, String member2, GeoUnit unit); + + Response> geohash(String key, String... members); + + Response> geopos(String key, String... members); + + Response> georadius(String key, double longitude, double latitude, + double radius, GeoUnit unit); + + Response> georadius(String key, double longitude, double latitude, + double radius, GeoUnit unit, GeoRadiusParam param); + + Response> georadiusByMember(String key, String member, double radius, + GeoUnit unit); + + Response> georadiusByMember(String key, String member, double radius, + GeoUnit unit, GeoRadiusParam param); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommands.java new file mode 100755 index 000000000..2052421f9 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommands.java @@ -0,0 +1,23 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; + +public interface ScriptingCommands { + Object eval(String script, int keyCount, String... params); + + Object eval(String script, List keys, List args); + + Object eval(String script); + + Object evalsha(String sha1); + + Object evalsha(String sha1, List keys, List args); + + Object evalsha(String sha1, int keyCount, String... params); + + Boolean scriptExists(String sha1); + + List scriptExists(String... sha1); + + String scriptLoad(String script); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommandsPipeline.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommandsPipeline.java new file mode 100755 index 000000000..71485ee92 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/ScriptingCommandsPipeline.java @@ -0,0 +1,19 @@ +package com.fr.third.redis.clients.jedis.commands; + +import com.fr.third.redis.clients.jedis.Response; + +import java.util.List; + +public interface ScriptingCommandsPipeline { + Response eval(String script, int keyCount, String... params); + + Response eval(String script, List keys, List args); + + Response eval(String script); + + Response evalsha(String sha1); + + Response evalsha(String sha1, List keys, List args); + + Response evalsha(String sha1, int keyCount, String... params); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/SentinelCommands.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/SentinelCommands.java new file mode 100755 index 000000000..1d9939bac --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/commands/SentinelCommands.java @@ -0,0 +1,22 @@ +package com.fr.third.redis.clients.jedis.commands; + +import java.util.List; +import java.util.Map; + +public interface SentinelCommands { + List> sentinelMasters(); + + List sentinelGetMasterAddrByName(String masterName); + + Long sentinelReset(String pattern); + + List> sentinelSlaves(String masterName); + + String sentinelFailover(String masterName); + + String sentinelMonitor(String masterName, String ip, int port, int quorum); + + String sentinelRemove(String masterName); + + String sentinelSet(String masterName, Map parameterMap); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/InvalidURIException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/InvalidURIException.java new file mode 100755 index 000000000..0af2635e9 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/InvalidURIException.java @@ -0,0 +1,19 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class InvalidURIException extends JedisException { + + private static final long serialVersionUID = -781691993326357802L; + + public InvalidURIException(String message) { + super(message); + } + + public InvalidURIException(Throwable cause) { + super(cause); + } + + public InvalidURIException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisAskDataException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisAskDataException.java new file mode 100755 index 000000000..619806c29 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisAskDataException.java @@ -0,0 +1,20 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +import com.fr.third.redis.clients.jedis.HostAndPort; + +public class JedisAskDataException extends JedisRedirectionException { + private static final long serialVersionUID = 3878126572474819403L; + + public JedisAskDataException(Throwable cause, HostAndPort targetHost, int slot) { + super(cause, targetHost, slot); + } + + public JedisAskDataException(String message, Throwable cause, HostAndPort targetHost, int slot) { + super(message, cause, targetHost, slot); + } + + public JedisAskDataException(String message, HostAndPort targetHost, int slot) { + super(message, targetHost, slot); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisBusyException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisBusyException.java new file mode 100755 index 000000000..c1f31b892 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisBusyException.java @@ -0,0 +1,19 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisBusyException extends JedisDataException { + + private static final long serialVersionUID = 3992655220229243478L; + + public JedisBusyException(final String message) { + super(message); + } + + public JedisBusyException(final Throwable cause) { + super(cause); + } + + public JedisBusyException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterException.java new file mode 100755 index 000000000..dc770fe43 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisClusterException extends JedisDataException { + private static final long serialVersionUID = 3878126572474819403L; + + public JedisClusterException(Throwable cause) { + super(cause); + } + + public JedisClusterException(String message, Throwable cause) { + super(message, cause); + } + + public JedisClusterException(String message) { + super(message); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterMaxAttemptsException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterMaxAttemptsException.java new file mode 100755 index 000000000..26b233ec6 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterMaxAttemptsException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisClusterMaxAttemptsException extends JedisClusterOperationException { + private static final long serialVersionUID = 167600616259092761L; + + public JedisClusterMaxAttemptsException(String message) { + super(message); + } + + public JedisClusterMaxAttemptsException(Throwable cause) { + super(cause); + } + + public JedisClusterMaxAttemptsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterOperationException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterOperationException.java new file mode 100755 index 000000000..4d03f24b8 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisClusterOperationException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisClusterOperationException extends JedisException { + private static final long serialVersionUID = 8124535086306604887L; + + public JedisClusterOperationException(String message) { + super(message); + } + + public JedisClusterOperationException(Throwable cause) { + super(cause); + } + + public JedisClusterOperationException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisConnectionException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisConnectionException.java new file mode 100755 index 000000000..c16008708 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisConnectionException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisConnectionException extends JedisException { + private static final long serialVersionUID = 3878126572474819403L; + + public JedisConnectionException(String message) { + super(message); + } + + public JedisConnectionException(Throwable cause) { + super(cause); + } + + public JedisConnectionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisDataException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisDataException.java new file mode 100755 index 000000000..ea48f49d5 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisDataException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisDataException extends JedisException { + private static final long serialVersionUID = 3878126572474819403L; + + public JedisDataException(String message) { + super(message); + } + + public JedisDataException(Throwable cause) { + super(cause); + } + + public JedisDataException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisException.java new file mode 100755 index 000000000..2378cb231 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisException extends RuntimeException { + private static final long serialVersionUID = -2946266495682282677L; + + public JedisException(String message) { + super(message); + } + + public JedisException(Throwable e) { + super(e); + } + + public JedisException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisExhaustedPoolException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisExhaustedPoolException.java new file mode 100755 index 000000000..5d96c8deb --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisExhaustedPoolException.java @@ -0,0 +1,20 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +/** + * This exception will be thrown when the Jedis client isn't able to retrieve a connection from the + * pool, since all the connections are being used (a.k.a. an "exhausted" pool). + */ +public class JedisExhaustedPoolException extends JedisException { + + public JedisExhaustedPoolException(String message) { + super(message); + } + + public JedisExhaustedPoolException(Throwable e) { + super(e); + } + + public JedisExhaustedPoolException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisMovedDataException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisMovedDataException.java new file mode 100755 index 000000000..18659e9d1 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisMovedDataException.java @@ -0,0 +1,19 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +import com.fr.third.redis.clients.jedis.HostAndPort; + +public class JedisMovedDataException extends JedisRedirectionException { + private static final long serialVersionUID = 3878126572474819403L; + + public JedisMovedDataException(String message, HostAndPort targetNode, int slot) { + super(message, targetNode, slot); + } + + public JedisMovedDataException(Throwable cause, HostAndPort targetNode, int slot) { + super(cause, targetNode, slot); + } + + public JedisMovedDataException(String message, Throwable cause, HostAndPort targetNode, int slot) { + super(message, cause, targetNode, slot); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoReachableClusterNodeException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoReachableClusterNodeException.java new file mode 100755 index 000000000..e51ebbefb --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoReachableClusterNodeException.java @@ -0,0 +1,17 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisNoReachableClusterNodeException extends JedisConnectionException { + private static final long serialVersionUID = 3878122572474110407L; + + public JedisNoReachableClusterNodeException(String message) { + super(message); + } + + public JedisNoReachableClusterNodeException(Throwable cause) { + super(cause); + } + + public JedisNoReachableClusterNodeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoScriptException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoScriptException.java new file mode 100755 index 000000000..bcf0a3cc8 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisNoScriptException.java @@ -0,0 +1,11 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +public class JedisNoScriptException extends JedisDataException { + private static final long serialVersionUID = 4674378093072060731L; + + public JedisNoScriptException(final String message) { super(message); } + + public JedisNoScriptException(final Throwable cause) { super(cause); } + + public JedisNoScriptException(final String message, final Throwable cause) { super(message, cause); } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisRedirectionException.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisRedirectionException.java new file mode 100755 index 000000000..73a4cbfaf --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/exceptions/JedisRedirectionException.java @@ -0,0 +1,36 @@ +package com.fr.third.redis.clients.jedis.exceptions; + +import com.fr.third.redis.clients.jedis.HostAndPort; + +public class JedisRedirectionException extends JedisDataException { + private static final long serialVersionUID = 3878126572474819403L; + + private HostAndPort targetNode; + private int slot; + + public JedisRedirectionException(String message, HostAndPort targetNode, int slot) { + super(message); + this.targetNode = targetNode; + this.slot = slot; + } + + public JedisRedirectionException(Throwable cause, HostAndPort targetNode, int slot) { + super(cause); + this.targetNode = targetNode; + this.slot = slot; + } + + public JedisRedirectionException(String message, Throwable cause, HostAndPort targetNode, int slot) { + super(message, cause); + this.targetNode = targetNode; + this.slot = slot; + } + + public HostAndPort getTargetNode() { + return targetNode; + } + + public int getSlot() { + return slot; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/params/GeoRadiusParam.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/GeoRadiusParam.java new file mode 100755 index 000000000..72a3d8e82 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/GeoRadiusParam.java @@ -0,0 +1,79 @@ +package com.fr.third.redis.clients.jedis.params; + +import com.fr.third.redis.clients.jedis.Protocol; +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +import java.util.ArrayList; + +public class GeoRadiusParam extends Params { + private static final String WITHCOORD = "withcoord"; + private static final String WITHDIST = "withdist"; + + // Do not add WITHHASH since we can't classify result of WITHHASH and WITHDIST, + // and WITHHASH is for debugging purposes + + private static final String ASC = "asc"; + private static final String DESC = "desc"; + private static final String COUNT = "count"; + + public GeoRadiusParam() { + } + + public static GeoRadiusParam geoRadiusParam() { + return new GeoRadiusParam(); + } + + public GeoRadiusParam withCoord() { + addParam(WITHCOORD); + return this; + } + + public GeoRadiusParam withDist() { + addParam(WITHDIST); + return this; + } + + public GeoRadiusParam sortAscending() { + addParam(ASC); + return this; + } + + public GeoRadiusParam sortDescending() { + addParam(DESC); + return this; + } + + public GeoRadiusParam count(int count) { + if (count > 0) { + addParam(COUNT, count); + } + return this; + } + + public byte[][] getByteParams(byte[]... args) { + ArrayList byteParams = new ArrayList(); + for (byte[] arg : args) { + byteParams.add(arg); + } + + if (contains(WITHCOORD)) { + byteParams.add(SafeEncoder.encode(WITHCOORD)); + } + if (contains(WITHDIST)) { + byteParams.add(SafeEncoder.encode(WITHDIST)); + } + + if (contains(COUNT)) { + byteParams.add(SafeEncoder.encode(COUNT)); + byteParams.add(Protocol.toByteArray((Integer) getParam(COUNT))); + } + + if (contains(ASC)) { + byteParams.add(SafeEncoder.encode(ASC)); + } else if (contains(DESC)) { + byteParams.add(SafeEncoder.encode(DESC)); + } + + return byteParams.toArray(new byte[byteParams.size()][]); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/params/Params.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/Params.java new file mode 100755 index 000000000..1920de1a8 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/Params.java @@ -0,0 +1,54 @@ +package com.fr.third.redis.clients.jedis.params; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public abstract class Params { + + private Map params; + + @SuppressWarnings("unchecked") + public T getParam(String name) { + if (params == null) return null; + + return (T) params.get(name); + } + + public byte[][] getByteParams() { + ArrayList byteParams = new ArrayList(); + + for (Entry param : params.entrySet()) { + byteParams.add(SafeEncoder.encode(param.getKey())); + if (param.getValue() != null) { + byteParams.add(SafeEncoder.encode(String.valueOf(param.getValue()))); + } + } + + return byteParams.toArray(new byte[byteParams.size()][]); + } + + protected boolean contains(String name) { + if (params == null) return false; + + return params.containsKey(name); + } + + protected void addParam(String name, Object value) { + if (params == null) { + params = new HashMap(); + } + params.put(name, value); + } + + protected void addParam(String name) { + if (params == null) { + params = new HashMap(); + } + params.put(name, null); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/params/SetParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/SetParams.java new file mode 100755 index 000000000..29b6c58e9 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/SetParams.java @@ -0,0 +1,84 @@ +package com.fr.third.redis.clients.jedis.params; + +import java.util.ArrayList; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +public class SetParams extends Params { + + private static final String XX = "xx"; + private static final String NX = "nx"; + private static final String PX = "px"; + private static final String EX = "ex"; + + public SetParams() { + } + + public static SetParams setParams() { + return new SetParams(); + } + + /** + * Set the specified expire time, in seconds. + * @param secondsToExpire + * @return SetParams + */ + public SetParams ex(int secondsToExpire) { + addParam(EX, secondsToExpire); + return this; + } + + /** + * Set the specified expire time, in milliseconds. + * @param millisecondsToExpire + * @return SetParams + */ + public SetParams px(long millisecondsToExpire) { + addParam(PX, millisecondsToExpire); + return this; + } + + /** + * Only set the key if it does not already exist. + * @return SetParams + */ + public SetParams nx() { + addParam(NX); + return this; + } + + /** + * Only set the key if it already exist. + * @return SetParams + */ + public SetParams xx() { + addParam(XX); + return this; + } + + public byte[][] getByteParams(byte[]... args) { + ArrayList byteParams = new ArrayList(); + for (byte[] arg : args) { + byteParams.add(arg); + } + + if (contains(NX)) { + byteParams.add(SafeEncoder.encode(NX)); + } + if (contains(XX)) { + byteParams.add(SafeEncoder.encode(XX)); + } + + if (contains(EX)) { + byteParams.add(SafeEncoder.encode(EX)); + byteParams.add(SafeEncoder.encode(String.valueOf(getParam(EX)))); + } + if (contains(PX)) { + byteParams.add(SafeEncoder.encode(PX)); + byteParams.add(SafeEncoder.encode(String.valueOf(getParam(PX)))); + } + + return byteParams.toArray(new byte[byteParams.size()][]); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZAddParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZAddParams.java new file mode 100755 index 000000000..3855bb8c2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZAddParams.java @@ -0,0 +1,69 @@ +package com.fr.third.redis.clients.jedis.params; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +import java.util.ArrayList; + +public class ZAddParams extends Params { + + private static final String XX = "xx"; + private static final String NX = "nx"; + private static final String CH = "ch"; + + public ZAddParams() { + } + + public static ZAddParams zAddParams() { + return new ZAddParams(); + } + + /** + * Only set the key if it does not already exist. + * @return ZAddParams + */ + public ZAddParams nx() { + addParam(NX); + return this; + } + + /** + * Only set the key if it already exist. + * @return ZAddParams + */ + public ZAddParams xx() { + addParam(XX); + return this; + } + + /** + * Modify the return value from the number of new elements added to the total number of elements + * changed + * @return ZAddParams + */ + public ZAddParams ch() { + addParam(CH); + return this; + } + + public byte[][] getByteParams(byte[] key, byte[]... args) { + ArrayList byteParams = new ArrayList(); + byteParams.add(key); + + if (contains(NX)) { + byteParams.add(SafeEncoder.encode(NX)); + } + if (contains(XX)) { + byteParams.add(SafeEncoder.encode(XX)); + } + if (contains(CH)) { + byteParams.add(SafeEncoder.encode(CH)); + } + + for (byte[] arg : args) { + byteParams.add(arg); + } + + return byteParams.toArray(new byte[byteParams.size()][]); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZIncrByParams.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZIncrByParams.java new file mode 100755 index 000000000..a77677702 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/params/ZIncrByParams.java @@ -0,0 +1,72 @@ +package com.fr.third.redis.clients.jedis.params; + +import com.fr.third.redis.clients.jedis.util.SafeEncoder; + +import java.util.ArrayList; + +/** + * Parameters for ZINCRBY commands
+ *
+ * In fact, Redis doesn't have parameters for ZINCRBY. Instead Redis has INCR parameter for ZADD.
+ * When users call ZADD with INCR option, its restriction (only one member) and return type is same + * to ZINCRBY.
+ * Document page for ZADD also describes INCR option to act like ZINCRBY.
+ * http://redis.io/commands/zadd
+ *
+ * So we decided to wrap "ZADD with INCR option" to ZINCRBY.
+ * https://github.com/xetorthio/jedis/issues/1067
+ *
+ * Works with Redis 3.0.2 and onwards. + */ +public class ZIncrByParams extends Params { + + private static final String XX = "xx"; + private static final String NX = "nx"; + private static final String INCR = "incr"; + + public ZIncrByParams() { + } + + public static ZIncrByParams zIncrByParams() { + return new ZIncrByParams(); + } + + /** + * Only set the key if it does not already exist. + * @return ZIncrByParams + */ + public ZIncrByParams nx() { + addParam(NX); + return this; + } + + /** + * Only set the key if it already exist. + * @return ZIncrByParams + */ + public ZIncrByParams xx() { + addParam(XX); + return this; + } + + public byte[][] getByteParams(byte[] key, byte[]... args) { + ArrayList byteParams = new ArrayList(); + byteParams.add(key); + + if (contains(NX)) { + byteParams.add(SafeEncoder.encode(NX)); + } + if (contains(XX)) { + byteParams.add(SafeEncoder.encode(XX)); + } + + byteParams.add(SafeEncoder.encode(INCR)); + + for (byte[] arg : args) { + byteParams.add(arg); + } + + return byteParams.toArray(new byte[byteParams.size()][]); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/ByteArrayComparator.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/ByteArrayComparator.java new file mode 100755 index 000000000..21e3fe467 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/ByteArrayComparator.java @@ -0,0 +1,24 @@ +package com.fr.third.redis.clients.jedis.util; + +public final class ByteArrayComparator { + private ByteArrayComparator() { + throw new InstantiationError( "Must not instantiate this class" ); + } + + public static int compare(final byte[] val1, final byte[] val2) { + int len1 = val1.length; + int len2 = val2.length; + int lmin = Math.min(len1, len2); + + for (int i = 0; i < lmin; i++) { + byte b1 = val1[i]; + byte b2 = val2[i]; + if(b1 < b2) return -1; + if(b1 > b2) return 1; + } + + if(len1 < len2) return -1; + if(len1 > len2) return 1; + return 0; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Hashing.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Hashing.java new file mode 100755 index 000000000..64521b6e2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Hashing.java @@ -0,0 +1,39 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public interface Hashing { + Hashing MURMUR_HASH = new MurmurHash(); + ThreadLocal md5Holder = new ThreadLocal(); + + Hashing MD5 = new Hashing() { + @Override + public long hash(String key) { + return hash(SafeEncoder.encode(key)); + } + + @Override + public long hash(byte[] key) { + try { + if (md5Holder.get() == null) { + md5Holder.set(MessageDigest.getInstance("MD5")); + } + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("++++ no md5 algorithm found"); + } + MessageDigest md5 = md5Holder.get(); + + md5.reset(); + md5.update(key); + byte[] bKey = md5.digest(); + long res = ((long) (bKey[3] & 0xFF) << 24) | ((long) (bKey[2] & 0xFF) << 16) + | ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF); + return res; + } + }; + + long hash(String key); + + long hash(byte[] key); +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/IOUtils.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/IOUtils.java new file mode 100755 index 000000000..66c585d3a --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/IOUtils.java @@ -0,0 +1,20 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.io.IOException; +import java.net.Socket; + +public class IOUtils { + private IOUtils() { + } + + public static void closeQuietly(Socket sock) { + // It's same thing as Apache Commons - IOUtils.closeQuietly() + if (sock != null) { + try { + sock.close(); + } catch (IOException e) { + // ignored + } + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisByteHashMap.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisByteHashMap.java new file mode 100755 index 000000000..063f7c102 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisByteHashMap.java @@ -0,0 +1,147 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class JedisByteHashMap implements Map, Cloneable, Serializable { + private static final long serialVersionUID = -6971431362627219416L; + private transient Map internalMap = new HashMap(); + + @Override + public void clear() { + internalMap.clear(); + } + + @Override + public boolean containsKey(Object key) { + if (key instanceof byte[]) return internalMap.containsKey(new ByteArrayWrapper((byte[]) key)); + return internalMap.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return internalMap.containsValue(value); + } + + @Override + public Set> entrySet() { + Iterator> iterator = internalMap.entrySet() + .iterator(); + HashSet> hashSet = new HashSet>(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + hashSet.add(new JedisByteEntry(entry.getKey().data, entry.getValue())); + } + return hashSet; + } + + @Override + public byte[] get(Object key) { + if (key instanceof byte[]) return internalMap.get(new ByteArrayWrapper((byte[]) key)); + return internalMap.get(key); + } + + @Override + public boolean isEmpty() { + return internalMap.isEmpty(); + } + + @Override + public Set keySet() { + Set keySet = new HashSet(); + Iterator iterator = internalMap.keySet().iterator(); + while (iterator.hasNext()) { + keySet.add(iterator.next().data); + } + return keySet; + } + + @Override + public byte[] put(byte[] key, byte[] value) { + return internalMap.put(new ByteArrayWrapper(key), value); + } + + @Override + @SuppressWarnings("unchecked") + public void putAll(Map m) { + Iterator iterator = m.entrySet().iterator(); + while (iterator.hasNext()) { + Entry next = (Entry) iterator + .next(); + internalMap.put(new ByteArrayWrapper(next.getKey()), next.getValue()); + } + } + + @Override + public byte[] remove(Object key) { + if (key instanceof byte[]) return internalMap.remove(new ByteArrayWrapper((byte[]) key)); + return internalMap.remove(key); + } + + @Override + public int size() { + return internalMap.size(); + } + + @Override + public Collection values() { + return internalMap.values(); + } + + private static final class ByteArrayWrapper { + private final byte[] data; + + public ByteArrayWrapper(byte[] data) { + if (data == null) { + throw new NullPointerException(); + } + this.data = data; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ByteArrayWrapper)) { + return false; + } + return Arrays.equals(data, ((ByteArrayWrapper) other).data); + } + + @Override + public int hashCode() { + return Arrays.hashCode(data); + } + } + + private static final class JedisByteEntry implements Entry { + private byte[] value; + private byte[] key; + + public JedisByteEntry(byte[] key, byte[] value) { + this.key = key; + this.value = value; + } + + @Override + public byte[] getKey() { + return this.key; + } + + @Override + public byte[] getValue() { + return this.value; + } + + @Override + public byte[] setValue(byte[] value) { + this.value = value; + return value; + } + + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterCRC16.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterCRC16.java new file mode 100755 index 000000000..b48a1bbc7 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterCRC16.java @@ -0,0 +1,89 @@ +package com.fr.third.redis.clients.jedis.util; + +/** + * CRC16 Implementation according to CCITT standard Polynomial : 1021 (x^16 + x^12 + x^5 + 1) See Appendix A. CRC16 reference implementation in ANSI + * C + */ +public final class JedisClusterCRC16 { + private static final int[] LOOKUP_TABLE = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, + 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, + 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, + 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, + 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, + 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, + 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, + 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, + 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, + 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, + 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, + 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, + 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, + 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, + 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, + 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, + 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, + 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, + 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, + 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, + 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,}; + + private JedisClusterCRC16(){ + throw new InstantiationError( "Must not instantiate this class" ); + } + + public static int getSlot(String key) { + key = JedisClusterHashTagUtil.getHashTag(key); + // optimization with modulo operator with power of 2 + // equivalent to getCRC16(key) % 16384 + return getCRC16(key) & (16384 - 1); + } + + public static int getSlot(byte[] key) { + int s = -1; + int e = -1; + boolean sFound = false; + for (int i = 0; i < key.length; i++) { + if (key[i] == '{' && !sFound) { + s = i; + sFound = true; + } + if (key[i] == '}' && sFound) { + e = i; + break; + } + } + if (s > -1 && e > -1 && e != s + 1) { + return getCRC16(key, s + 1, e) & (16384 - 1); + } + return getCRC16(key) & (16384 - 1); + } + + /** + * Create a CRC16 checksum from the bytes. implementation is from mp911de/lettuce, modified with + * some more optimizations + * @param bytes + * @return CRC16 as integer value See Issue 733 + */ + public static int getCRC16(byte[] bytes, int s, int e) { + int crc = 0x0000; + + for (int i = s; i < e; i++) { + crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (bytes[i] & 0xFF)) & 0xFF]); + } + return crc & 0xFFFF; + } + + public static int getCRC16(byte[] bytes) { + return getCRC16(bytes, 0, bytes.length); + } + + public static int getCRC16(String key) { + byte[] bytesKey = SafeEncoder.encode(key); + return getCRC16(bytesKey, 0, bytesKey.length); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterHashTagUtil.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterHashTagUtil.java new file mode 100755 index 000000000..28c685880 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisClusterHashTagUtil.java @@ -0,0 +1,36 @@ +package com.fr.third.redis.clients.jedis.util; + +/** + * Holds various methods/utilities to manipualte and parse redis hash-tags. See Cluster-Spec : Keys hash tags + */ +public final class JedisClusterHashTagUtil { + + private JedisClusterHashTagUtil() { + throw new InstantiationError("Must not instantiate this class"); + } + + public static String getHashTag(String key) { + return extractHashTag(key, true); + } + + public static boolean isClusterCompliantMatchPattern(byte[] matchPattern) { + return isClusterCompliantMatchPattern(SafeEncoder.encode(matchPattern)); + } + + public static boolean isClusterCompliantMatchPattern(String matchPattern) { + String tag = extractHashTag(matchPattern, false); + return tag != null && !tag.isEmpty(); + } + + private static String extractHashTag(String key, boolean returnKeyOnAbsence) { + int s = key.indexOf("{"); + if (s > -1) { + int e = key.indexOf("}", s + 1); + if (e > -1 && e != s + 1) { + return key.substring(s + 1, e); + } + } + return returnKeyOnAbsence ? key : null; + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisURIHelper.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisURIHelper.java new file mode 100755 index 000000000..7af24060e --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/JedisURIHelper.java @@ -0,0 +1,57 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.net.URI; + +public final class JedisURIHelper { + + private static final int DEFAULT_DB = 0; + + private static final String REDIS = "redis"; + private static final String REDISS = "rediss"; + + private JedisURIHelper(){ + throw new InstantiationError( "Must not instantiate this class" ); + } + + public static String getPassword(URI uri) { + String userInfo = uri.getUserInfo(); + if (userInfo != null) { + return userInfo.split(":", 2)[1]; + } + return null; + } + + public static int getDBIndex(URI uri) { + String[] pathSplit = uri.getPath().split("/", 2); + if (pathSplit.length > 1) { + String dbIndexStr = pathSplit[1]; + if (dbIndexStr.isEmpty()) { + return DEFAULT_DB; + } + return Integer.parseInt(dbIndexStr); + } else { + return DEFAULT_DB; + } + } + + public static boolean isValid(URI uri) { + if (isEmpty(uri.getScheme()) || isEmpty(uri.getHost()) || uri.getPort() == -1) { + return false; + } + + return true; + } + + private static boolean isEmpty(String value) { + return value == null || value.trim().length() == 0; + } + + public static boolean isRedisScheme(URI uri) { + return REDIS.equals(uri.getScheme()); + } + + public static boolean isRedisSSLScheme(URI uri) { + return REDISS.equals(uri.getScheme()); + } + +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/KeyMergeUtil.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/KeyMergeUtil.java new file mode 100755 index 000000000..312fdbaf2 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/KeyMergeUtil.java @@ -0,0 +1,21 @@ +package com.fr.third.redis.clients.jedis.util; + +public final class KeyMergeUtil { + private KeyMergeUtil(){ + throw new InstantiationError( "Must not instantiate this class" ); + } + + public static String[] merge(String destKey, String[] keys) { + String[] mergedKeys = new String[keys.length + 1]; + mergedKeys[0] = destKey; + System.arraycopy(keys, 0, mergedKeys, 1, keys.length); + return mergedKeys; + } + + public static byte[][] merge(byte[] destKey, byte[][] keys) { + byte[][] mergedKeys = new byte[keys.length + 1][]; + mergedKeys[0] = destKey; + System.arraycopy(keys, 0, mergedKeys, 1, keys.length); + return mergedKeys; + } +} \ No newline at end of file diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/MurmurHash.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/MurmurHash.java new file mode 100755 index 000000000..e79c262f3 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/MurmurHash.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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 com.fr.third.redis.clients.jedis.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * This is a very fast, non-cryptographic hash suitable for general hash-based lookup. See + * http://murmurhash.googlepages.com/ for more details.
+ *

+ * The C version of MurmurHash 2.0 found at that site was ported to Java by Andrzej Bialecki (ab at + * getopt org). + *

+ */ +public class MurmurHash implements Hashing { + /** + * Hashes bytes in an array. + * @param data The bytes to hash. + * @param seed The seed for the hash. + * @return The 32 bit hash of the bytes in question. + */ + public static int hash(byte[] data, int seed) { + return hash(ByteBuffer.wrap(data), seed); + } + + /** + * Hashes bytes in part of an array. + * @param data The data to hash. + * @param offset Where to start munging. + * @param length How many bytes to process. + * @param seed The seed to start with. + * @return The 32-bit hash of the data in question. + */ + public static int hash(byte[] data, int offset, int length, int seed) { + return hash(ByteBuffer.wrap(data, offset, length), seed); + } + + /** + * Hashes the bytes in a buffer from the current position to the limit. + * @param buf The bytes to hash. + * @param seed The seed for the hash. + * @return The 32 bit murmur hash of the bytes in the buffer. + */ + public static int hash(ByteBuffer buf, int seed) { + // save byte order for later restoration + ByteOrder byteOrder = buf.order(); + buf.order(ByteOrder.LITTLE_ENDIAN); + + int m = 0x5bd1e995; + int r = 24; + + int h = seed ^ buf.remaining(); + + int k; + while (buf.remaining() >= 4) { + k = buf.getInt(); + + k *= m; + k ^= k >>> r; + k *= m; + + h *= m; + h ^= k; + } + + if (buf.remaining() > 0) { + ByteBuffer finish = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); + // for big-endian version, use this first: + // finish.position(4-buf.remaining()); + finish.put(buf).rewind(); + h ^= finish.getInt(); + h *= m; + } + + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + buf.order(byteOrder); + return h; + } + + public static long hash64A(byte[] data, int seed) { + return hash64A(ByteBuffer.wrap(data), seed); + } + + public static long hash64A(byte[] data, int offset, int length, int seed) { + return hash64A(ByteBuffer.wrap(data, offset, length), seed); + } + + public static long hash64A(ByteBuffer buf, int seed) { + ByteOrder byteOrder = buf.order(); + buf.order(ByteOrder.LITTLE_ENDIAN); + + long m = 0xc6a4a7935bd1e995L; + int r = 47; + + long h = seed ^ (buf.remaining() * m); + + long k; + while (buf.remaining() >= 8) { + k = buf.getLong(); + + k *= m; + k ^= k >>> r; + k *= m; + + h ^= k; + h *= m; + } + + if (buf.remaining() > 0) { + ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); + // for big-endian version, do this first: + // finish.position(8-buf.remaining()); + finish.put(buf).rewind(); + h ^= finish.getLong(); + h *= m; + } + + h ^= h >>> r; + h *= m; + h ^= h >>> r; + + buf.order(byteOrder); + return h; + } + + @Override + public long hash(byte[] key) { + return hash64A(key, 0x1234ABCD); + } + + @Override + public long hash(String key) { + return hash(SafeEncoder.encode(key)); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Pool.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Pool.java new file mode 100755 index 000000000..01892ecdc --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Pool.java @@ -0,0 +1,191 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.io.Closeable; +import java.util.NoSuchElementException; + +import com.fr.third.org.apache.commons.pool2.PooledObjectFactory; +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPool; +import com.fr.third.org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; +import com.fr.third.redis.clients.jedis.exceptions.JedisExhaustedPoolException; + +public abstract class Pool implements Closeable { + protected GenericObjectPool internalPool; + + /** + * Using this constructor means you have to set and initialize the internalPool yourself. + */ + public Pool() { + } + + public Pool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { + initPool(poolConfig, factory); + } + + @Override + public void close() { + destroy(); + } + + public boolean isClosed() { + return this.internalPool.isClosed(); + } + + public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { + + if (this.internalPool != null) { + try { + closeInternalPool(); + } catch (Exception e) { + } + } + + this.internalPool = new GenericObjectPool(factory, poolConfig); + } + + public T getResource() { + try { + return internalPool.borrowObject(); + } catch (NoSuchElementException nse) { + if (null == nse.getCause()) { // The exception was caused by an exhausted pool + throw new JedisExhaustedPoolException( + "Could not get a resource since the pool is exhausted", nse); + } + // Otherwise, the exception was caused by the implemented activateObject() or ValidateObject() + throw new JedisException("Could not get a resource from the pool", nse); + } catch (Exception e) { + throw new JedisConnectionException("Could not get a resource from the pool", e); + } + } + + protected void returnResourceObject(final T resource) { + if (resource == null) { + return; + } + try { + internalPool.returnObject(resource); + } catch (Exception e) { + throw new JedisException("Could not return the resource to the pool", e); + } + } + + protected void returnBrokenResource(final T resource) { + if (resource != null) { + returnBrokenResourceObject(resource); + } + } + + protected void returnResource(final T resource) { + if (resource != null) { + returnResourceObject(resource); + } + } + + public void destroy() { + closeInternalPool(); + } + + protected void returnBrokenResourceObject(final T resource) { + try { + internalPool.invalidateObject(resource); + } catch (Exception e) { + throw new JedisException("Could not return the resource to the pool", e); + } + } + + protected void closeInternalPool() { + try { + internalPool.close(); + } catch (Exception e) { + throw new JedisException("Could not destroy the pool", e); + } + } + + /** + * Returns the number of instances currently borrowed from this pool. + * + * @return The number of instances currently borrowed from this pool, -1 if + * the pool is inactive. + */ + public int getNumActive() { + if (poolInactive()) { + return -1; + } + + return this.internalPool.getNumActive(); + } + + /** + * Returns the number of instances currently idle in this pool. + * + * @return The number of instances currently idle in this pool, -1 if the + * pool is inactive. + */ + public int getNumIdle() { + if (poolInactive()) { + return -1; + } + + return this.internalPool.getNumIdle(); + } + + /** + * Returns an estimate of the number of threads currently blocked waiting for + * a resource from this pool. + * + * @return The number of threads waiting, -1 if the pool is inactive. + */ + public int getNumWaiters() { + if (poolInactive()) { + return -1; + } + + return this.internalPool.getNumWaiters(); + } + + /** + * Returns the mean waiting time spent by threads to obtain a resource from + * this pool. + * + * @return The mean waiting time, in milliseconds, -1 if the pool is + * inactive. + */ + public long getMeanBorrowWaitTimeMillis() { + if (poolInactive()) { + return -1; + } + + return this.internalPool.getMeanBorrowWaitTimeMillis(); + } + + /** + * Returns the maximum waiting time spent by threads to obtain a resource + * from this pool. + * + * @return The maximum waiting time, in milliseconds, -1 if the pool is + * inactive. + */ + public long getMaxBorrowWaitTimeMillis() { + if (poolInactive()) { + return -1; + } + + return this.internalPool.getMaxBorrowWaitTimeMillis(); + } + + private boolean poolInactive() { + return this.internalPool == null || this.internalPool.isClosed(); + } + + public void addObjects(int count) { + try { + for (int i = 0; i < count; i++) { + this.internalPool.addObject(); + } + } catch (Exception e) { + throw new JedisException("Error trying to add idle objects", e); + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisInputStream.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisInputStream.java new file mode 100755 index 000000000..da7ffda6b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisInputStream.java @@ -0,0 +1,206 @@ +/* + * Copyright 2009-2010 MBTE Sweden AB. 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 com.fr.third.redis.clients.jedis.util; + +import java.io.*; + +import com.fr.third.redis.clients.jedis.exceptions.JedisConnectionException; + +/** + * This class assumes (to some degree) that we are reading a RESP stream. As such it assumes certain + * conventions regarding CRLF line termination. It also assumes that if the Protocol layer requires + * a byte that if that byte is not there it is a stream error. + */ +public class RedisInputStream extends FilterInputStream { + + protected final byte[] buf; + + protected int count, limit; + + public RedisInputStream(InputStream in, int size) { + super(in); + if (size <= 0) { + throw new IllegalArgumentException("Buffer size <= 0"); + } + buf = new byte[size]; + } + + public RedisInputStream(InputStream in) { + this(in, 8192); + } + + public byte readByte() throws JedisConnectionException { + ensureFill(); + return buf[count++]; + } + + public String readLine() { + final StringBuilder sb = new StringBuilder(); + while (true) { + ensureFill(); + + byte b = buf[count++]; + if (b == '\r') { + ensureFill(); // Must be one more byte + + byte c = buf[count++]; + if (c == '\n') { + break; + } + sb.append((char) b); + sb.append((char) c); + } else { + sb.append((char) b); + } + } + + final String reply = sb.toString(); + if (reply.length() == 0) { + throw new JedisConnectionException("It seems like server has closed the connection."); + } + + return reply; + } + + public byte[] readLineBytes() { + + /* + * This operation should only require one fill. In that typical case we optimize allocation and + * copy of the byte array. In the edge case where more than one fill is required then we take a + * slower path and expand a byte array output stream as is necessary. + */ + + ensureFill(); + + int pos = count; + final byte[] buf = this.buf; + while (true) { + if (pos == limit) { + return readLineBytesSlowly(); + } + + if (buf[pos++] == '\r') { + if (pos == limit) { + return readLineBytesSlowly(); + } + + if (buf[pos++] == '\n') { + break; + } + } + } + + final int N = (pos - count) - 2; + final byte[] line = new byte[N]; + System.arraycopy(buf, count, line, 0, N); + count = pos; + return line; + } + + /** + * Slow path in case a line of bytes cannot be read in one #fill() operation. This is still faster + * than creating the StrinbBuilder, String, then encoding as byte[] in Protocol, then decoding + * back into a String. + */ + private byte[] readLineBytesSlowly() { + ByteArrayOutputStream bout = null; + while (true) { + ensureFill(); + + byte b = buf[count++]; + if (b == '\r') { + ensureFill(); // Must be one more byte + + byte c = buf[count++]; + if (c == '\n') { + break; + } + + if (bout == null) { + bout = new ByteArrayOutputStream(16); + } + + bout.write(b); + bout.write(c); + } else { + if (bout == null) { + bout = new ByteArrayOutputStream(16); + } + + bout.write(b); + } + } + + return bout == null ? new byte[0] : bout.toByteArray(); + } + + public int readIntCrLf() { + return (int) readLongCrLf(); + } + + public long readLongCrLf() { + final byte[] buf = this.buf; + + ensureFill(); + + final boolean isNeg = buf[count] == '-'; + if (isNeg) { + ++count; + } + + long value = 0; + while (true) { + ensureFill(); + + final int b = buf[count++]; + if (b == '\r') { + ensureFill(); + + if (buf[count++] != '\n') { + throw new JedisConnectionException("Unexpected character!"); + } + + break; + } else { + value = value * 10 + b - '0'; + } + } + + return (isNeg ? -value : value); + } + + @Override + public int read(byte[] b, int off, int len) throws JedisConnectionException { + ensureFill(); + + final int length = Math.min(limit - count, len); + System.arraycopy(buf, count, b, off, length); + count += length; + return length; + } + + /** + * This methods assumes there are required bytes to be read. If we cannot read anymore bytes an + * exception is thrown to quickly ascertain that the stream was smaller than expected. + */ + private void ensureFill() throws JedisConnectionException { + if (count >= limit) { + try { + limit = in.read(buf); + count = 0; + if (limit == -1) { + throw new JedisConnectionException("Unexpected end of stream."); + } + } catch (IOException e) { + throw new JedisConnectionException(e); + } + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisOutputStream.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisOutputStream.java new file mode 100755 index 000000000..1f7e8752b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/RedisOutputStream.java @@ -0,0 +1,136 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * The class implements a buffered output stream without synchronization There are also special + * operations like in-place string encoding. This stream fully ignore mark/reset and should not be + * used outside Jedis + */ +public final class RedisOutputStream extends FilterOutputStream { + protected final byte[] buf; + + protected int count; + + private final static int[] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, + 999999999, Integer.MAX_VALUE }; + + private final static byte[] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', + '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', + '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', + '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', + '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', + '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', }; + + private final static byte[] DigitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', + '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', }; + + private final static byte[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', + 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z' }; + + public RedisOutputStream(final OutputStream out) { + this(out, 8192); + } + + public RedisOutputStream(final OutputStream out, final int size) { + super(out); + if (size <= 0) { + throw new IllegalArgumentException("Buffer size <= 0"); + } + buf = new byte[size]; + } + + private void flushBuffer() throws IOException { + if (count > 0) { + out.write(buf, 0, count); + count = 0; + } + } + + public void write(final byte b) throws IOException { + if (count == buf.length) { + flushBuffer(); + } + buf[count++] = b; + } + + @Override + public void write(final byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + if (len >= buf.length) { + flushBuffer(); + out.write(b, off, len); + } else { + if (len >= buf.length - count) { + flushBuffer(); + } + + System.arraycopy(b, off, buf, count, len); + count += len; + } + } + + public void writeCrLf() throws IOException { + if (2 >= buf.length - count) { + flushBuffer(); + } + + buf[count++] = '\r'; + buf[count++] = '\n'; + } + + public void writeIntCrLf(int value) throws IOException { + if (value < 0) { + write((byte) '-'); + value = -value; + } + + int size = 0; + while (value > sizeTable[size]) + size++; + + size++; + if (size >= buf.length - count) { + flushBuffer(); + } + + int q, r; + int charPos = count + size; + + while (value >= 65536) { + q = value / 100; + r = value - ((q << 6) + (q << 5) + (q << 2)); + value = q; + buf[--charPos] = DigitOnes[r]; + buf[--charPos] = DigitTens[r]; + } + + for (;;) { + q = (value * 52429) >>> (16 + 3); + r = value - ((q << 3) + (q << 1)); + buf[--charPos] = digits[r]; + value = q; + if (value == 0) break; + } + count += size; + + writeCrLf(); + } + + @Override + public void flush() throws IOException { + flushBuffer(); + out.flush(); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/SafeEncoder.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/SafeEncoder.java new file mode 100755 index 000000000..4b71cb469 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/SafeEncoder.java @@ -0,0 +1,43 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.io.UnsupportedEncodingException; + +import com.fr.third.redis.clients.jedis.Protocol; +import com.fr.third.redis.clients.jedis.exceptions.JedisDataException; +import com.fr.third.redis.clients.jedis.exceptions.JedisException; + +/** + * The only reason to have this is to be able to compatible with java 1.5 :( + */ +public final class SafeEncoder { + private SafeEncoder(){ + throw new InstantiationError( "Must not instantiate this class" ); + } + + public static byte[][] encodeMany(final String... strs) { + byte[][] many = new byte[strs.length][]; + for (int i = 0; i < strs.length; i++) { + many[i] = encode(strs[i]); + } + return many; + } + + public static byte[] encode(final String str) { + try { + if (str == null) { + throw new JedisDataException("value sent to redis cannot be null"); + } + return str.getBytes(Protocol.CHARSET); + } catch (UnsupportedEncodingException e) { + throw new JedisException(e); + } + } + + public static String encode(final byte[] data) { + try { + return new String(data, Protocol.CHARSET); + } catch (UnsupportedEncodingException e) { + throw new JedisException(e); + } + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/ShardInfo.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/ShardInfo.java new file mode 100755 index 000000000..58efc65ce --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/ShardInfo.java @@ -0,0 +1,20 @@ +package com.fr.third.redis.clients.jedis.util; + +public abstract class ShardInfo { + private int weight; + + public ShardInfo() { + } + + public ShardInfo(int weight) { + this.weight = weight; + } + + public int getWeight() { + return this.weight; + } + + protected abstract T createResource(); + + public abstract String getName(); +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Sharded.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Sharded.java new file mode 100755 index 000000000..32f101a84 --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Sharded.java @@ -0,0 +1,108 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Sharded> { + + public static final int DEFAULT_WEIGHT = 1; + private TreeMap nodes; + private final Hashing algo; + private final Map, R> resources = new LinkedHashMap, R>(); + + /** + * The default pattern used for extracting a key tag. The pattern must have a group (between + * parenthesis), which delimits the tag to be hashed. A null pattern avoids applying the regular + * expression for each lookup, improving performance a little bit is key tags aren't being used. + */ + private Pattern tagPattern = null; + // the tag is anything between {} + public static final Pattern DEFAULT_KEY_TAG_PATTERN = Pattern.compile("\\{(.+?)\\}"); + + public Sharded(List shards) { + this(shards, Hashing.MURMUR_HASH); // MD5 is really not good as we works + // with 64-bits not 128 + } + + public Sharded(List shards, Hashing algo) { + this.algo = algo; + initialize(shards); + } + + public Sharded(List shards, Pattern tagPattern) { + this(shards, Hashing.MURMUR_HASH, tagPattern); // MD5 is really not good + // as we works with + // 64-bits not 128 + } + + public Sharded(List shards, Hashing algo, Pattern tagPattern) { + this.algo = algo; + this.tagPattern = tagPattern; + initialize(shards); + } + + private void initialize(List shards) { + nodes = new TreeMap(); + + for (int i = 0; i != shards.size(); ++i) { + final S shardInfo = shards.get(i); + if (shardInfo.getName() == null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { + nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo); + } + else for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { + nodes.put(this.algo.hash(shardInfo.getName() + "*" + n), shardInfo); + } + resources.put(shardInfo, shardInfo.createResource()); + } + } + + public R getShard(byte[] key) { + return resources.get(getShardInfo(key)); + } + + public R getShard(String key) { + return resources.get(getShardInfo(key)); + } + + public S getShardInfo(byte[] key) { + SortedMap tail = nodes.tailMap(algo.hash(key)); + if (tail.isEmpty()) { + return nodes.get(nodes.firstKey()); + } + return tail.get(tail.firstKey()); + } + + public S getShardInfo(String key) { + return getShardInfo(SafeEncoder.encode(getKeyTag(key))); + } + + /** + * A key tag is a special pattern inside a key that, if preset, is the only part of the key hashed + * in order to select the server for this key. + * @see partitioning + * @param key + * @return The tag if it exists, or the original key + */ + public String getKeyTag(String key) { + if (tagPattern != null) { + Matcher m = tagPattern.matcher(key); + if (m.find()) return m.group(1); + } + return key; + } + + public Collection getAllShardInfo() { + return Collections.unmodifiableCollection(nodes.values()); + } + + public Collection getAllShards() { + return Collections.unmodifiableCollection(resources.values()); + } +} diff --git a/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Slowlog.java b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Slowlog.java new file mode 100755 index 000000000..324c31e4b --- /dev/null +++ b/fine-jedis/src/com/fr/third/redis/clients/jedis/util/Slowlog.java @@ -0,0 +1,60 @@ +package com.fr.third.redis.clients.jedis.util; + +import java.util.ArrayList; +import java.util.List; + +public class Slowlog { + private final long id; + private final long timeStamp; + private final long executionTime; + private final List args; + private static final String COMMA = ","; + + @SuppressWarnings("unchecked") + private Slowlog(List properties) { + super(); + this.id = (Long) properties.get(0); + this.timeStamp = (Long) properties.get(1); + this.executionTime = (Long) properties.get(2); + + List bargs = (List) properties.get(3); + this.args = new ArrayList(bargs.size()); + + for (byte[] barg : bargs) { + this.args.add(SafeEncoder.encode(barg)); + } + } + + @SuppressWarnings("unchecked") + public static List from(List nestedMultiBulkReply) { + List logs = new ArrayList(nestedMultiBulkReply.size()); + for (Object obj : nestedMultiBulkReply) { + List properties = (List) obj; + logs.add(new Slowlog(properties)); + } + + return logs; + } + + public long getId() { + return id; + } + + public long getTimeStamp() { + return timeStamp; + } + + public long getExecutionTime() { + return executionTime; + } + + public List getArgs() { + return args; + } + + @Override + public String toString() { + return new StringBuilder().append(id).append(COMMA).append(timeStamp).append(COMMA) + .append(executionTime).append(COMMA).append(args).toString(); + } +}