Repository: alphazero/jredis Branch: master Commit: 98b4c73701b8 Files: 241 Total size: 1.2 MB Directory structure: gitextract_s0bg69rx/ ├── .gitignore ├── DEV-NOTES.txt ├── LICENSE ├── NOTICE ├── README ├── RELEASE-NOTES.txt ├── Release/ │ ├── RELEASE-NOTES-Update-03122010.txt │ ├── RELEASE-NOTES-Update-08132009.txt │ ├── RELEASE-NOTES-Update-11082009.txt │ ├── RELEASE_BUILD_TIME │ ├── jredis-core-all-a.0-SNAPSHOT-jar-with-dependencies.jar │ ├── jredis-core-all-a.0-SNAPSHOT-sources.jar │ ├── jredis-core-all-a.0-SNAPSHOT.jar │ ├── jredis-core-api-a.0-SNAPSHOT-sources.jar │ ├── jredis-core-api-a.0-SNAPSHOT.jar │ ├── jredis-core-bench-a.0-SNAPSHOT-jar-with-dependencies.jar │ ├── jredis-core-bench-a.0-SNAPSHOT-sources.jar │ ├── jredis-core-bench-a.0-SNAPSHOT.jar │ ├── jredis-core-ri-a.0-SNAPSHOT-jar-with-dependencies.jar │ ├── jredis-core-ri-a.0-SNAPSHOT-sources.jar │ ├── jredis-core-ri-a.0-SNAPSHOT.jar │ ├── jredis-examples-a.0-SNAPSHOT-jar-with-dependencies.jar │ ├── jredis-examples-a.0-SNAPSHOT-sources.jar │ └── jredis-examples-a.0-SNAPSHOT.jar ├── core/ │ ├── LICENSE │ ├── NOTICE │ ├── README │ ├── all/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ └── pom.xml │ ├── api/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── jredis/ │ │ │ │ ├── ClientRuntimeException.java │ │ │ │ ├── Codec.java │ │ │ │ ├── Event.java │ │ │ │ ├── JRedis.java │ │ │ │ ├── JRedisFuture.java │ │ │ │ ├── KeyValueSet.java │ │ │ │ ├── NotSupportedException.java │ │ │ │ ├── ObjectEncoding.java │ │ │ │ ├── ObjectInfo.java │ │ │ │ ├── ProviderException.java │ │ │ │ ├── Query.java │ │ │ │ ├── Redis.java │ │ │ │ ├── RedisException.java │ │ │ │ ├── RedisInfo.java │ │ │ │ ├── RedisType.java │ │ │ │ ├── Semantics.java │ │ │ │ ├── Sort.java │ │ │ │ ├── ZSetEntry.java │ │ │ │ ├── _specification.java │ │ │ │ ├── connector/ │ │ │ │ │ ├── Connection.java │ │ │ │ │ ├── ConnectionException.java │ │ │ │ │ ├── ConnectionFault.java │ │ │ │ │ ├── ConnectionReset.java │ │ │ │ │ ├── ConnectionSpec.java │ │ │ │ │ ├── FaultedConnection.java │ │ │ │ │ ├── Message.java │ │ │ │ │ ├── NotConnectedException.java │ │ │ │ │ ├── RequestListener.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── package-info.java │ │ │ │ ├── protocol/ │ │ │ │ │ ├── BulkResponse.java │ │ │ │ │ ├── Command.java │ │ │ │ │ ├── CommandNotImplemented.java │ │ │ │ │ ├── MultiBulkResponse.java │ │ │ │ │ ├── Protocol.java │ │ │ │ │ ├── Request.java │ │ │ │ │ ├── Response.java │ │ │ │ │ ├── ResponseStatus.java │ │ │ │ │ ├── StatusResponse.java │ │ │ │ │ ├── ValueResponse.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── resource/ │ │ │ │ │ ├── Context.java │ │ │ │ │ ├── Resource.java │ │ │ │ │ ├── ResourceException.java │ │ │ │ │ ├── _specification.java │ │ │ │ │ └── package-info.java │ │ │ │ └── semantics/ │ │ │ │ ├── BasicCodecManager.java │ │ │ │ ├── CodecManager.java │ │ │ │ ├── KeyCodec.java │ │ │ │ ├── SemanticJRedis.java │ │ │ │ ├── SemanticQuery.java │ │ │ │ ├── SemanticSort.java │ │ │ │ ├── Semantics.java │ │ │ │ └── package-info.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── redis-commands-2.4.n.txt │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── jredis/ │ │ │ ├── TestBase.java │ │ │ ├── compliance/ │ │ │ │ └── CheckRedisCompliance.java │ │ │ ├── connector/ │ │ │ │ └── TestSpecElements.java │ │ │ └── protocol/ │ │ │ └── TestCommand.java │ │ └── resources/ │ │ └── log4j.properties │ ├── bench/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── jredis/ │ │ │ ├── bench/ │ │ │ │ ├── JRedisBenchmark.java │ │ │ │ ├── JRedisJProfileSubject.java │ │ │ │ └── Util.java │ │ │ └── ri/ │ │ │ └── alphazero/ │ │ │ └── bench/ │ │ │ ├── JRedisClientBenchmark.java │ │ │ ├── JRedisClientJProfileSubject.java │ │ │ ├── JRedisPipelineServiceBenchmark.java │ │ │ ├── JRedisServiceBenchmark.java │ │ │ ├── SimpleBenchJRedisAsync.java │ │ │ ├── SimpleBenchJRedisChunkedPipeline.java │ │ │ ├── SimpleBenchJRedisClient.java │ │ │ └── SimpleBenchJRedisPipeline.java │ │ └── resources/ │ │ └── log4j.properties │ ├── pom.xml │ └── ri/ │ ├── LICENSE │ ├── NOTICE │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── jredis/ │ │ └── ri/ │ │ ├── RI.java │ │ ├── alphazero/ │ │ │ ├── BulkSetMapping.java │ │ │ ├── JRedisAsyncClient.java │ │ │ ├── JRedisChunkedPipeline.java │ │ │ ├── JRedisClient.java │ │ │ ├── JRedisFutureSupport.java │ │ │ ├── JRedisPipeline.java │ │ │ ├── JRedisPipelineService.java │ │ │ ├── JRedisService.java │ │ │ ├── JRedisSupport.java │ │ │ ├── Pair.java │ │ │ ├── RedisVersion.java │ │ │ ├── SyncJRedisBase.java │ │ │ ├── ZSetEntryImpl.java │ │ │ ├── _specification.java │ │ │ ├── connection/ │ │ │ │ ├── AsyncConnection.java │ │ │ │ ├── AsyncPipelineConnection.java │ │ │ │ ├── ChunkedPipelineConnection.java │ │ │ │ ├── ConnectionBase.java │ │ │ │ ├── DefaultConnectionFactory.java │ │ │ │ ├── DefaultConnectionSpec.java │ │ │ │ ├── HeartbeatJinn.java │ │ │ │ ├── PendingRequest.java │ │ │ │ ├── PipelineConnectionBase.java │ │ │ │ ├── SyncConnection.java │ │ │ │ ├── SyncPipelineConnection.java │ │ │ │ └── UnexpectedEOFException.java │ │ │ ├── package-info.java │ │ │ ├── protocol/ │ │ │ │ ├── ConcurrentSyncProtocol.java │ │ │ │ ├── DefaultProtocolFactory.java │ │ │ │ ├── ProtocolBase.java │ │ │ │ ├── ResponseSupport.java │ │ │ │ ├── SyncProtocol.java │ │ │ │ └── VirtualResponse.java │ │ │ ├── semantics/ │ │ │ │ ├── DefaultKeyCodec.java │ │ │ │ ├── DefaultStringCodec.java │ │ │ │ └── GZipCompressedStringCodec.java │ │ │ └── support/ │ │ │ ├── Assert.java │ │ │ ├── Convert.java │ │ │ ├── DefaultCodec.java │ │ │ ├── FastBufferedInputStream.java │ │ │ ├── GZip.java │ │ │ ├── Log.java │ │ │ ├── Signal.java │ │ │ ├── SortSupport.java │ │ │ └── package-info.java │ │ └── package-info.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── jredis/ │ │ └── ri/ │ │ ├── JRedisTestSuiteBase.java │ │ ├── ProviderTestBase.java │ │ ├── adhoc/ │ │ │ ├── AdHocTestChunkPipeline.java │ │ │ ├── AdHocTestInfo.java │ │ │ └── AdHocTestNoConnection.java │ │ └── alphazero/ │ │ ├── ConcurrentJRedisProviderTestsBase.java │ │ ├── JRedisAsyncClientTest.java │ │ ├── JRedisChunkedPipelineClientTest.java │ │ ├── JRedisClientTest.java │ │ ├── JRedisFutureProviderTestsBase.java │ │ ├── JRedisPipelineServiceTest.java │ │ ├── JRedisPipelineTest.java │ │ ├── JRedisProviderTestsBase.java │ │ ├── JRedisServiceTest.java │ │ └── support/ │ │ ├── ConvertTest.java │ │ └── GZipTest.java │ └── resources/ │ └── log4j.properties ├── documentation/ │ ├── LICENSE │ ├── NOTICE │ └── design/ │ └── notes/ │ └── api/ │ └── jredis-api.txt ├── examples/ │ ├── LICENSE │ ├── NOTICE │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── jredis/ │ │ └── examples/ │ │ ├── HelloAgain.java │ │ ├── PipelineInAction.java │ │ ├── UsingConnectionSpec.java │ │ ├── UsingJRedisFuture.java │ │ ├── UsingJRedisPipeline.java │ │ ├── UsingJRedisPipelineService.java │ │ ├── UsingJRedisService.java │ │ └── commands/ │ │ ├── UsingBulkCommands.java │ │ ├── UsingZrangeSubset.java │ │ └── package-info.java │ └── resources/ │ └── log4j.properties ├── extensions/ │ ├── 3rd party Licenses/ │ │ ├── NET.SPY.MEMCACHED LICENSE │ │ └── NET.SPY.MEMCACHED NOTICE │ ├── LICENSE │ ├── NOTICE │ ├── README.markdown │ ├── api/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.markdown │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── jredis/ │ │ └── cluster/ │ │ ├── ClusterModel.java │ │ ├── ClusterNodeSpec.java │ │ ├── ClusterSpec.java │ │ ├── ClusterType.java │ │ ├── connector/ │ │ │ └── ClusterConnection.java │ │ ├── model/ │ │ │ ├── ConsistentHashCluster.java │ │ │ └── StaticHashCluster.java │ │ └── support/ │ │ └── HashAlgorithm.java │ ├── pom.xml │ └── ri/ │ ├── LICENSE │ ├── NOTICE │ ├── README.markdown │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── jredis/ │ │ └── ri/ │ │ └── cluster/ │ │ ├── DefaultClusterNodeSpec.java │ │ ├── DefaultClusterSpec.java │ │ ├── connection/ │ │ │ ├── ClusterConnectionBase.java │ │ │ └── SynchClusterConnection.java │ │ ├── model/ │ │ │ ├── BasicStaticHashCluster.java │ │ │ ├── KetamaClusterModel.java │ │ │ └── KetamaHashProvider.java │ │ └── support/ │ │ └── CryptoHashUtils.java │ └── test/ │ └── java/ │ └── org/ │ └── jredis/ │ ├── cluster/ │ │ ├── ClusterModelProviderTestBase.java │ │ ├── ClusterNodeSpecProviderTestBase.java │ │ ├── ClusterSpecProviderTestBase.java │ │ ├── ClusterSuiteTestData.java │ │ ├── ProviderTestBase.java │ │ ├── RefImplTestSuiteBase.java │ │ ├── models/ │ │ │ ├── BasicStaticHashClusterTest.java │ │ │ ├── ConsistentHashClusterProviderTestBase.java │ │ │ ├── KetamaClusterModelTest.java │ │ │ ├── KetamaHashAlgoTest.java │ │ │ └── StaticHashClusterProviderTestBase.java │ │ └── support/ │ │ └── HashAlgorithmProviderTestBase.java │ ├── ri/ │ │ └── cluster/ │ │ ├── ClusterNodeSpecImplTest.java │ │ ├── DefaultClusterSpecTest.java │ │ └── support/ │ │ └── CryptoHashUtilTest.java │ └── test/ │ └── util/ │ └── RunningAverage.java ├── pom.xml └── redis_version_compliance.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ target *.log *.classpath *.project *.settings *.DS_Store *.class jredis.tmproj *.iml *.idea ================================================ FILE: DEV-NOTES.txt ================================================ Wed Feb 1 2012 * Merged chunk-pipelien branch to master REFERENCE commit a9cff24fd8f041ec181598c571989680a4e5ad15 is master prior to merge. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: README ================================================ (This README is mainly addressing the project structure and build. For information about JRedis, please see the RELEASE-NOTES.) ---------------------------------------------------------- BUILD ---------------------------------------------------------- * Requirements: - maven Project uses Maven 2 as the build management system. You will need maven to conveniently build and package the project jars. Project master is built on Mac OS X 10.5 using maven 2.0.9. * howto Project is not yet using assemblies, but is a multi-module maven build with master POM in root directory (where you found this document). Each module has its own pom, but you will need to run maven with specific goals at the level of a major sub-module. As of now (intial release alpha.0-04152009) that translates to a 'Core' and an 'Examples'. If you wish to use JRedis only you will need the product of the Core-RI (reference implementation). If you wish to run benchmarks + use JRedis, you will also need to build the Core-BENCHMARK. Simplest thing to do is to run maven with goal package at the Core module level. This will initiate and build each module in Core: API - The specifications, which are mostly interfaces. RI - The reference implementation, which is you will use. BENCH - The benchmark classes ALL - A convenience virtual module that will build a unified jar of all modules besides itself in Core. On command line in ~/core/., issue: mvn install This will compile, test, package, and install JRedis core artifacts in your local repository and create a set of jars in each core module's 'target' directory. The pom of the Core as well as the super pom in the root folder use a source attachment plugin, so a source jar will also be created for each module. The poms of core-ri, core-bench, and core-all use a include dependencies mechanism which will build, besides the individual module's jar, an aggregate unified jar of the module's classes + the classes of any dependency it has declared. (Only install goal will do this -- package goal will simply create the module's own jar file(s)). For example, issuing mvn install from core will create in module core-bench's target directory the class and source jars for the BENCH module + a single jar containing all dependencies, which for BENCH are 'API' and 'RI'. The build system uses a structured naming convention which results in longish names, but it is very effective. (You can obviously simply change the jar names when you deploy to your application's lib or jvm classpath as you see fit.) The super pom in project root will initiate the same for all modules of JRedis, which as of now are CORE, and EXAMPLES. (Same considerations apply here.) **** IMPORTANT NOTE REGARDING UNIT TEST **** Running unit tests is absolutely critical to a sane process for developing JRedis, given that it is subject to a very demanding refactoring regime (as it is a work in progress). That means testing is critical for the master copy and accordingly ALL poms in the project require SUCCESSFUL tests after compile before continuing. **** REDIS MUST BE RUNNING ON LOCALHOST ***** So, as tests on a connector without a server are fairly pointless, this project will NOT build unless you have your localhost (6379) Redis server running. **** BUILD USES PASSWORD 'jredis' **** If you do not have password set on your redis, no worries. If you do, the build will fail, unless you do one of the following: 1) Change the password in core/pom.xml to match your password in redis.conf 2) Alternatively tell maven to skip tests: mvn -Dmaven.test.skip=true install **** REMEMBER: TESTS WILL FLUSH DBS 13 AND 10 ***** - 04-21-09 ================================================ FILE: RELEASE-NOTES.txt ================================================ ~!! n o ' r o o z !!~ ------------------------------------------------------------------------- n o ' r o o z R E L E A S E N O T E S ------------------------------------------------------------------------- JREDIS SPECIFICATION AND REFERENCE IMPLEMENTATION [updated on 04-21-09] * About This is the initial (alpha.0) release of JREDIS, (code named "no'rooz"). Work on this began around the Persian New Year (No'Rooz, meaning New Day) and has continued since then almost to the exclusion of nearly everything else. * Redis compliance The specification and the implementation in this release comply with the the latest redis development release specification as of 04-21-09 and tested against the build of the REDIS server from the github repository. * Structure JREDIS is composed of a Reference and a reference Implementation, under a unified top-level packages. (A single jar build is provided in this release, which can be included in your classpath and simply utilized.) Further Java artifacts include testing, benchmarking, and example source directories. All these, and the core, can be found under the java directory. To build JREDIS, you will only need to compile the files under ~/java/src/. * Included features This initial revision includes a complete functioning Synchronous connector for REDIS. (Asynchronous connection and pipelining support are on the immediate TODO shortlist.) The initial release connection only supports (for now) a use case scenario of create, continuously use, and discard. Enhanced connection maintenance and transparent re-connect are also on the immediate TODO list for the initial revision. This connector is exposed for use as org.jredis.ri.alphazero.JRedisClient and you simply need to instantiate and immediately use it to connect to your localhost REDIS server. This is a synchronous mode connection, with blocking semantics for JRedis API, and is intended for use strictly in a synchronized request/reply manner. To achieve concurrent connections, simply create a client instance per thread. (See the benchmark classes for examples.) A queuing synchronous mode connector suitable for use in containers is on the drawing board. (If you can not wait for this, you may hack your own by placing a JRedis facade in front of a combination of classes from java.util.concurrent and using a pool of JRedisClients behind the scenes.) If you elect to do this, do note that connections to Redis are effectively stateful and you can not trivially use a connection to serve the requests of multiple requesters (threads) IFF you use commands such as SELECT. Beyond that, JREDIS as of now supports basic Java serialization and you can use your Java objects as 'values' for String, Set, and List keys in REDIS. And of course, the JRedis interface has been designed to provide flexibility and 'native' REDIS access, so you can always send whatever byte[] that you want (which is probably more efficient if you already have externalization means for your Java classes). * Requirements REDIS: Obviously you will need a REDIS server to use JREDIS! JREDIS is fully compliant with all documented REDIS beta-0.09 commands, and also supports EXPIRE. JREDIS as of now does NOT support the earlier 0.08 versions but structurally is able to handle a multiplicity of REDIS versions, and you should be able to create your own variant of ProtocolHandler to interact with earlier (obsolete) versions of REDIS. JAVA: JREDIS has no external dependencies at this time. The alpha.0 release has been developed using Java 6 to utilize enhanced reporting afforded by the compiler to implementations of Java interfaces. However, JRE 6 or any of its features or libraries are not a fundamental requirement and this release has the incompatible usage instances commented out to allow for use with JRE 5.x and above. Please note that while this software may or may not work under JDK 1.4.x or earlier, it is simply *not* a goal for this project to support these earlier version of Java, as that would adversely constrain the options in both the expression of JRedis semantics and an effective implementation. TESTS: Test source files and classes clearly require TestNG library , but these test artifacts are not included in the drop-in jar so JUnit is not a requirement for using JRedis. BUILD: JREDIS was built on Eclipse 3.4, on Apple's Mac OS X 10.5 running JVM1.6. The JREDIS core software itself can be built simply using jacav and jar That said, the current release uses structured Maven 2 poms to build the project and execute the tests. * Status This software is a *work in progress* and subject to change. Careful consideration has been given to the concerns and requirements of the end- user of this software to minimize the impact of (certain) changes to the code-base as development continues, to extend functionality, to enhance performance, and to address the feedback of its user community. That said, this is working code that has been tested and stressed, and minimally provides a viable and versatile interface between Java systems and the REDIS database. Coding to the specification artifacts will minimize, but will not eliminate, any impact of the un-going developments. Rough edges are to be expected, and your patience appreciated while this release makes the necessary transition to maturity. And finally, *all the usual caveats regarding software apply: So to repeat from the license terms, JREDIS is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * Performance The performance characteristics of this intial JREDIS No'Rooz implementation are acceptable. In developing a connector for a system such as REDIS, there are a few performance bottle necks which are inevitable. Based on intial educated guesses and a few recent insights gained through the use of a profiling tool, every attempt has been made to optimize the performance of this initial revision. Nevertheless (and thankfully), there remain many areas for further enhancement of the included sychronous connector (JRedis- Client). a) Network IO This release has a known bottleneck that can be addressed, and will be likely after the work on the Asynchronous connection has reached the same level of development as the initial synchronous connection in the initial release. While it is expected that a blocking network client would be io- bound to a significant degree, this is actually not that bad in this release. The wip Asynch connection (still using stream sockets, and not yet NIO), has been clocked at anywhere from 100,000 to 175,000 PING/INCRs on a single non-blocking connection to a localhost REDIS. (These are relative numbers but a reference point is that the REDIS redis-bench program clocks at around 34K/sec for the same). The work on this was stopped to get this release prepared and resumption of work is a high priority as it was way too much fun! b) java.lang.String The worst offender, and an inescapable one at that, is the String class of Java, as far as performance hotspots are concerned. On one hand, it is great to have a Unicode character string implementations that allows JREDIS to use (as tested) Russian, Chinese, and Farsi, keys in storing information in REDIS, nevertheless, the performance hit of conversion of (typical) ASCII strings to byte[] required for the protocol are quite annoying: Given that String is a final class, little short of caching can be done about it. (Any suggestions addressing this is greatly appreciated.) c) data conversion and byte shoveling. The current implementation makes every attempt to minimize the allocation of byte[] buffers, and subsequent copying of bytes from one array to another, but in some cases there is nothing than can be done about it. Also, some attempts have been made to optimize conversion from numbers to byte[] and back, to avoid a visit to the dreaded String.getBytes . The current (hack) solution will cost a few hundred Ks of RAM per classloader but its probably worth it. * Documentation Documentation is a priority item. In the interim please refer to the java api docs. If you are not familiar with the REDIS command set, you are certainly encouraged to read the Command reference and get to know the REDIS vocabulary. The main critical interfaces exposed by JREDIS is the JRedis interface which is a near analog of the redis command set. The overall connector design itself is documented as a ~UML diagram image capture. * Updates Updates about JREDIS specification and reference implementation will be posted at: JREDIS project site: http://code.google.com/p/jredis/ and REDIS Google newsgroup: http://groups.google.com/group/redis-db +spontanous updates will also be posted to twitter under http://twitter.com/SunOf27 * Feedback Your feedback is welcomed and appreciated. Please direct your questions, suggestion, comments, concerns, and complaints to the above public forums, or directly to me @ alphazero@sensesay.net Most specially, information regarding bugs, performance, and usability issues, will be highly appreciated. / Enjoy! ~ Dedicated to my lovely parents, Kiyou and Farideh. ~ Joubin 04-12-09 Fairfax Station, NoVA ================================================ FILE: Release/RELEASE-NOTES-Update-03122010.txt ================================================ Final release for Redis 1.2.n compliant clients. REQUIREMENTS CHANGES: Requires Redis server 1.2.n. This release is NOT compatible with either Redis 1.000 or 2.n (development edge). See: http://code.google.com/p/redis/downloads/list NEW FEATURES: * Heartbeat Connectors will launch a heartbeat thread to maintain connections. Frequency can be set using ConnectionSpec, with default of 1/sec. * Synchronous semantics for Pipeline See JRedisPipeline.sync(). In a nutshell, allows for mixing in synchronous semantics when using the asynchronous pipelines. For example, you may issue a set of asynchronous commands (per JRedisFuture interface) and then use pipeline.sync() to obtain a JRedis (synchronous) interface to issue a blocking command. See UsingJredisPipeline.exampleUseofSyncInPipeline() (in /Examples) BUG FIX: A few bugs were reported and have been addressed. (Thanks to all who provided feedback.) See: http://github.com/alphazero/jredis/issues/closed ROAD MAP: Development focus will be on Redis 2.n compliance in tandem with Redis edge (http://github.com/antirez/redis), and maintenance for (this) 1.2.n compliant clients. /enjoy 03-12-2010 NoVA ================================================ FILE: Release/RELEASE-NOTES-Update-08132009.txt ================================================ This update includes changes to the JRedis interface, as previously announced. API CHANGES: As of this release update, you will need to specify the password and/or db on connect time. You will no longer be able to change the selected db for a connection. The default db selected on connect is db 0. The test suites are all positive on these changes. (Fairly substantial changes in the internal connector packages.) That said, I have not had the time to conduct exhaustive testing beyond the test suites. If you find a bug, please report it under issues in github. If you post to the Redis newsgroup, please indicate JRedis in the subject line as I usually just skim the list's subjects and may miss your feedback. BUG FIX: Previous releases had a bug that reconnected to db 0 on timed out connections. Per the changes in this release, the automatic reconnect will connect to the Redis server using the authorization credentials (if any), and, the db you specified, on the initial connection (using new JRedisClient()). ROAD MAP: Now that we have the db select issue out of the way, the way is clear for connection pooling and thread safe connections. Also, the ConnectionFactory interface of the specification may be finally brought into use to allow for complete user control over the Redis and TCP connection characteristics. (Current implementation relies on a default ConnectionSpec to create its connection.) This will potentially of use to domains that require specialized settings for the buffer sizes, tcp preferences, etc. Pipe-lining is also an upcoming addtion. Frankly its been held back for no better reason other than my laziness and/or other pressing issues occupying my time. A rough implementation has been played with and the performance characteristic increases are in the order of magnitude. (100,000 ops/sec vs 30,000 for the synch clients on my machine). Of course these are throughput measures, and not latency. /enjoy 08-13-2009 NoVA ================================================ FILE: Release/RELEASE-NOTES-Update-11082009.txt ================================================ This is a fairly substantial update release and the final release supporting Redis 1.000. This update includes changes to the JRedis interface, as previously announced. REQUIREMENTS CHANGES: Not so much a change but a strict conformance to Java 5. For whatever reasons (me?) maven apparently had ignored the Java 1.5 directives in the build and I was blissfully unaware that the project was, in fact, using Java 6 constructs (specificially the enhanced semantics of @Override). Well, no more. JRedis is now strictly 1.5. API CHANGES: * Asynchronous semantics A new JRedisFuture interface has been introduced to provide asynchronous call interaction semantics with redis. This is the very first try at this interface and I am not specially happy with the amount of angled bracket typing required to use Future directly. So forewarned that JRedisFuture may change (for the better) and we'll introduce new types to alleviate the angled bracket typing when using this interface. * spec package reorg A few interfaces were shuffled around in course of refactorings and introduction of the asynch semantics. Fairly minor stuff, but it could potentially break some of your import statements. NEW FEATURES: (See the examples project for usage details of new additions). * Pipelining Pipelines supporting both synchronous and asynchronous call semantics are provided. New JRedisPipeline, JRedisPipelineService respectively provide Asynch and Synch access to redis. Both are safe for concurrent use. Note that while this release is Redis 1.000 compliant, there was a rewrite of redis that changed how pipelining was implemented on server side, and the pipelining in this release has only passed unit test against Redis 1.050. * Asynchronous call semantics Besides the pipelines, the new JRedisAsynchClient also supports the JRedisFuture interface and (unlike pipelines) provides true fire and forget behaviour, with minimal latencies in call completion. Please note that unlike the pipelines (which queue pending responses AFTER writing the request to the remote server) the asynch client will queue your request. So you can fire and forget, but you MUST remember that memory is a constrained resource and you can not queue more requests that your JVM can handle . BUG FIX: A couple of bugs were reported since that last update and have been fixed in this release. Reconnect has proven to be a can of worms and given that it is *impossible* for JRedis to guarantee a completely transparent reconnect without potentially resending commands without becoming excessively complex (and incurring computation costs for even those who do not require it) the clients will reconnect but will NOT attempt to transparently resend your command. A ClientRuntimeException will be thrown after reconnect to alert the caller that a reconnect occured at some point during the execution of the request. (Java sockets will NOT raise exceptions on write even in case of a disconnection so it is nearly impossible to guarantee transparent reconnect since it is unknown whether the command write actually went through or not.) ROAD MAP: First up is the addtion of support for the new Redis commands, such as MSet, all the Z*s, etc. After that, we'll likely introduce a extensible mechanism for the user applications to spec a server selection/distribution mechanism, with a basic hashing implementation for sharding. /enjoy 11-08-2009 NoVA ================================================ FILE: Release/RELEASE_BUILD_TIME ================================================ Fri Mar 12 11:19:49 EST 2010 ================================================ FILE: core/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: core/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: core/README ================================================ README : JRedis Module Core ---------------------------------------------------------- BUILD ---------------------------------------------------------- For build instructions refer to the README in root. ---------------------------------------------------------- MODULES ---------------------------------------------------------- The Core module is composed of all the core elements that are used to create JRedis clients and connectors for the Redis server. This includes: - the specification (API), - the reference implementation (RI), - the benchmark module (BENCH) An additional module ALL is a virtual module used to create a unified jar for the core. (Its a hack of manve; likely will be replaced by an assembly directive in the future. If you are a maven guru let me know how to do it better.) * JRedis users If you want to use JRedis clients and connectors, you'll only need the classes of API and RI. Depending on your preferences and requirements, you may either uses the individual jars for each module, OR, simply use the jar 'with dependencies' in RI's target directory. Each module should also produce a source jar. (See build instructions.) No javadocs are produced as of now. (c.f. maven newbiness). **** PLEASE REFER TO UNIT TEST INFO IN ROOT README ***** /end ================================================ FILE: core/all/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: core/all/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: core/all/pom.xml ================================================ 4.0.0 org.jredis jredis-core a.0-SNAPSHOT JRedis - Core - ALL [Jar POM] org.jredis jredis-core-all a.0-SNAPSHOT jar org.jredis jredis-core-api a.0-SNAPSHOT org.jredis jredis-core-ri a.0-SNAPSHOT org.jredis jredis-core-bench a.0-SNAPSHOT maven-assembly-plugin simple-install package attached jar-with-dependencies ================================================ FILE: core/api/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: core/api/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: core/api/pom.xml ================================================ 4.0.0 org.jredis jredis-core a.0-SNAPSHOT JRedis - Core - API org.jredis jredis-core-api a.0-SNAPSHOT jar org.testng testng 7.7.0 test log4j log4j 1.2.12 commons-logging commons-logging 1.1.1 ================================================ FILE: core/api/src/main/java/org/jredis/ClientRuntimeException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * Base class for all non-Redis exceptions relating to client runtime. Implementations * must only throw this type of exception when the problem(s) encountered are neither Redis usage errors, nor * unexpected code segment execution. *

* For example, failure to establish a connection, or losing the connection, should raise this type of exception. But * encountering parse errors in Redis responses streams is a bug and should be noted by raising a {@link ProviderException}. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public class ClientRuntimeException extends RuntimeException { /** */ private static final long serialVersionUID = _specification.Version.major; /** * @param message */ public ClientRuntimeException(String message) { super (message); } /** * @param message * @param cause */ public ClientRuntimeException(String message, Throwable cause) { super(message, cause); } // ------------------------------------------------------------------------ // Super overrides // ------------------------------------------------------------------------ /* (non-Javadoc) * @see java.lang.Throwable#getLocalizedMessage() */ @Override final public String getLocalizedMessage () { return this.getMessage(); } /* (non-Javadoc) * @see java.lang.Throwable#getMessage() */ @Override final public String getMessage () { StringBuffer buff = new StringBuffer(); String message = super.getMessage(); if(null == message) buff.append("[BUG: null message]"); else buff.append(message); Throwable cause = getCause(); if(null != cause) buff.append(" cause: => [").append(cause.getClass().getSimpleName()).append(": ").append(cause.getMessage()).append("]"); return buff.toString(); } } ================================================ FILE: core/api/src/main/java/org/jredis/Codec.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * Defines the necessary methods for a Java type encoder-decoder. Implementations of this * interface can be registered with a {@link CodecManager} used by a {@link JRedis} implementation * (whether per instance or by other relations per JRedis provider implementation), and used * during the encoding of an semantic java type into {@link byte[]} and back. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Apr 14, 2009 * @since alpha.0 * */ public interface Codec { /** * @param bytes * @return an instance of type T corresponding to the value of decoded bytes */ public T decode (byte[] bytes); /** * @param object * @return */ public byte[] encode (T object); /** * @param type * @return whether this codec supports the (en/de)coding of the type T */ public boolean supports (Class type); } ================================================ FILE: core/api/src/main/java/org/jredis/Event.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.WeakReference; /** * Generic Event class for JRedis and sub-modules. * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 29, 2010 * @since alpha.0 * * @param * @param * @param */ public class Event implements Serializable{ /** */ private static final long serialVersionUID = 1L; /** */ private final ETYPE type; /** */ private transient WeakReference srcRef; /** */ private final INFO info; /** * @param src * @param eType * @param info */ protected Event(SRC src, ETYPE eType, INFO info){ this.type = eType; this.srcRef = new WeakReference(src); this.info = info; } /** * @param src * @param eType */ protected Event(SRC src, ETYPE eType){ this(src, eType, null); } /** * @return */ public ETYPE getType () { return type; } /** * @return */ public SRC getSource () { return srcRef.get(); } /** * @return */ public INFO getInfo () { return info; } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(srcRef.get()); } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); srcRef = new WeakReference((SRC)in.readObject()); } } ================================================ FILE: core/api/src/main/java/org/jredis/JRedis.java ================================================ /* * Copyright 2009-2011 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.io.Serializable; import java.util.List; import java.util.Map; /** *

This is effectively a one to one mapping to Redis commands. And that * is basically it. *

Beyond that , just be aware that an implementation may throw {@link ClientRuntimeException} * or an extension to report problems (typically connectivity) or features {@link NotSupportedException} * or bugs. These are {@link RuntimeException}. * * @author joubin (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ @Redis(versions="1.07") public interface JRedis { // ------------------------------------------------------------------------ // Semantic context methods // ------------------------------------------------------------------------ // TODO: reach a decision on whether to include this or not. // /** // * Provides for access to an interface providing standard Java collections // * semantics on the specified parametric type. // *

// * The type T can be of 3 categories: // *

    // *
  1. It is // *
// * @param a Java class type that you wish to perform {@link Set}, // * {@link List}, or {@link Map}, operations. // * @return the {@link JavaSemantics} for type T, if the type specified meets // * the required initialization characteristics. // */ // public JavaSemantics semantic (Class type) throws ClientRuntimeException; // ------------------------------------------------------------------------ // Security and User Management // NOTE: Moved to ConnectionSpec // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // "Connection Handling" // ------------------------------------------------------------------------ /** * Ping redis * @return true (unless not authorized) * @throws RedisException (as of ver. 0.09) in case of unauthorized access */ public JRedis ping () throws RedisException; /** * Disconnects the client. * @Redis QUIT */ public void quit (); // ------------------------------------------------------------------------ // "Commands operating on string values" // ------------------------------------------------------------------------ /** * Bind the value to key. * @Redis SET * @param key any UTF-8 {@link String} * @param value any bytes. For current data size limitations, refer to * Redis documentation. * @throws RedisException on user error. * @throws ProviderException on un-documented features/bug * @throws ClientRuntimeException on errors due to operating environment (Redis or network) */ public void set (K key, byte[] value) throws RedisException; /** * Convenient method for {@link String} data binding * @Redis SET * @param key * @param stringValue * @throws RedisException * @see {@link JRedis#set(String, byte[])} */ public void set (K key, String stringValue) throws RedisException; /** * Convenient method for {@link String} numeric values binding * @Redis SET * @param key * @param numberValue * @throws RedisException * @see {@link JRedis#set(String, byte[])} */ public void set (K key, Number numberValue) throws RedisException; /** * Binds the given java {@link Object} to the key. Serialization format is * implementation specific. Simple implementations may apply the basic {@link Serializable} * protocol. * @Redis SET * @param * @param key * @param object * @throws RedisException * @see {@link JRedis#set(String, byte[])} */ public void set (K key, T object) throws RedisException; /** * @Redis SETNX * @param key * @param value * @return * @throws RedisException */ public boolean setnx (K key, byte[] value) throws RedisException; public boolean setnx (K key, String stringValue) throws RedisException; public boolean setnx (K key, Number numberValue) throws RedisException; public boolean setnx (K key, T object) throws RedisException; /** * @Redis GET * @param key * @return * @throws RedisException */ public byte[] get (K key) throws RedisException; public byte[] getset (K key, byte[] value) throws RedisException; public byte[] getset (K key, String stringValue) throws RedisException; public byte[] getset (K key, Number numberValue) throws RedisException; public byte[] getset (K key, T object) throws RedisException; /** * @Redis MGET * @param key * @param moreKeys * @return * @throws RedisException */ public List mget(K...keys) throws RedisException; /** * @Redis MSET * @param keyValueMap a {@link Map}ping of {@link String} key names to byte[] values. * @return * @throws RedisException */ public void mset(Map keyValueMap) throws RedisException; public void mset(KeyValueSet.ByteArrays mappings) throws RedisException; public void mset(KeyValueSet.Strings mappings) throws RedisException; public void mset(KeyValueSet.Numbers mappings) throws RedisException; public void mset(KeyValueSet.Objects mappings) throws RedisException; /** * @Redis MSETNX * @param keyValueMap a {@link Map}ping of {@link String} key names to byte[] values. * @return false if ANY of the keys in the map already existed, true if all were new and were set. * @throws RedisException */ public boolean msetnx(Map keyValueMap) throws RedisException; public boolean msetnx(KeyValueSet.ByteArrays mappings) throws RedisException; public boolean msetnx(KeyValueSet.Strings mappings) throws RedisException; public boolean msetnx(KeyValueSet.Numbers mappings) throws RedisException; public boolean msetnx(KeyValueSet.Objects mappings) throws RedisException; /** * @Redis INCR * @param key * @return * @throws RedisException */ public long incr (K key) throws RedisException; /** * @Redis INCRBY * @param key * @param delta * @return * @throws RedisException */ public long incrby (K key, int delta) throws RedisException; /** * @Redis DECR * @param key * @return * @throws RedisException */ public long decr (K key) throws RedisException; /** * @Redis DECRBY * @param key * @param delta * @return * @throws RedisException */ public long decrby (K key, int delta) throws RedisException; /** * @Redis SUBSTR * @param key * @param from * @param to * @return * @throws RedisException */ public byte[] substr (K key, long from, long to) throws RedisException; /** * @Redis APPEND * @param key * @param value * @return length (byte count) of appended value * @throws RedisException */ public long append (K key, byte[] value) throws RedisException; public long append (K key, String stringValue) throws RedisException; public long append (K key, Number numberValue) throws RedisException; public long append (K key, T object) throws RedisException; // ------------------------------------------------------------------------ // "Commands operating on the key space" // ------------------------------------------------------------------------ /** * @Redis EXISTS * @param key * @return * @throws RedisException */ public boolean exists(K key) throws RedisException; /** * @Redis DEL * @param keys one or more non-null, non-zero-length, keys to be deleted * @return number of keys actually deleted * @throws RedisException */ // public boolean del (K key) throws RedisException; public long del (K ... keys) throws RedisException; /** * @Redis TYPE * @param key * @return * @throws RedisException */ public RedisType type (K key) throws RedisException; /** * @Redis KEYS * @param pattern * @return * @throws RedisException */ public List keys (K pattern) throws RedisException; /** * Convenience method. Equivalent to calling jredis.keys("*"); * @Redis KEYS * @return * @throws RedisException * @see {@link JRedis#keys(String)} */ public List keys () throws RedisException; /** * @Redis RANDOMKEY * @return * @throws RedisException */ public byte[] randomkey() throws RedisException; /** * @Redis RENAME * @param oldkey * @param newkey * @throws RedisException */ public void rename (K oldkey, K newkey) throws RedisException; /** * @Redis RENAMENX * @param oldkey * @param brandnewkey * @return * @throws RedisException */ public boolean renamenx (K oldkey, K brandnewkey) throws RedisException; /** * @Redis DBSIZE * @return * @throws RedisException */ public long dbsize () throws RedisException; /** * @Redis EXPIRE * @param key * @param ttlseconds * @return * @throws RedisException */ public boolean expire (K key, int ttlseconds) throws RedisException; /** * * @Redis EXPIREAT * @param key * @param UNIX epoch-time in milliseconds. Note that Redis expects epochtime * in seconds. Implementations are responsible for converting to seconds. * method * @return * @throws RedisException * @see {@link System#currentTimeMillis()} */ public boolean expireat (K key, long epochtimeMillisecs) throws RedisException; /** * @Redis TTL * @param key * @return * @throws RedisException */ public long ttl (K key) throws RedisException; // ------------------------------------------------------------------------ // Commands operating on lists // ------------------------------------------------------------------------ /** * @Redis RPUSH * @param listkey * @param value * @throws RedisException */ public void rpush (K listkey, byte[] value) throws RedisException; public void rpush (K listkey, String stringValue) throws RedisException; public void rpush (K listkey, Number numberValue) throws RedisException; public void rpush (K listkey, T object) throws RedisException; /** * @Redis LPUSH * @param listkey * @param value * @throws RedisException */ public void lpush (K listkey, byte[] value) throws RedisException; public void lpush (K listkey, String stringValue) throws RedisException; public void lpush (K listkey, Number numberValue) throws RedisException; public void lpush (K listkey, T object) throws RedisException; /** * @Redis LSET * @param key * @param index * @param value * @throws RedisException */ public void lset (K key, long index, byte[] value) throws RedisException; public void lset (K key, long index, String stringValue) throws RedisException; public void lset (K key, long index, Number numberValue) throws RedisException; public void lset (K key, long index, T object) throws RedisException; /** * @Redis LREM * @param listKey * @param value * @param count * @return * @throws RedisException */ public long lrem (K listkey, byte[] value, int count) throws RedisException; public long lrem (K listkey, String stringValue, int count) throws RedisException; public long lrem (K listkey, Number numberValue, int count) throws RedisException; public long lrem (K listkey, T object, int count) throws RedisException; /** * Given a 'list' key, returns the number of items in the list. * @Redis LLEN * @param listkey * @return * @throws RedisException */ public long llen (K listkey) throws RedisException; /** * @Redis LRANGE * @param listkey * @param from * @param to * @return * @throws RedisException */ public List lrange (K listkey, long from, long to) throws RedisException; /** * @Redis LTRIM * @param listkey * @param keepFrom * @param keepTo * @throws RedisException */ public void ltrim (K listkey, long keepFrom, long keepTo) throws RedisException; /** * @Redis LINDEX * @param listkey * @param index * @return * @throws RedisException */ public byte[] lindex (K listkey, long index) throws RedisException; /** * @Redis LPOP * @param listKey * @return * @throws RedisException */ public byte[] lpop (K listkey) throws RedisException; /** * @Redis RPOP * @param listKey * @return * @throws RedisException */ public byte[] rpop (K listkey) throws RedisException; /** * @Redis RPOPLPUSH * @param srcList * @param destList * @return * @throws RedisException */ public byte[] rpoplpush (K srcList, K destList) throws RedisException; // ------------------------------------------------------------------------ // Commands operating on sets // ------------------------------------------------------------------------ /** * @Redis SADD * @param setkey * @param member * @return * @throws RedisException */ public boolean sadd (K setkey, byte[] member) throws RedisException; public boolean sadd (K setkey, String stringValue) throws RedisException; public boolean sadd (K setkey, Number numberValue) throws RedisException; public boolean sadd (K setkey, T object) throws RedisException; /** * @Redis SREM * @param setKey * @param member * @return * @throws RedisException */ public boolean srem (K setkey, byte[] member) throws RedisException; public boolean srem (K setkey, String stringValue) throws RedisException; public boolean srem (K setkey, Number numberValue) throws RedisException; public boolean srem (K setkey, T object) throws RedisException; /** * @Redis SISMEMBER * @param setKey * @param member * @return * @throws RedisException */ public boolean sismember (K setkey, byte[] member) throws RedisException; public boolean sismember (K setkey, String stringValue) throws RedisException; public boolean sismember (K setkey, Number numberValue) throws RedisException; public boolean sismember (K setkey, T object) throws RedisException; /** * @Redis SMOVE * @param srcKey * @param destKey * @param member * @return * @throws RedisException */ public boolean smove (K srcKey, K destKey, byte[] member) throws RedisException; public boolean smove (K srcKey, K destKey, String stringValue) throws RedisException; public boolean smove (K srcKey, K destKey, Number numberValue) throws RedisException; public boolean smove (K srcKey, K destKey, T object) throws RedisException; /** * @Redis SCARD * @param setKey * @return * @throws RedisException */ public long scard (K setKey) throws RedisException; /** * @Redis SINTER * @param set1 * @param sets * @return * @throws RedisException */ public List sinter (K set1, K...sets) throws RedisException; /** * @Redis SINTERSTORE * @param destSetKey * @param sets * @throws RedisException */ public void sinterstore (K destSetKey, K...sets) throws RedisException; /** * @Redis SUNION * @param set1 * @param sets * @return * @throws RedisException */ public List sunion (K set1, K...sets) throws RedisException; /** * @Redis SUNIONSTORE * @param destSetKey * @param sets * @throws RedisException */ public void sunionstore (K destSetKey, K...sets) throws RedisException; /** * @Redis SDIFF * @param set1 * @param sets * @return * @throws RedisException */ public List sdiff (K set1, K...sets) throws RedisException; /** * @Redis SDIFFSTORE * @param destSetKey * @param sets * @throws RedisException */ public void sdiffstore (K destSetKey, K...sets) throws RedisException; /** * @Redis SMEMBERS * @param setkey * @return * @throws RedisException */ public List smembers (K setKey) throws RedisException; /** * @Redis SRANDMEMBER * @param setkey * @return * @throws RedisException */ public byte[] srandmember (K setKey) throws RedisException; /** * @Redis SPOP * @param setkey * @return * @throws RedisException */ public byte[] spop (K setKey) throws RedisException; // ------------------------------------------------------------------------ // Commands operating on sets // ------------------------------------------------------------------------ /** * @Redis ZADD * @param setkey * @param score * @param member * @return * @throws RedisException */ public boolean zadd (K setkey, double score, byte[] member) throws RedisException; public boolean zadd (K setkey, double score, String stringValue) throws RedisException; public boolean zadd (K setkey, double score, Number numberValue) throws RedisException; public boolean zadd (K setkey, double score, T object) throws RedisException; /** * @Redis ZREM * @param setKey * @param member * @return * @throws RedisException */ public boolean zrem (K setkey, byte[] member) throws RedisException; public boolean zrem (K setkey, String stringValue) throws RedisException; public boolean zrem (K setkey, Number numberValue) throws RedisException; public boolean zrem (K setkey, T object) throws RedisException; /** * @Redis ZCARD * @param setKey * @return * @throws RedisException */ public long zcard (K setKey) throws RedisException; /** * @Redis ZSCORE * @param setkey * @param member * @return * @throws RedisException */ public Double zscore (K setkey, byte[] member) throws RedisException; public Double zscore (K setkey, String stringValue) throws RedisException; public Double zscore (K setkey, Number numberValue) throws RedisException; public Double zscore (K setkey, T object) throws RedisException; /** * @Redis ZRANK * @param setkey * @param member * @return * @throws RedisException */ public long zrank (K setkey, byte[] member) throws RedisException; public long zrank (K setkey, String stringValue) throws RedisException; public long zrank (K setkey, Number numberValue) throws RedisException; public long zrank (K setkey, T object) throws RedisException; /** * @Redis ZREVRANK * @param setkey * @param member * @return * @throws RedisException */ public long zrevrank (K setkey, byte[] member) throws RedisException; public long zrevrank (K setkey, String stringValue) throws RedisException; public long zrevrank (K setkey, Number numberValue) throws RedisException; public long zrevrank (K setkey, T object) throws RedisException; /** * @Redis ZRANGE * @param setkey * @param from * @param to * @return * @throws RedisException */ public List zrange (K setkey, long from, long to) throws RedisException; /** * @Redis ZREVRANGE * @param setkey * @param from * @param to * @return * @throws RedisException */ public List zrevrange (K setkey, long from, long to) throws RedisException; /** * Equivalent to {@link JRedis#zrange(String, long, long)} with the {@link Option.Options#WITHSCORES}. * Unlike the general ZRANGE command that only returns the values, this method returns both * values and associated scores for the specified range. * * @Redis ZRANGE ... WITHSCORES * @param setkey * @param from * @param to * @return the subset of the specified set * @throws RedisException * @see JRedis#zrange(String, long, long) * @see ZSetEntry */ public List zrangeSubset (K setkey, long from, long to) throws RedisException; /** * Equivalent to {@link JRedis#zrevrange(String, long, long)} with the {@link Option.Options#WITHSCORES}. * Unlike the general ZREVRANGE command that only returns the values, this method returns both * values and associated scores for the specified range. * * @Redis ZREVRANGE ... WITHSCORES * @param setkey * @param from * @param to * @return the subset of the specified set * @throws RedisException * @see JRedis#zrevrange(String, long, long) * @see ZSetEntry */ public List zrevrangeSubset (K setkey, long from, long to) throws RedisException; /** * @Redis ZRANGE * @param setkey * @param minScore * @param maxScore * @return * @throws RedisException */ public List zrangebyscore (K setkey, double minScore, double maxScore) throws RedisException; /** * @Redis ZRANGEBYSCORE ... WITHSCORES * @param setkey * @param minScore * @param maxScore * @return * @throws RedisException */ public List zrangebyscoreSubset (K setkey, double minScore, double maxScore) throws RedisException; /** * @Redis ZREMRANGEBYSCORE * @param setkey * @param minScore * @param maxScore * @return number of removed elements * @throws RedisException */ public long zremrangebyscore (K setkey, double minScore, double maxScore) throws RedisException; /** * @Redis ZREMRANGEBYRANK * @param setkey * @param minRank * @param maxRank * @return number of removed elements * @throws RedisException */ public long zremrangebyrank (K setkey, long minRank, long maxRank) throws RedisException; /** * @Redis ZINCRBY * @param setkey * @param score * @param member * @return * @throws RedisException */ @Redis(versions="1.07") public Double zincrby (K setkey, double score, byte[] member) throws RedisException; public Double zincrby (K setkey, double score, String stringValue) throws RedisException; public Double zincrby (K setkey, double score, Number numberValue) throws RedisException; public Double zincrby (K setkey, double score, T object) throws RedisException; /** * @Redis ZCOUNT * @param setkey * @param minScore * @param maxScore * @return count of set members with score in the given range. * @throws RedisException */ public long zcount (K setkey, double minScore, double maxScore) throws RedisException; // ------------------------------------------------------------------------ // Commands operating on hashes // ------------------------------------------------------------------------ /** * @Redis HSET * @param key * @param field * @param value * @return */ @Redis(versions="1.3.n") public boolean hset(K key, K field, byte[] value) throws RedisException; /** * @Redis HSET * @param key * @param field * @param string * @return */ @Redis(versions="1.3.n") public boolean hset(K key, K field, String string) throws RedisException; /** * @Redis HSET * @param key * @param field * @param number * @return */ @Redis(versions="1.3.n") public boolean hset(K key, K field, Number number) throws RedisException; /** * @Redis HSET * @param * @param key * @param field * @param object * @return */ @Redis(versions="1.3.4") public boolean hset(K key, K field, T object) throws RedisException; /** * @Redis HGET * @param key * @param field * @return */ @Redis(versions="1.3.4") public byte[] hget(K key, K field) throws RedisException; /** * * @Redis HEXISTS * @param key * @param field * @return true if the spec'd field exists for the spec'd (hash type) key * @throws RedisException */ @Redis(versions="1.3.n") public boolean hexists(K key, K field) throws RedisException; /** * * @Redis HDEL * @param key * @param field * @return true if the spec'd field exists for the spec'd (hash type) key * @throws RedisException */ @Redis(versions="1.3.n") public boolean hdel(K key, K field) throws RedisException; /** * * @Redis HLEN * @param key * @return # of fields/entries in the given hashtable. * @throws RedisException */ @Redis(versions="1.3.n") public long hlen(K key) throws RedisException; /** * * @Redis HKEYS * @param key * @return list of keys in the given hashtable. * @throws RedisException */ @Redis(versions="1.3.n") public List hkeys(K key) throws RedisException; /** * * @Redis HVALS * @param key * @return list of values in the given hashtable. * @throws RedisException */ @Redis(versions="1.3.n") public List hvals(K key) throws RedisException; /** * * @Redis HGETALL * @param key * @return the given hash as a Map * @throws RedisException */ @Redis(versions="1.3.n") public Map hgetall(K key) throws RedisException; // ------------------------------------------------------------------------ // Transactional commands // ------------------------------------------------------------------------ /** * one option is to return a subclass of JRedis (e.g. JRedisCommandSequence) * and have that interface declare discard and multi. Benefit is being able * to associate state with the transaction. * @throws RedisException */ @Redis(versions="2.0") public JRedis multi() throws RedisException; /** * @throws RedisException */ @Redis(versions="2.0") public JRedis discard () throws RedisException; // ------------------------------------------------------------------------ // Multiple databases handling commands // ------------------------------------------------------------------------ // @Deprecated // public JRedis select (int index) throws RedisException; /** * Flushes the db you selected when connecting to Redis server. Typically, * implementations will select db 0 on connecting if non was specified. Remember * that there is no roll-back. * @Redis FLUSHDB * @return * @throws RedisException */ public JRedis flushdb () throws RedisException; /** * Flushes all dbs in the connect Redis server, regardless of which db was selected * on connect time. Remember that there is no rollback. * @Redis FLUSHALL * @return * @throws RedisException */ public JRedis flushall () throws RedisException; /** * Moves the given key from the currently selected db to the one indicated * by dbIndex. * @Redis MOVE * @param key * @param dbIndex * @return * @throws RedisException */ public boolean move (K key, int dbIndex) throws RedisException; // ------------------------------------------------------------------------ // Sorting // ------------------------------------------------------------------------ /** * Usage: *

Usage: *

	 * List  results = redis.sort("my-list-or-set-key").BY("weight*").LIMIT(1, 11).GET("object*").DESC().ALPHA().exec();
	 * for(byte[] item : results) {
	 *     // do something with item ..
	 *  }
	 * 
*

Sort specification elements are all options. You could simply say: *

	 * List  results = redis.sort("my-list-or-set-key").exec();
	 * for(byte[] item : results) {
	 *     // do something with item ..
	 *  }
	 * 
*

Sort specification elements are also can appear in any order -- the client implementation will send them to the server * in the order expected by the protocol, although it is good form to specify the predicates in natural order: *

	 * List  results = redis.sort("my-list-or-set-key").GET("object*").DESC().ALPHA().BY("weight*").LIMIT(1, 11).exec();
	 * for(byte[] item : results) {
	 *     // do something with item ..
	 *  }
	 * 
* * @Redis SORT */ public Sort sort(K key); // ------------------------------------------------------------------------ // Persistence control commands // ------------------------------------------------------------------------ /** * @Redis SAVE * @throws RedisException */ public void save() throws RedisException; /** * @Redis BGSAVE * @throws RedisException */ public void bgsave () throws RedisException; /** * @Redis BGREWRITEAOF * @return ack message. * @throws RedisException */ public String bgrewriteaof () throws RedisException; /** * @Redis LASTSAVE * @return * @throws RedisException */ public long lastsave () throws RedisException; // @Deprecated // public void shutdown () throws RedisException; // ------------------------------------------------------------------------ // Remote server control commands // ------------------------------------------------------------------------ /** * @Redis INFO * @return * @throws RedisException */ public Map info () throws RedisException; /** * @Redis SLAVEOF * @param host ip address * @param port */ public void slaveof(String host, int port) throws RedisException; /** * Convenience method. Turns off replication. * @Redis SLAVEOF "no one" */ public void slaveofnone() throws RedisException; // ------------------------------------------------------------------------ // Diagnostics commands // ------------------------------------------------------------------------ /** * @Redis ECHO * @param msg * @return * @throws RedisException */ public byte[] echo (byte[] msg) throws RedisException; public byte[] echo (String msg) throws RedisException; public byte[] echo (Number msg) throws RedisException; public byte[] echo (T msg) throws RedisException; /** * @Redis DEBUG OBJECT * @param key * @return * @throws RedisException * @see {@link ObjectInfo} */ public ObjectInfo debug (K key) throws RedisException; public boolean setbit(K key, int offset, boolean value) throws RedisException; public boolean getbit(K key, int offset) throws RedisException; } ================================================ FILE: core/api/src/main/java/org/jredis/JRedisFuture.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.protocol.ResponseStatus; /** * The asynchronous interface to Redis. *

* This is effectively a one to one mapping to Redis commands. Depending on the implementation * either the redis response and/or redis write are asynchronous. Regardless, each method returns * an extension of {@link Future} and the returned results conforms to the contract of that interface (which * you should review). *

* If your request results in a {@link RedisException}, the call to {@link Future#get()} (of either flavor) * will raise a {@link ExecutionException} with {@link ExecutionException#getCause()} returning the underlying * {@link RedisException}. *

* Similarly, if the request results in either {@link ClientRuntimeException} or {@link ProviderException}, the * {@link Future}'s {@link ExecutionException} will wrap these as the cause. *

* Beyond that , just be aware that an implementation may throw {@link ClientRuntimeException} * or an extension to report problems (typically connectivity) or {@link ProviderException} * (to highlight implementation features/bugs). * These are {@link RuntimeException} that have been encountered while trying to queue your request. *

* Note that this interface provides no guarantees whatsoever regarding the execution of your requests beyond * the strict ordering of the requests per your invocations. Specifically, in the event of connection issues, this * interface's contract does not place any requirements on the implementation beyond to notify the user of such issues * either during a call to this interface, or, on the attempt to get the result of a pending response on {@link Future#get()}. * Refer to the documentation of the implementation of {@link JRedisFuture} for the specifics of behavior in context of * errors. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ @Redis(versions="1.07") public interface JRedisFuture { // ------------------------------------------------------------------------ // "Connection Handling" // ------------------------------------------------------------------------ /** * Ping redis */ public Future ping (); /** * Disconnects the client. * @Redis QUIT */ public Future quit (); /** * Optional connection control command. * @param * @return */ public Future flush (); // ------------------------------------------------------------------------ // "Commands operating on string values" // ------------------------------------------------------------------------ /** * Bind the value to key. * @Redis SET * @param key any UTF-8 {@link String} * @param value any bytes. For current data size limitations, refer to * Redis documentation. * @throws ProviderException on un-documented features/bug * @throws ClientRuntimeException on errors due to operating environment (Redis or network) */ public Future set (K key, byte[] value); /** * Convenient method for {@link String} data binding * @Redis SET * @param key * @param stringValue * @see {@link JRedis#set(String, byte[])} */ public Future set (K key, String stringValue); /** * Convenient method for {@link String} numeric values binding * @Redis SET * @param key * @param numberValue * @see {@link JRedis#set(String, byte[])} */ public Future set (K key, Number numberValue); /** * Binds the given java {@link Object} to the key. Serialization format is * implementation specific. Simple implementations may apply the basic {@link Serializable} * protocol. * @Redis SET * @param * @param key * @param object * @see {@link JRedis#set(String, byte[])} */ public Future set (K key, T object); /** * @Redis SETNX * @param key * @param value * @return */ public Future setnx (K key, byte[] value); public Future setnx (K key, String stringValue); public Future setnx (K key, Number numberValue); public Future setnx (K key, T object); public Future setbit(K key, int offset, boolean value); /** * @Redis GET * @param key * @return */ public Future get (K key) ; public Future getset (K key, byte[] value); public Future getset (K key, String stringValue); public Future getset (K key, Number numberValue); public Future getset (K key, T object); public Future getbit(K key, int offset); /** * @Redis MGET * @param key * @param moreKeys * @return */ public Future> mget(String ... keys); /** * @Redis MSET * @param keyValueMap a {@link Map}ping of {@link String} key names to byte[] values. * @return Future indicating if all of sets were OK or not * @throws RedisException */ public Future mset(Map keyValueMap); public Future mset(KeyValueSet.ByteArrays mappings); public Future mset(KeyValueSet.Strings mappings); public Future mset(KeyValueSet.Numbers mappings); public Future mset(KeyValueSet.Objects mappings); /** * @Redis MSETNX * @param keyValueMap a {@link Map}ping of {@link String} key names to byte[] values. * @return Future indicating if all of sets were OK or not * @throws RedisException */ public Future msetnx(Map keyValueMap); public Future msetnx(KeyValueSet.ByteArrays mappings); public Future msetnx(KeyValueSet.Strings mappings); public Future msetnx(KeyValueSet.Numbers mappings); public Future msetnx(KeyValueSet.Objects mappings); /** * @Redis INCR * @param key * @return */ public Future incr (K key); /** * @Redis INCRBY * @param key * @param delta * @return */ public Future incrby (K key, int delta); /** * @Redis DECR * @param key * @return */ public Future decr (K key); /** * @Redis DECRBY * @param key * @param delta * @return */ public Future decrby (K key, int delta); /** * @Redis SUBSTR * @param listkey * @param from * @param to * @return */ public Future substr (K listkey, long from, long to); /** * @Redis APPEND * @param key * @param value * @return the length (byte count) of appended key. */ public Future append (K key, byte[] value); public Future append (K key, String stringValue); public Future append (K key, Number numberValue); public Future append (K key, T object); /** * @Redis EXISTS * @param key * @return */ public Future exists(K key); /** * @Redis DEL * @param keys one or more, non-null, non-zero-length, keys to be deleted * @return Future of number keys actually deleted. */ public Future del (K ... keys); /** * @Redis TYPE * @param key * @return */ public Future type (K key); // ------------------------------------------------------------------------ // "Commands operating on the key space" // ------------------------------------------------------------------------ /** * @Redis KEYS * @param pattern * @return */ public Future> keys (K pattern); /** * Convenience method. Equivalent to calling jredis.keys("*"); * @Redis KEYS * @return * @see {@link JRedis#keys(String)} */ public Future> keys (); /** * @Redis RANDOMKEY * @return */ public Future randomkey(); /** * @Redis RENAME * @param oldkey * @param newkey */ public Future rename (K oldkey, K newkey); /** * @Redis RENAMENX * @param oldkey * @param brandnewkey * @return */ public Future renamenx (K oldkey, K brandnewkey); /** * @Redis DBSIZE * @return */ public Future dbsize (); /** * @Redis EXPIRE * @param key * @param ttlseconds * @return */ public Future expire (K key, int ttlseconds); /** * @Redis EXPIREAT * @param key * @param UNIX epoch-time in milliseconds. Note that Redis expects epochtime * in seconds. Implementations are responsible for converting to seconds. * method * @return * @see {@link System#currentTimeMillis()} */ public Future expireat (K key, long epochtimeMillisecs); /** * @Redis TTL * @param key * @return */ public Future ttl (K key); // ------------------------------------------------------------------------ // Commands operating on lists // ------------------------------------------------------------------------ /** * @Redis RPUSH * @param listkey * @param value */ public Future rpush (K listkey, byte[] value); public Future rpush (K listkey, String stringValue); public Future rpush (K listkey, Number numberValue); public Future rpush (K listkey, T object); /** * @Redis LPUSH * @param listkey * @param value */ public Future lpush (K listkey, byte[] value); public Future lpush (K listkey, String stringValue); public Future lpush (K listkey, Number numberValue); public Future lpush (K listkey, T object); /** * @Redis LSET * @param key * @param index * @param value */ public Future lset (K key, long index, byte[] value); public Future lset (K key, long index, String stringValue); public Future lset (K key, long index, Number numberValue); public Future lset (K key, long index, T object); /** * @Redis LREM * @param listKey * @param value * @param count * @return */ public Future lrem (K listkey, byte[] value, int count); public Future lrem (K listkey, String stringValue, int count); public Future lrem (K listkey, Number numberValue, int count); public Future lrem (K listkey, T object, int count); /** * Given a 'list' key, returns the number of items in the list. * @Redis LLEN * @param listkey * @return */ public Future llen (K listkey); /** * @Redis LRANGE * @param listkey * @param from * @param to * @return */ public Future> lrange (K listkey, long from, long to); /** * @Redis LTRIM * @param listkey * @param keepFrom * @param keepTo */ public Future ltrim (K listkey, long keepFrom, long keepTo); /** * @Redis LINDEX * @param listkey * @param index * @return */ public Future lindex (K listkey, long index); /** * @Redis LPOP * @param listKey * @return */ public Future lpop (K listkey); /** * @Redis RPOP * @param listKey * @return */ public Future rpop (K listkey); /** * @Redis RPOPLPUSH * @param srcList * @param destList * @return */ public Future rpoplpush (String srcList, String destList); // ------------------------------------------------------------------------ // Commands operating on sets // ------------------------------------------------------------------------ /** * @Redis SADD * @param setkey * @param member * @return */ public Future sadd (K setkey, byte[] member); public Future sadd (K setkey, String stringValue); public Future sadd (K setkey, Number numberValue); public Future sadd (K setkey, T object); /** * @Redis SREM * @param setKey * @param member * @return */ public Future srem (K setkey, byte[] member); public Future srem (K setkey, String stringValue); public Future srem (K setkey, Number numberValue); public Future srem (K setkey, T object); /** * @Redis SISMEMBER * @param setKey * @param member * @return */ public Future sismember (K setkey, byte[] member); public Future sismember (K setkey, String stringValue); public Future sismember (K setkey, Number numberValue); public Future sismember (K setkey, T object); /** * @Redis SMOVE * @param srcKey * @param destKey * @param member * @return */ public Future smove (K srcKey, K destKey, byte[] member); public Future smove (K srcKey, K destKey, String stringValue); public Future smove (K srcKey, K destKey, Number numberValue); public Future smove (K srcKey, K destKey, T object); /** * @Redis SCARD * @param setKey * @return */ public Future scard (K setKey); /** * @Redis SINTER * @param set1 * @param sets * @return */ public Future> sinter (K set1, K...sets); /** * @Redis SINTERSTORE * @param destSetKey * @param sets */ public Future sinterstore (K destSetKey, K...sets); /** * @Redis SUNION * @param set1 * @param sets * @return */ public Future> sunion (K set1, K...sets); /** * @Redis SUNIONSTORE * @param destSetKey * @param sets */ public Future sunionstore (K destSetKey, K...sets); /** * @Redis SDIFF * @param set1 * @param sets * @return */ public Future> sdiff (K set1, K...sets); /** * @Redis SDIFFSTORE * @param destSetKey * @param sets */ public Future sdiffstore (K destSetKey, K...sets); /** * @Redis SMEMBERS * @param setkey * @return */ public Future> smembers (K setkey); /** * @Redis SRANDMEMBER * @param setkey * @return */ public Future srandmember (K setkey); /** * @Redis SPOP * @param setkey * @return */ public Future spop (K setkey); // ------------------------------------------------------------------------ // Commands operating on sorted sets // ------------------------------------------------------------------------ /** * @Redis ZADD * @param setkey * @param score * @param member * @return */ public Future zadd (K setkey, double score, byte[] member); public Future zadd (K setkey, double score, String stringValue); public Future zadd (K setkey, double score, Number numberValue); public Future zadd (K setkey, double score, T object); /** * @Redis ZREM * @param setKey * @param member * @return */ public Future zrem (K setkey, byte[] member); public Future zrem (K setkey, String stringValue); public Future zrem (K setkey, Number numberValue); public Future zrem (K setkey, T object); /** * @Redis ZCARD * @param setKey * @return */ public Future zcard (K setKey); /** * @Redis ZSCORE * @param setkey * @param member * @return */ public Future zscore (K setkey, byte[] member); public Future zscore (K setkey, String stringValue); public Future zscore (K setkey, Number numberValue); public Future zscore (K setkey, T object); /** * @Redis ZRANK * @param setkey * @param member * @return */ public Future zrank (K setkey, byte[] member); public Future zrank (K setkey, String stringValue); public Future zrank (K setkey, Number numberValue); public Future zrank (K setkey, T object); /** * @Redis ZREVRANK * @param setkey * @param member * @return */ public Future zrevrank (K setkey, byte[] member); public Future zrevrank (K setkey, String stringValue); public Future zrevrank (K setkey, Number numberValue); public Future zrevrank (K setkey, T object); /** * @Redis ZRANGE * @param setkey * @param from * @param to * @return */ public Future> zrange (K setkey, long from, long to); /** * @Redis ZREVRANGE * @param setkey * @param from * @param to * @return */ public Future> zrevrange (K setkey, long from, long to); /** * Equivalent to {@link JRedis#zrange(String, long, long)} with the {@link Command.Option#WITHSCORES}. * Unlike the general ZRANGE command that only returns the values, this method returns both * values and associated scores for the specified range. * * @Redis ZRANGE ... WITHSCORES * @param setkey * @param from * @param to * @return * @see JRedis#zrange(String, long, long) * @see ZSetEntry */ public Future> zrangeSubset (K setkey, long from, long to); /** * Equivalent to {@link JRedis#zrange(String, long, long)} with the {@link Command.Option#WITHSCORES}. * Unlike the general ZRANGE command that only returns the values, this method returns both * values and associated scores for the specified range. * * @Redis ZREVRANGE ... WITHSCORES * @param setkey * @param from * @param to * @return * @see JRedis#zrevrange(String, long, long) * @see ZSetEntry */ public Future> zrevrangeSubset (K setkey, long from, long to); /** * @Redis ZINCRBY * @param setkey * @param score * @param member * @return */ @Redis(versions="1.07") public Future zincrby (K setkey, double score, byte[] member); public Future zincrby (K setkey, double score, String stringValue); public Future zincrby (K setkey, double score, Number numberValue); public Future zincrby (K setkey, double score, T object); /** * @Redis ZRANGEBYSCORE * @param setkey * @param from * @param to * @return */ public Future> zrangebyscore (K setkey, double minScore, double maxScore); /** * @Redis ZRANGEBYSCORE ... WITHSCORES * @param setkey * @param minScore * @param maxScore * @return */ public Future> zrangebyscoreSubset (K setkey, double minScore, double maxScore); /** * @Redis ZREMRANGEBYSCORE * @param setkey * @param from * @param to * @return number of removed elements */ public Future zremrangebyscore (K setkey, double minScore, double maxScore); /** * @Redis ZCOUNT * @param setkey * @param minScore * @param maxScore * @return number of removed elements */ public Future zcount (K setkey, double minScore, double maxScore); /** * @Redis ZREMRANGEBYRANK * @param setkey * @param from * @param to * @return number of removed elements */ public Future zremrangebyrank (K setkey, long minRank, long maxRank); // ------------------------------------------------------------------------ // Commands operating on hashes // ------------------------------------------------------------------------ /** * @Redis HSET * @param key * @param field * @param value * @return */ @Redis(versions="1.3.n") public Future hset(K key, K entry, byte[] value); /** * @Redis HSET * @param key * @param field * @param string * @return */ @Redis(versions="1.3.n") public Future hset(K key, K entry, String string); /** * @Redis HSET * @param key * @param field * @param number * @return */ @Redis(versions="1.3.n") public Future hset(K key, K entry, Number number); /** * @Redis HSET * @param * @param key * @param field * @param object * @return */ @Redis(versions="1.3.4") public Future hset(K key, K entry, T object); /** * @Redis HGET * @param key * @param field * @return */ @Redis(versions="1.3.4") public Future hget(K key, K entry); /** * @Redis HINCRBY * @param key * @param entry * @param increment * @return */ public Future hincrby(K key, K entry, long increment); /** * * @Redis HEXISTS * @param key * @param field * @return true if the spec'd field exists for the spec'd (hash type) key */ @Redis(versions="1.3.5") public Future hexists(K key, K entry); /** * * @Redis HDEL * @param key * @param field * @return true if the spec'd field exists for the spec'd (hash type) key */ @Redis(versions="1.3.5") public Future hdel(K key, K entry); /** * * @Redis HLEN * @param key * @param field * @return # of fields/entries for the given hash type key */ @Redis(versions="1.3.5") public Future hlen(K key); /** * * @Redis HKEYS * @param key * @return list of keys in the given hashtable. * @throws RedisException */ @Redis(versions="1.3.n") public Future> hkeys(K key); /** * * @Redis HVALS * @param key * @return list of values in the given hashtable. * @throws RedisException */ @Redis(versions="1.3.n") public Future> hvals(K key); /** * * @Redis HGETALL * @param key * @return the given hash as a Map * @throws RedisException */ @Redis(versions="1.3.n") public Future> hgetall(K key); // ------------------------------------------------------------------------ // Multiple databases handling commands // ------------------------------------------------------------------------ // @Deprecated // public Future select (int index); /** * Flushes the db you selected when connecting to Redis server. Typically, * implementations will select db 0 on connecting if non was specified. Remember * that there is no roll-back. * @Redis FLUSHDB * @return */ public Future flushdb (); /** * Flushes all dbs in the connect Redis server, regardless of which db was selected * on connect time. Remember that there is no rollback. * @Redis FLUSHALL * @return */ public Future flushall (); /** * Moves the given key from the currently selected db to the one indicated * by dbIndex. * @Redis MOVE * @param key * @param dbIndex * @return */ public Future move (K key, int dbIndex); // ------------------------------------------------------------------------ // Sorting // ------------------------------------------------------------------------ /** *

For Usage details regarding sort semantics, see {@link JRedis#sort}. The * only difference in usage is that you must use the {@link Sort#execAsync()} method * which returns a {@link Future} instances. *

Usage: *

	 * Future>  futureResults = redis.sort("my-list-or-set-key").BY("weight*").LIMIT(1, 11).GET("object*").DESC().ALPHA().execAsync();
	 * List results = futureResult.get();  // wait for the asynchronous response to be processed
	 * for(byte[] item : results) {
	 *     // do something with item ..
	 *  }
	 * 
* * @Redis SORT * @see Redis * @see Future * */ public Sort sort(K key); // ------------------------------------------------------------------------ // Persistence control commands // ------------------------------------------------------------------------ /** * @Redis SAVE */ public Future save(); /** * @Redis BGSAVE */ public Future bgsave (); /** * @Redis BGREWRITEAOF * @return ack message. */ public Future bgrewriteaof (); /** * @Redis LASTSAVE * @return */ public Future lastsave (); // ------------------------------------------------------------------------ // Remote server control commands // ------------------------------------------------------------------------ /** * @Redis INFO * @return */ public Future> info () ; /** * @Redis SLAVEOF * @param host ip address * @param port */ public Future slaveof(String host, int port); /** * Convenience method. Turns off replication. * @Redis SLAVEOF "no one" */ public Future slaveofnone(); // ------------------------------------------------------------------------ // Diagnostics commands // ------------------------------------------------------------------------ /** * @Redis ECHO * @param msg * @return */ public Future echo (byte[] msg); public Future echo (String msg); public Future echo (Number msg); public Future echo (T msg); /** * @Redis DEBUG OBJECT * @param key * @return */ public Future debug (K key); } ================================================ FILE: core/api/src/main/java/org/jredis/KeyValueSet.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.io.Serializable; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 25, 2009 * @since alpha.0 * */ public interface KeyValueSet { public KeyValueSet add(K key, T value); byte[][] getMappings (); public interface ByteArrays extends KeyValueSet{/* nop */} public interface Numbers extends KeyValueSet{/* nop */} public interface Strings extends KeyValueSet{/* nop */} public interface Objects extends KeyValueSet{/* nop */} // abstract static class GenImpl implements BulkSetMappings { // protected final Map map = new HashMap(); // public BulkSetMappings add (String key, T value) { // map.put(key, value); // return this; // } // public Map getMappings () { return map; } // abstract protected byte[] toBytes(T value) ; // } // public final static class BytesValueMappings extends GenImpl implements BulkSetMappings { // abstract protected byte[] toBytes(T value) { // // } // } // public final static class StringValueMappings extends GenImpl implements BulkSetMappings { // abstract protected byte[] toBytes(T value) { // // } // } // public final static class NumberValueMappings extends GenImpl implements BulkSetMappings { // abstract protected byte[] toBytes(T value) { // // } // } // public final static class ObjectValueMappings extends GenImpl implements BulkSetMappings { // abstract protected byte[] toBytes(T value) { // // } // } // // public static final class TestMe { // @SuppressWarnings("null") // public static void main (String[] args) { // BulkSetMappings byteMappings = new BytesValueMappings(); // byteMappings // .add("foo", "woof".getBytes()) // .add("bar", "meow".getBytes()) // .add("paz", "the salt".getBytes()) // .add("x?", "yz!".getBytes()); // // try { // JRedis jredis = null; // if(jredis.mset(byteMappings)) { // System.out.println("ok!"); // } // } // catch (RedisException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // } // } } ================================================ FILE: core/api/src/main/java/org/jredis/NotSupportedException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * Providers can throw this exception in response for requests for optional * aspects of Redis Specification. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public class NotSupportedException extends ProviderException { /** */ private static final long serialVersionUID = _specification.Version.major; /** * @param string */ public NotSupportedException(String string) { super (string); } /** * @param string */ public NotSupportedException(String string, Throwable cause) { super (string, cause); } } ================================================ FILE: core/api/src/main/java/org/jredis/ObjectEncoding.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * Internal Redis object encoding schemes. * * @see ObjectInfo#getEncoding() * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 17, 2010 * @since alpha.0 | Redis 1.3.5 * */ public enum ObjectEncoding { /** Redis 'raw' */ RAW (), /** Redis 'int' */ INT (), /** Redis 'zipmap' */ ZIPMAP(), /** Redis 'hashtable' */ HASHTABLE, /** Redis 'embstr' */ EMBSTR, } ================================================ FILE: core/api/src/main/java/org/jredis/ObjectInfo.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.util.ArrayList; import java.util.Formatter; import java.util.List; import java.util.StringTokenizer; /** * Encapsulates the debug information returned by Redis in response to DEBUG * OBJECT . * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 16, 2010 * @since alpha.0 * @see JRedis#debug(String) */ public class ObjectInfo { private static final long numTokens = 7; private final String keyAddress; private final long keyRefCount; private final ObjectEncoding encoding; private final long serializedLength; private final long lru; private final long lruSecondsIdle; public ObjectInfo(String keyAddress, long keyRefCount, ObjectEncoding encoding, long serializedLength, long lru, long lruSecondsIdle) { this.keyAddress = keyAddress; this.keyRefCount = keyRefCount; this.encoding = encoding; this.serializedLength = serializedLength; this.lru = lru; this.lruSecondsIdle = lruSecondsIdle; } /** * @return the keyAddress as {@link String} representation of hex address. * Ex: "0x100d60" */ public String getKeyAddress() { return keyAddress; } /** @return the keyRefCount */ public long getKeyRefCount() { return keyRefCount; } /** * @return the valueAddress as {@link String} representation of hex address. * Ex: "0x100d60" */ /** @return the encoding */ public ObjectEncoding getEncoding() { return encoding; } /** * Convenience method to convert the address info to long. Ex: "0x101860" => * 1054816. (Note that the returned number is (obviously) base 10.) * * @param addressStrRep * @return */ public static long toLong(String addressStrRep) { return Long.parseLong(addressStrRep.substring(2), 16); } @SuppressWarnings("boxing") @Override public String toString() { Formatter formatter = new Formatter(); formatter .format("ObjectInfo: key [addr:%s refCnt: %d] encoding:%s serializedLength: %d lru %d lruSecondsIdle %d", keyAddress, keyRefCount, encoding, serializedLength, lru, lruSecondsIdle); return formatter.toString(); } /** * Strictly speaking, this doesn't belong here but cuts down on redundant * code. Parses the DEBUG OBJECT response to return an instance of * {@link ObjectInfo}. * * @param strRep * @return {@link ObjectInfo} */ static public final ObjectInfo valueOf(String strRep) { StringTokenizer tokenizer = new StringTokenizer(strRep); int tokenCnt = tokenizer.countTokens(); if (tokenCnt != numTokens) throw new ProviderException( "DEBUG OBJECT response does not conform to expected format. Got: [" + strRep + "]"); List tokens = new ArrayList(tokenCnt); while (tokenizer.hasMoreElements()) tokens.add(tokenizer.nextToken()); String keyAddr = tokens.get(1).substring(3); String keyCnt = tokens.get(2).substring("refcount:".length()); String encodingRep = tokens.get(3).substring("encoding:".length()); String serlen = tokens.get(4).substring("serializedlength:".length()); String lru = tokens.get(5).substring("lru:".length()); String lruSecondsIdle = tokens.get(6).substring( "lru_seconds_idle:".length()); ObjectInfo info = new ObjectInfo(keyAddr, Integer.parseInt(keyCnt), ObjectEncoding.valueOf(encodingRep.toUpperCase()), Integer.parseInt(serlen), Integer.parseInt(lru), Integer.parseInt(lruSecondsIdle)); return info; } } ================================================ FILE: core/api/src/main/java/org/jredis/ProviderException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * Used by JRedis implementation providers to indicate an exception related to the * implementation of the specification. Effectively, this exception and its extensions * should only be thrown when a fault has occurred that is neither a {@link SystemException}, * nor a Redis prompted server side error. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public class ProviderException extends ClientRuntimeException { /** */ public static final long version = _specification.Version.major; /** */ private static final long serialVersionUID = _specification.Version.major; /** * @param message */ public ProviderException(String message) { super(message); // TODO Auto-generated constructor stub } /** * @param message * @param cause */ public ProviderException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } } ================================================ FILE: core/api/src/main/java/org/jredis/Query.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.util.List; import java.util.concurrent.Future; /** * Provides for specifying and chaining query predicates. The results of the query directive are returned * as an ordered {@link List} of byte[] values. * *

Specific {@link Query} types are obtained from the {@link JRedis} api. Extensions of this interface * allow for the definition of natural expression of the query clauses. Such interfaces should only * define methods for the optional elements of the specific query type. Redis interface itself will also * only allow for obtaining a reference to such an interface by specifying the required arguments in the * associated method signature. * *

For example, to get a {@link Sort} instance, {@link JRedis} api specifies the required argument, * (namely the the key) in the method signature for {@link JRedis#sort(String)}, and all the optional * elements of the SORT command are provided for in the {@link Sort} interface: *

*


 * Sort    sort = redis.sort (key)
 * List  results = BY("foo*").LIMIT(1, 11).GET("*woof*").DESC().exec();
 * for(byte[] item : results) {
 *     // do something with item ..
 *  }
 * 
 * 
* * @see Sort * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface Query { /** * Executes the query. * * @return the resultant value list from redis. * @throws IllegalStateException * @throws RedisException */ // TODO: why illegal state? public List exec () throws IllegalStateException, RedisException; public Future> execAsync (); public static class Support { public static long unpackValue (List queryResult){ if(null == queryResult) throw new ClientRuntimeException("queryResult is null"); if(queryResult.size() < 1) throw new ClientRuntimeException("queryResult must have at least 1 entry"); return Long.parseLong(new String(queryResult.get(0))); } } } ================================================ FILE: core/api/src/main/java/org/jredis/Redis.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation to mark complaint elements of JRedis implementations. * * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PACKAGE}) @Retention(RetentionPolicy.RUNTIME) public @interface Redis { /** * @return array of strings denoting version of redis protocol supported */ String[] versions(); } ================================================ FILE: core/api/src/main/java/org/jredis/RedisException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import org.jredis.protocol.Command; //import org.jredis.connector.ProviderException; /** * [TODO: update doc regarding "extensions"] * * RedisExceptions are only created/raised subsequent to an error result from the redis server. * These exceptions are _not_ intended as a general purpose exception mechanism for * implementations of this specification. Accordingly, this exception (and extensions if any) * do not (and must not) provide default no arg constructors. (Minimal information required is * an associated {@link Command} object.) * *

Further, this class (and extension if any) should never expose a constructor accepting * an underlying 'cause' (as in {@link Exception#Exception(Throwable)}), since this excpetion, * to repeat, has very specific semantics: Redis has responded with an error * status to an issued command and the content of that message (e.g. "-ERR operation on wrong type" * is what is required for instantiating a {@link RedisException}. * * @see ClientRuntimeException * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public final class RedisException extends Exception { /** */ public static final long version = _specification.Version.major; /** */ private static final long serialVersionUID = version; /** */ private final Command command; // public RedisException (String message) { // super (message); // } public RedisException (Command command, String message){ super (message); this.command = command; } /** @return the associated {@link Command} if any */ public Command getCommand () { return command; } public String toString () { return "Exception on [" + command.code + "] => " + getMessage(); } } ================================================ FILE: core/api/src/main/java/org/jredis/RedisInfo.java ================================================ /* * Copyright 2009-2012 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.util.Map; /** * RedisInfo is a convenience enum that tracks the canonical entries returned by INFO command. * Exception to this is the db## when ## is db number e.g. db10. * * Note that of course the {@link JRedis#info()} command returns a {@link Map} so you can always * query for the db info directly using the returned map. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0-05062012 * @since alpha.0 * */ public enum RedisInfo { redis_git_dirty, redis_git_sha1, used_cpu_user, client_longest_output_list, latest_fork_usec, used_memory_peak, pubsub_patterns, used_cpu_sys_children, used_memory_rss, lru_clock, aof_enabled, mem_fragmentation_ratio, used_memory_peak_human, loading, client_biggest_input_buf, used_cpu_sys, keyspace_hits, evicted_keys, pubsub_channels, used_cpu_user_children, gcc_version, expired_keys, mem_allocator, keyspace_misses, arch_bits, multiplexing_api, redis_version, process_id, connected_clients, connected_slaves, blocked_clients, used_memory, used_memory_human, changes_since_last_save, bgsave_in_progress, last_save_time, bgrewriteaof_in_progress, total_connections_received, total_commands_processed, uptime_in_seconds, uptime_in_days, // hash_max_zipmap_entries, // hash_max_zipmap_value, // vm_enabled, os, tcp_port, used_memory_lua, last_bgsave_status, instantaneous_ops_per_sec, rejected_connections, run_id, bgrewriteaof_scheduled, role } ================================================ FILE: core/api/src/main/java/org/jredis/RedisType.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * * @author Joubin Houshyar (alphazero@sensesay.net) */ public enum RedisType { NONE, string, list, set, hash } ================================================ FILE: core/api/src/main/java/org/jredis/Semantics.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * Interface to specify the semantics of the actual key/values stored in the Redis server. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Aug 23, 2009 * @since alpha.0 * */ public interface Semantics { /** * @param * @param keyClass * @return the Codec used to */ public Codec getKeyCodec(Class keyClass); /** * @param * @param keyClass * @param keyCodec * @return */ public Semantics setKeyCodec(Class keyClass, Codec keyCodec); /** * @param * @param valueClass * @return */ public Codec getValueCodec(Class valueClass); /** * @param * @param valueClass * @param valueCdec * @return */ public Semantics setValueCodec(Class valueClass, Codec valueCdec); } ================================================ FILE: core/api/src/main/java/org/jredis/Sort.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import java.util.List; /** * Sort, a type of {@link Query}, provides for specifying and chaining the query predicates. The results of the sort directive are returned * as an ordered {@link List} of byte[] values. * *

Sort instances are obtained directly from the {@link JRedis} client, by calling the eponymous method {@link JRedis#sort(String)}, which expects * a key name as its required arguments. * *
[Note: this provides the pattern for all future query types: required args are specified in the method that returns the {@link Query} form.] * *

As with all {@link Query} forms, * do not forget to call {@link Query#exec()} at the end! * *

Usage: *

 * List  results = redis.sort("my-list-or-set-key").BY("weight*").LIMIT(1, 11).GET("object*").DESC().ALPHA().exec();
 * for(byte[] item : results) {
 *     // do something with item ..
 *  }
 * 
*

Sort specification elements are all optional. You could simply say: *

 * List  results = redis.sort("my-list-or-set-key").exec();
 * for(byte[] item : results) {
 *     // do something with item ..
 *  }
 * 
*

Sort specification elements are also can appear in any order -- the client implementation will send them to the server * in the order expected by the protocol, although it is good form to specify the predicates in natural order: *

 * List  results = redis.sort("my-list-or-set-key").GET("object*").DESC().ALPHA().BY("weight*").LIMIT(1, 11).exec();
 * for(byte[] item : results) {
 *     // do something with item ..
 *  }
 * 
* * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface Sort extends Query { /** species the BY clause */ Sort BY (K pattern); /** specifies the GET clause */ Sort GET (K pattern); /** * Specifies the LIMIT class: from is the initial index, count is the number of results * @param from limit sort results to element at index 'from' * @param count limit sort results to count - results will be result[from] to result[from+count], inclusve. */ Sort LIMIT (long from, long count); /** default sort is ASCending -- use this in your sort to specify DESC sort */ Sort DESC (); /** sort is be default numeric -- use this to indicate lexiographic alphanumeric sort */ Sort ALPHA (); /** * Store the sort results in another key. *

* This command alters the semantics of the {@link Query#exec()} (or {@link Query#execAsync()()} * to return a list of size 1, with the single entry being the long value representing the size * of the created (store destination) list. *

*

	 *	// NOTE: this is a Long List - javadoc doesn't print the brackets.
	 *	//
	 *	List storeResult = DefaultCodec.toLong(jredis.sort(srcSet).DESC().STORE(destKey).exec());
	 *
	 *	// the STORE() option changes result semantics -- now we expect our list to have a single entry
	 *	// for the size of the stored list
	 *	//
	 *	size = sortedSet.get(0);
	 * 
	 * 	System.out.format("Sorted list (size: %d):\n", size);
	 * 
*/ Sort STORE (K destKey); } ================================================ FILE: core/api/src/main/java/org/jredis/ZSetEntry.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * An entry in a Redis "sorted set" and returned by a subset Z* commands. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 20, 2010 * @since alpha.0 * */ public interface ZSetEntry{ /** @return the value of this entry in a Redis sorted set */ byte[] getValue(); /** @return the score associated with the value */ double getScore(); } ================================================ FILE: core/api/src/main/java/org/jredis/_specification.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface _specification { /** specification level */ public interface Version { long major = 0xA; long minor = 0x1; } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/Connection.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Response; /** * {@link Connection} defines the general (required) and optional * contract of a JRedis connection. *

* Redis protocol does not provide for request sequencing (for a * variety of good reasons) and guaranteed delivery semantics are * supported only by multi-exec (redis transaction) protocol. Accordingly, * {@link Connection} is not required (given that it can not) to support * fault-tolerant semantics. *

* If a connection faults during an interaction with the server * (e.g. anytime during send and receive) it must raise one of the following: *

    *
  • {@link ConnectionReset} - connection faulted but connection re-established *
  • {@link ConnectionFault} - connection faulted and reconnect not possible. *
* In the former case, the specific request that was being processed will not * be transparently re-issued by the connector. The application layer must * determine what course of action to take. *

* In either case, it should be noted that there exists a tiny (but possible) * window where even with append logging on the server, where the server received * and processed the request but never could reply (e.g. the server crashes for * whatever reason). These considerations are irrelevant to read only commands, * but are significant in context of write ops (e.g. INCR). If you require * guarantees on writes, you must use redis transactions (e.g. multi-bulk). * *

* That said, the {@link Connection}'s optional event and state management * do provide sufficient support for a softer set of guarantees for * transparent connection management (on fault detection, connect on demand, * etc.). * *
* @author joubin (alphazero@sensesay.net) * @date Sep 12, 2010 * */ public interface Connection { /** * The {@link ConnectionSpec} of a Connection must be invariant during its life-cycle. * @return the associated {@link ConnectionSpec} for this Connection. */ public ConnectionSpec getSpec(); /** * A blocking call to service the specified request. This method will return upon * the completion of the request response protocol with the connected redis server. Timeouts * and related matters are not addressed by this method or the {@link Protocol} interface * and can (and should) be addressed at the implementation level (for example when creating * handler instances using a specification set, including max wait for synchronous response.) * *

{@link Modality#Asynchronous} handlers must always throw a {@link ClientRuntimeException} * for this method which violates the contract for {@link Modality#Asynchronous} handlers. * * @param cmd * @param args * @return * @throws RedisException * @throws ClientRuntimeException * @throws ProviderException */ public Response serviceRequest (Command cmd, byte[]...args) throws RedisException, ClientRuntimeException, ProviderException; /** * A non-blocking call to service the specified request at some point in the future. * This method will return immediately with a {@link Future} object of parametric type {@link Response} *

* When the request is serviced, call to {@link Future#get()} will return the request response. *

{@link Modality#Synchronous} handlers must always throw a {@link ClientRuntimeException} * for this method which violates the contract for {@link Modality#Synchronous} handlers. *

* If request resulted in a redis error ({@link RedisException}), the exception will be set as the cause of * the corresponding {@link ExecutionException} of the {@link Future} object returned. * @param cmd * @param args * @return the {@link Future} {@link Response}. * @throws ClientRuntimeException * @throws ProviderException * @see Future * @see ExecutionException */ public Future queueRequest (Command cmd, byte[]...args) throws ClientRuntimeException, ProviderException; // ------------------------------------------------------------------------ // State management -- optional /** */ public enum State { /** Connection is initialized and ready. Will connect on demand */ INITIALIZED, /** Connection is established. */ CONNECTED, /** Not connected to remote server. Can connect on demand. */ DISCONNECTED, /** Connection is shutdown and can be disposed. */ TERMINATED } // ------------------------------------------------------------------------ // Event management -- optional /** * Connection.Event propagation. * @optional * @param connListener * @return true if listener was successfully added. */ public boolean addListener(Listener connListener); /** * Optional event propagation method. Removes the specified listenr * from the list of {@link Connection.Listener}s. * @param connListener * @return true if the listener was actually present and was removed. */ public boolean removeListener(Listener connListener); // ======================================================================== // Innner Types // ======================================================================== /** * Enumeration of the top-level properties of the {@link Connection} that can be * specified by the User. * * @author joubin (alphazero@sensesay.net) * @date Sep 13, 2010 * */ public enum Property { /** the redis server host name or ip */ HOST, /** the Redis server port */ PORT, /** the password used */ CREDENTIAL, /** The db selected on (re-)connect */ DB, /** Defines the {@link Connection}'s {@link Connection.Modality} */ MODALITY, /** On Connect (or reconnect after faults or timeouts) the maximum duration that you are willing to wait, milliseconds */ MAX_CONNECT_WAIT, /** number of reconnect attempts after timeouts or faults. */ MAX_CONNECT_ATTEMPT, /** if specified, is used to create the new protocol */ PROTOCOL_CLASS, /** if specified, is used to create the new protocol */ PROTOCOL_FACTORY, /** if specified, is used to create the new connection */ CONNECTION_CLASS, /** if specified, is used to create the new connection. */ CONNECTION_FACTORY, ;// -- fini } /** * Enum for defining the operational modality of the protocol handlers. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public enum Modality { /** blocking request/reply semantics */ Synchronous, /** non-blocking request/future-response semantics */ Asynchronous, /** */ PubSub, /** */ Monitor, ; // -- end } /** * {@link Connection} listeners' callback API. * @optional * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 29, 2010 * @since alpha.0 * */ public interface Listener { public void onEvent(Connection.Event event); } /** * Events raised by the {@link Connection}. *
Events are typed and may have optional event info (Objects). * @optional * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 29, 2010 * @since alpha.0 * */ @SuppressWarnings("serial") final public static class Event extends org.jredis.Event{ /** * @param src * @param type */ public Event (Connection src, Type type) { super(src, type); } public Event (Connection src, Type type, Object eventInfo) { super(src, type, eventInfo); } /** Connector.Event types. */ public enum Type { // INITIALIZED, /** Raised when Connector is about to initiate the connect protocol */ CONNECTING, /** Raised when Connector has established connectivity to the remote server */ CONNECTED, /** Raised when Connector is about to initiate the disconnect protocol */ DISCONNECTING, /** Raised when Connector has disconnected from the remote server */ DISCONNECTED, /** Raised when the Connector encounters a {@link ClientRuntimeException} or {@link ProviderException}. */ FAULTED, /** * Raised to signal the beginning of the shutdown sequence (commences after listerners are notified. * Cease all activity on receipt * */ // STOPPING, /** Raised when Connector is terminated. Dispose of your references on receipt. */ SHUTDOWN } } // ------------------------------------------------------------------------ // Connection.Flag // ------------------------------------------------------------------------ /** * Connection flags - not necessarily mutually exclusive. Uses a 32 bit * mask for 32 (max) disctinct flags for Connnections. * * @author joubin (alphazero@sensesay.net) * @date Sep 18, 2010 * */ public enum Flag { /** if true will connect immediately on initialization. otherwise on first use. */ CONNECT_IMMEDIATELY, // /** */ // TRANSPARENT_RECONNECT, // /** */ // RETRY_AFTER_RESET, /** if true uses pipelining */ PIPELINE, /** Connection can be used by more than 1 client concurrently */ SHARED, /** if true attempts to maintain connection. drops are detected. Better fault tolerance guarantees, w/ some performance impact */ RELIABLE, /** if true connection maintains conversational state - for use with multi-exec */ STATEFUL, /** if true service requests are logged (verbose/slower due to io) */ TRACE, ; public final int bitmask; static final int OPAQUE_BITMASK = 0x0000; Flag (){ this.bitmask = (int)Math.pow(2, ordinal()); } static final public int bitset(Flag...flags){ int bitset = OPAQUE_BITMASK; return bitset(bitset, flags); } static final public int bitset(final int bitset, Flag...flags){ int _bitset = bitset; for(Flag f : flags) _bitset = _bitset | f.bitmask; return _bitset; } static final public int bitclear(final int bitset, Flag...flags){ int _bitset = bitset; for(Flag f : flags) _bitset = _bitset ^ f.bitmask; return _bitset; } public static boolean isSet(int bitset, Flag flag) { return (bitset & flag.bitmask) > OPAQUE_BITMASK; } } // ------------------------------------------------------------------------ // Connection.Socket // ------------------------------------------------------------------------ public interface Socket { /** * Flag keys for SocketFlag settings of the connection specification. * @see ConnectionSpec#getSocketFlag(SocketFlag) * @author Joubin Houshyar (alphazero@sensesay.net) * */ public enum Flag { /** Corresponds to SO_KEEP_ALIVE flag of {@link Socket}. @see {@link Socket#setSoTimeout(int)} */ SO_KEEP_ALIVE, ; } /** * Property keys for SocketProperty settings of the connection specification. * @see ConnectionSpec#getSocketProperty(SocketProperty) * @author Joubin Houshyar (alphazero@sensesay.net) * */ public enum Property { /** * Corresponds to SO_SNDBUF flag. see {@link Socket#setSendBufferSize(int)} *

expected value is an int or an {@link Integer}. */ SO_SNDBUF, /** * corresponds to SO_RCVBUF flag. see {@link Socket#setReceiveBufferSize(int)} */ SO_RCVBUF, /** * corresponds to SO_TIMEOUT flag. see {@link Socket#setSoTimeout(int)} */ SO_TIMEOUT, /** * Socket performance preferences. *

This property will be used in conjunction with other associated properties. * @See {@link SocketProperty#latency} * @See {@link SocketProperty#bandwidth} * @See {@link Socket#setPerformancePreferences(int, int, int)} for details. */ SO_PREF_CONN_TIME, /** * Socket performance preferences. *

This property will be used in conjunction with other associated properties. * @See {@link SocketProperty#bandwidth} * @See {@link SocketProperty#connection_time} * @See {@link Socket#setPerformancePreferences(int, int, int)} for details. */ SO_PREF_LATENCY, /** * Socket performance preferences. *

This property will be used in conjunction with other associated properties. * @See {@link SocketProperty#latency} * @See {@link SocketProperty#connection_time} * @See {@link Socket#setPerformancePreferences(int, int, int)} for details. */ SO_PREF_BANDWIDTH, } } // ------------------------------------------------------------------------ // Connection.Factor // ------------------------------------------------------------------------ public interface Factory { /** * Creates a connection to a redis server using the specified connection attributes. * @param spec of the new connection * @return a new {@link Connection} initialized per spec. * @throws ClientRuntimeException if the requirements exceed system resources/limits. * @throws NotSupportedException if the {@link ConnectionSpec} provided * can not be supported by the provider. */ public Connection newConnection (ConnectionSpec spec) throws ClientRuntimeException, NotSupportedException; } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/ConnectionException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.net.SocketException; import org.jredis.ClientRuntimeException; import org.jredis._specification; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ abstract public class ConnectionException extends ClientRuntimeException{ /** */ private static final long serialVersionUID = _specification.Version.major; /** * TODO: not sure if specifying {@link SocketException} is a good idea. * @param msg * @param e */ public ConnectionException(String msg, SocketException e) { super(msg, e); } /** * @param msg */ public ConnectionException(String msg) { super(msg); } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/ConnectionFault.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.net.SocketException; import org.jredis._specification; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 12, 2010 * */ public class ConnectionFault extends ConnectionException { /** */ private static final long serialVersionUID = _specification.Version.major; /** * @param string * @param e */ public ConnectionFault(String msg, SocketException e) { super(msg, e); } public ConnectionFault(String msg) { super(msg); } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/ConnectionReset.java ================================================ /* * Copyright 2009-2011 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketException; import org.jredis._specification; import org.jredis.protocol.Command; /** * This exception is thrown by a {@link Connection} to indicate that the connection to redis was * reset (typically due to timeout on Redis side). *

* If the {@link Connection} is specified to use * auto-reconnect, the connection has been re-established, but, the status of the request that * gave rise to this exception is indeterminate, in the sense that while this exception certainly indicates * that the response was not received from Redis, it is not clear whether the request itself was * received by the server. This is due to the fact that writing to the {@link OutputStream} of a {@link Socket} * that has been closed by the remote peer does NOT raise an exception. The exception is raised (reliably) only * on the receive when reading from the {@link Socket}'s {@link InputStream}. *

* Depending on the application domain and the {@link Command} of the request, the user may retry * the request. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ public class ConnectionReset extends ConnectionException { /** */ private static final long serialVersionUID = _specification.Version.major; private static final String info = "potential redis timeout"; /** * @param string * @param e */ public ConnectionReset(String msg, SocketException e) { super(String.format("(%s) %s", info, msg), e); } public ConnectionReset(String msg) { super(String.format("(%s) %s", info, msg)); } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/ConnectionSpec.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import org.jredis.connector.Connection.Modality; import org.jredis.connector.Connection.Property; /** * ConnectionSpec specifies the parameters used in the creation and * runtime operation of JRedis connections. * * @author joubin (alphazero@sensesay.net) * * @see Factory#newConnection(ConnectionSpec) * @see java.resource.cci.ConnectionSpec * */ public interface ConnectionSpec { /** * @return */ public InetAddress getAddress(); /** * @param address * @return the {@link ConnectionSpec} */ public ConnectionSpec setAddress(InetAddress address); /** * @return the port number for the connection */ public int getPort (); /** * @param port * @return the {@link ConnectionSpec} */ public ConnectionSpec setPort(int port); /** * @return the password (if any) for the connection. Used on (re-)connect to authenticate * the client after connectivity has been established * @Redis AUTH * @see Command#AUTH */ public byte[] getCredentials(); /** * Set the Connection's credentials, presented to Redis on (re)connects. * @param credentials * @see ConnectionSpec#setCredentials(String) * @return the {@link ConnectionSpec} */ public ConnectionSpec setCredentials(byte[] credentials); /** * Convenience method * @param credentials * @see ConnectionSpec#setCredentials(byte[]) * @return the {@link ConnectionSpec} */ public ConnectionSpec setCredentials(String credentials); /** * @return the database selected for the connection. Used on (re-)connect to select the db * after network connectivity has been established. * @Redis SELECT * @see Command#SELECT */ public int getDatabase (); /** * @param database * @return the {@link ConnectionSpec} */ public ConnectionSpec setDatabase(int database); /** * @return the {@link Modality} of this protocol handler. */ public Modality getModality (); /** * @param modality * @return the the {@link ConnectionSpec} */ public ConnectionSpec setModality (Modality modality); /** * Get the {@link SocketFlag} for the {@link ConnectionSpec} * @param flag * @return the specified TCP socket flag used for the connection. * @see SocketFlag */ public boolean getSocketFlag (Connection.Socket.Flag flag); /** * Set the {@link Connection.Socket.Flag} for the {@link ConnectionSpec} * @param flag * @param value * @return {@link ConnectionSpec} this */ public ConnectionSpec setSocketFlag(Connection.Socket.Flag flag, Boolean value); /** * Get the {@link Connection.Socket.Property} for the {@link ConnectionSpec} * @param property * @return the specified socket property used for the connection. * @see SocketFlag */ public Integer getSocketProperty (Connection.Socket.Property property); /** * Set the {@link SocketProperty} for the {@link ConnectionSpec} * @param property * @param value * @return the previous value (if any). Null if none existed, per {@link Map#put(Object, Object)} semantics. */ public ConnectionSpec setSocketProperty(Connection.Socket.Property property, Integer value); /** * @param flag * @return the {@link Connection.Flag} * @see SocketFlag */ public boolean getConnectionFlag (Connection.Flag flag); /** * Sets the specified {@link Connection.Flag} * @param flag * @param value * @return the referenced {@link ConnectionSpec} */ public ConnectionSpec setConnectionFlag(Connection.Flag flag, Boolean value); /** * @param * @param proptype * @return */ public Object getConnectionProperty(Property prop); /** * @param * @param property * @param value * @return */ public ConnectionSpec setConnectionProperty(Property prop, Object value); /** * @return */ public int getReconnectCnt (); /** * @param cnt * @return the {@link ConnectionSpec} */ public ConnectionSpec setReconnectCnt(int cnt); /** * @return */ public int getMaxConnectWait (); /** * @param cnt * @return the {@link ConnectionSpec} */ public ConnectionSpec setMaxConnectWait(int cnt); /** * @return the heartbeat period in seconds */ public int getHeartbeat(); /** * @param seconds heartbeat period * @return the {@link ConnectionSpec} */ public ConnectionSpec setHeartbeat(int seconds); // ------------------------------------------------------------------------ // Reference Implementation // ------------------------------------------------------------------------ /** * Reference implementation of {@link ConnectionSpec}. *

* This implementation is a read/write implementation providing no default * values of any kind. It can be used as is and initialized as required, * or (as it is intended) it will provide support for various connection * profiles (e.g. server connectors), etc. * * @author joubin (alphazero@sensesay.net) * @version alpha.0, Aug 23, 2009 * @since alpha.0 * */ public static class RefImpl implements ConnectionSpec { // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ /** {@link Map} of the {@link SocketFlag}s of the {@link ConnectionSpec} */ Map socketFlags = new HashMap(); /** {@link Map} of the {@link SocketProperty}s of the {@link ConnectionSpec} */ Map socketProperties = new HashMap(); /** Connection.Flag bitmask */ int connectionFlagBitmask; /** {@link Map} of the {@link Connection.Flag}s of the {@link ConnectionSpec} */ Map connectionProperties = new HashMap(); /** heartbeat period in milliseconds */ private int heartbeat; // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Interface // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getAddress() */ @Override final public InetAddress getAddress () { return (InetAddress) getConnectionProperty(Connection.Property.HOST); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getCredentials() */ @Override final public byte[] getCredentials () { return (byte[]) getConnectionProperty(Connection.Property.CREDENTIAL); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getDatabase() */ @Override final public int getDatabase () { return (Integer) getConnectionProperty(Connection.Property.DB); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getPort() */ @Override final public int getPort () { return (Integer) getConnectionProperty(Connection.Property.PORT); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getModality() */ final public Modality getModality () { return (Modality) getConnectionProperty(Connection.Property.MODALITY); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getMaxConnectWait() */ final public int getMaxConnectWait () { return (Integer) getConnectionProperty(Connection.Property.MAX_CONNECT_WAIT); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getReconnectCnt() */ @Override final public int getReconnectCnt () { return (Integer) getConnectionProperty(Connection.Property.MAX_CONNECT_ATTEMPT); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getSocketFlag(org.jredis.connector.ConnectionSpec.SocketFlag) */ @Override final public boolean getSocketFlag (Connection.Socket.Flag flag) { return socketFlags.get(flag); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getSocketProperty(org.jredis.connector.ConnectionSpec.SocketProperty) */ @Override final public Integer getSocketProperty (Connection.Socket.Property property) { return socketProperties.get(property); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getConnectionProperty(org.jredis.connector.Connection.Property) */ @Override final public Object getConnectionProperty(Property prop){ return connectionProperties.get(prop); } // ------------------------------------------------------------------------ // Property Setters // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setAddress(java.net.InetAddress) */ final public ConnectionSpec setAddress (InetAddress address) { setConnectionProperty(Connection.Property.HOST, address); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setPort(int) */ @Override final public ConnectionSpec setPort (int port) { setConnectionProperty(Connection.Property.PORT, port); // this.port = port; return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setCredentials(byte[]) */ @Override final public ConnectionSpec setCredentials (byte[] credentials) { setConnectionProperty(Connection.Property.CREDENTIAL, credentials); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setCredentials(java.lang.String) */ @Override final public ConnectionSpec setCredentials (String credentials) { byte[] bytes; if(credentials == null || credentials.length() == 0) bytes = null; else bytes = credentials.getBytes(); return setCredentials(bytes); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setDatabase(int) */ @Override final public ConnectionSpec setDatabase (int database) { setConnectionProperty(Connection.Property.DB, database); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setReconnectCnt(int) */ @Override final public ConnectionSpec setReconnectCnt (int reconnectCnt) { setConnectionProperty(Connection.Property.MAX_CONNECT_ATTEMPT, reconnectCnt); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setConnectionProperty(org.jredis.connector.Connection.Property, java.lang.Object) */ @Override final public ConnectionSpec setConnectionProperty(Property prop, Object value){ try { connectionProperties.put(prop, value); } catch (ClassCastException e){ throw new IllegalArgumentException("value type", e);} return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setModality(org.jredis.connector.Connection.Modality) */ @Override final public ConnectionSpec setModality (Modality modality) { setConnectionProperty(Connection.Property.MODALITY, modality); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getMaxConnectWait(int) */ final public ConnectionSpec setMaxConnectWait(int cnt) { setConnectionProperty(Connection.Property.MAX_CONNECT_WAIT, cnt); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setSocketFlag(org.jredis.connector.ConnectionSpec.SocketFlag, java.lang.Boolean) */ @Override final public ConnectionSpec setSocketFlag(Connection.Socket.Flag flag, Boolean value){ socketFlags.put(flag, value); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setSocketProperty(org.jredis.connector.ConnectionSpec.SocketProperty, java.lang.Integer) */ @Override final public ConnectionSpec setSocketProperty(Connection.Socket.Property property, Integer value){ socketProperties.put(property, value); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getConnectionFlag(org.jredis.connector.ConnectionSpec.ConnectionFlag) */ @Override final public boolean getConnectionFlag (Connection.Flag flag){ return Connection.Flag.isSet(connectionFlagBitmask, flag); } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setConnectionFlag(org.jredis.connector.ConnectionSpec.ConnectionFlag, java.lang.Boolean) */ @SuppressWarnings("boxing") @Override final public ConnectionSpec setConnectionFlag(Connection.Flag flag, Boolean value){ connectionFlagBitmask = value ? Connection.Flag.bitset(connectionFlagBitmask, flag) : Connection.Flag.bitclear(connectionFlagBitmask, flag); return this; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#getHeartbeat() */ @Override final public int getHeartbeat() { return heartbeat/1000; } /* (non-Javadoc) @see org.jredis.connector.ConnectionSpec#setHeartbeat(int) */ @Override final public ConnectionSpec setHeartbeat(int seconds) { this.heartbeat = seconds * 1000; return this; } } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/FaultedConnection.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.protocol.Command; import org.jredis.protocol.Response; /** * {@link FaultedConnection} is a support class for implementors of JRedis API. * * @optional * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 11, 2009 * @since alpha.0 * */ public class FaultedConnection implements Connection { /** */ final private String errorMsg; /** */ final private ConnectionSpec connSpec; /** * instantiates a faulted connection for the given {@link ConnectionSpec} * @param connSpec * @param errMsg */ public FaultedConnection (ConnectionSpec connSpec, String errMsg) { this.errorMsg = errMsg; this.connSpec = connSpec; } /* (non-Javadoc) @see org.jredis.connector.Connection#getSpec() */ @Override public ConnectionSpec getSpec() { return connSpec; } /* (non-Javadoc) @see org.jredis.connector.Connection#serviceRequest(org.jredis.protocol.Command, byte[][]) */ @Override public Response serviceRequest(Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { throw new ClientRuntimeException (errorMsg); } /* (non-Javadoc) @see org.jredis.connector.Connection#queueRequest(org.jredis.protocol.Command, byte[][]) */ @Override public Future queueRequest(Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { throw new ClientRuntimeException (errorMsg); } /* (non-Javadoc) @see org.jredis.connector.Connection#addListener(org.jredis.connector.Connection.Listener) */ @Override final public boolean addListener (Listener connListener) { throw new NotSupportedException("Events not supported"); } /* (non-Javadoc) @see org.jredis.connector.Connection#removeListener(org.jredis.connector.Connection.Listener) */ @Override final public boolean removeListener (Listener connListener) { throw new NotSupportedException("Events not supported"); } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/Message.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface Message { /** * Reads itself from the provided {@link InputStream} * * @param in the steam to read from. * * @throws ClientRuntimeException to indicate a system error, potentially network related * and hopefully recoverable. Typically used to wrap and propagate the IO stream's thrown * {@link IOException}s. * * @throws ProviderException to indicate operational error not directly related to the stream, * and should be treated as a bug. */ public void read (InputStream in) throws ClientRuntimeException, ProviderException; /** * Writes itself to the provided {@link OutputStream}. * * @param out the stream to write to. * * @throws ClientRuntimeException to indicate a system error, potentially network related * and hopefully recoverable. Typically used to wrap and propagate the IO stream's thrown * {@link IOException}s. * * @throws ProviderException to indicate operational error not directly related to the stream, * and should be treated as a bug. */ public void write (OutputStream out)throws ClientRuntimeException, ProviderException; } ================================================ FILE: core/api/src/main/java/org/jredis/connector/NotConnectedException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis._specification; /** * This connection will should be thrown if users issue any calls on * the {@link JRedis} interface after a call to either {@link JRedis#quit()} * or {@link JRedis#shutdown()} * * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public class NotConnectedException extends ClientRuntimeException { /** */ private static final long serialVersionUID = _specification.Version.major; public NotConnectedException (String msg) { super (msg); } } ================================================ FILE: core/api/src/main/java/org/jredis/connector/RequestListener.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import org.jredis.protocol.Request; import org.jredis.protocol.Response; public interface RequestListener { public void onResponse (Object context, Request request, Response response); } ================================================ FILE: core/api/src/main/java/org/jredis/connector/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * Native Client Interface Specification. *
* This package depends on the Protocol package. * [TODO: document me!] *

* This is an optional package. */ package org.jredis.connector; ================================================ FILE: core/api/src/main/java/org/jredis/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * This package contains the constructs that define the Java API semantics of the * JRedis clients. * * TODO: elaborate on the spec requirements. * * @author joubin (alphazero@sensesay.net) * * @since jredis-a.0 * @compliance redis-0.09 * */ package org.jredis; ================================================ FILE: core/api/src/main/java/org/jredis/protocol/BulkResponse.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface BulkResponse extends Response { /** * @return */ public byte[] getBulkData (); } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/Command.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import org.jredis.Redis; /** * Redis commands, (~) verbatim. Each member of the Command enum maps to a * corresponding (protocol level) command in Redis. Commands with optional * semantics are further distinguished: optional variants include * embedded '$'). * *

specification Redis 1.00 * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, 04/02/09 (Redis 1.n) * @version alpha.0, 09/17/10 (Redis 2.n) * @since alpha.0 * */ @Redis(versions={"1.n", "2.0"}) public enum Command { // security AUTH (RequestType.KEY, ResponseType.STATUS), // connection handling PING (RequestType.NO_ARG, ResponseType.STATUS), QUIT (RequestType.NO_ARG, ResponseType.VIRTUAL), CONN_FLUSH (RequestType.NO_ARG, ResponseType.NOP), // String values operations SET (RequestType.KEY_VALUE, ResponseType.STATUS), GET (RequestType.KEY, ResponseType.BULK), GETSET (RequestType.KEY_VALUE, ResponseType.BULK), MGET (RequestType.MULTI_KEY, ResponseType.MULTI_BULK), SETNX (RequestType.KEY_VALUE, ResponseType.BOOLEAN), MSET (RequestType.BULK_SET, ResponseType.STATUS), MSETNX (RequestType.BULK_SET, ResponseType.BOOLEAN), INCR (RequestType.KEY, ResponseType.NUMBER), INCRBY (RequestType.KEY_NUM, ResponseType.NUMBER), DECR (RequestType.KEY, ResponseType.NUMBER), DECRBY (RequestType.KEY_NUM, ResponseType.NUMBER), EXISTS (RequestType.KEY, ResponseType.BOOLEAN), DEL (RequestType.MULTI_KEY, ResponseType.NUMBER), TYPE (RequestType.KEY, ResponseType.STRING), SUBSTR (RequestType.KEY_NUM_NUM, ResponseType.BULK), APPEND (RequestType.KEY_VALUE, ResponseType.NUMBER), // "Commands operating on the key space" KEYS (RequestType.KEY, ResponseType.MULTI_BULK), KEYSTOLIST (RequestType.KEY_KEY, ResponseType.NUMBER), RANDOMKEY (RequestType.NO_ARG, ResponseType.BULK), RENAME (RequestType.KEY_KEY, ResponseType.STATUS), RENAMENX (RequestType.KEY_KEY, ResponseType.BOOLEAN), DBSIZE (RequestType.NO_ARG, ResponseType.NUMBER), EXPIRE (RequestType.KEY_NUM, ResponseType.BOOLEAN), EXPIREAT (RequestType.KEY_NUM, ResponseType.BOOLEAN), TTL (RequestType.KEY, ResponseType.NUMBER), // Commands operating on lists RPUSH (RequestType.KEY_VALUE, ResponseType.NUMBER), RPUSHX (RequestType.KEY_VALUE, ResponseType.NUMBER), LPUSH (RequestType.KEY_VALUE, ResponseType.NUMBER), LPUSHX (RequestType.KEY_VALUE, ResponseType.NUMBER), LINSERT (RequestType.BULK_SET, ResponseType.NUMBER), LLEN (RequestType.KEY, ResponseType.NUMBER), LRANGE (RequestType.KEY_NUM_NUM, ResponseType.MULTI_BULK), LTRIM (RequestType.KEY_NUM_NUM, ResponseType.STATUS), LINDEX (RequestType.KEY_NUM, ResponseType.BULK), LSET (RequestType.KEY_IDX_VALUE, ResponseType.STATUS), LREM (RequestType.KEY_CNT_VALUE, ResponseType.NUMBER), LPOP (RequestType.KEY, ResponseType.BULK), RPOP (RequestType.KEY, ResponseType.BULK), RPOPLPUSH (RequestType.KEY_KEY, ResponseType.BULK), // Commands operating on sets SADD (RequestType.KEY_VALUE, ResponseType.BOOLEAN), SREM (RequestType.KEY_VALUE, ResponseType.BOOLEAN), SCARD (RequestType.KEY, ResponseType.NUMBER), SISMEMBER (RequestType.KEY_VALUE, ResponseType.BOOLEAN), SINTER (RequestType.MULTI_KEY, ResponseType.MULTI_BULK), SINTERSTORE (RequestType.MULTI_KEY, ResponseType.STATUS), SUNION (RequestType.MULTI_KEY, ResponseType.MULTI_BULK), SUNIONSTORE (RequestType.MULTI_KEY, ResponseType.STATUS), SDIFF (RequestType.MULTI_KEY, ResponseType.MULTI_BULK), SDIFFSTORE (RequestType.MULTI_KEY, ResponseType.STATUS), SMEMBERS (RequestType.KEY, ResponseType.MULTI_BULK), SMOVE (RequestType.KEY_KEY_VALUE, ResponseType.BOOLEAN), SRANDMEMBER (RequestType.KEY, ResponseType.BULK), SPOP (RequestType.KEY, ResponseType.BULK), // Commands operating on sorted sets ZADD (RequestType.KEY_IDX_VALUE, ResponseType.BOOLEAN), ZREM (RequestType.KEY_VALUE, ResponseType.BOOLEAN), ZCARD (RequestType.KEY, ResponseType.NUMBER), ZSCORE (RequestType.KEY_VALUE, ResponseType.BULK), ZRANK (RequestType.KEY_VALUE, ResponseType.NUMBER), ZREVRANK (RequestType.KEY_VALUE, ResponseType.NUMBER), ZRANGE (RequestType.KEY_NUM_NUM, ResponseType.MULTI_BULK), /** ZRANGE with OPTIONS */ ZRANGE$OPTS (RequestType.KEY_NUM_NUM_OPTS, ResponseType.MULTI_BULK), ZREVRANGE (RequestType.KEY_NUM_NUM, ResponseType.MULTI_BULK), /** ZREVRANGE with OPTIONS */ ZREVRANGE$OPTS (RequestType.KEY_NUM_NUM_OPTS, ResponseType.MULTI_BULK), ZINCRBY (RequestType.KEY_IDX_VALUE, ResponseType.BULK), ZRANGEBYSCORE (RequestType.KEY_NUM_NUM, ResponseType.MULTI_BULK), ZRANGEBYSCORE$OPTS (RequestType.KEY_NUM_NUM_OPTS, ResponseType.MULTI_BULK), ZREMRANGEBYSCORE (RequestType.KEY_NUM_NUM, ResponseType.NUMBER), ZREMRANGEBYRANK (RequestType.KEY_NUM_NUM, ResponseType.NUMBER), ZCOUNT (RequestType.KEY_NUM_NUM, ResponseType.NUMBER), // Commands operating on bit sets SETBIT (RequestType.KEY_IDX_VALUE, ResponseType.NUMBER), GETBIT (RequestType.KEY_NUM, ResponseType.NUMBER), // Commands operating on hashes HSET (RequestType.KEY_KEY_VALUE, ResponseType.BOOLEAN), HGET (RequestType.KEY_VALUE, ResponseType.BULK), HEXISTS (RequestType.KEY_VALUE, ResponseType.BOOLEAN), HDEL (RequestType.KEY_VALUE, ResponseType.BOOLEAN), HLEN (RequestType.KEY, ResponseType.NUMBER), HKEYS (RequestType.KEY, ResponseType.MULTI_BULK), HVALS (RequestType.KEY, ResponseType.MULTI_BULK), HGETALL (RequestType.KEY, ResponseType.MULTI_BULK), HINCRBY (RequestType.KEY_KEY_NUM, ResponseType.NUMBER), // transactional commands MULTI (RequestType.NO_ARG, ResponseType.STATUS), EXEC (RequestType.NO_ARG, ResponseType.RESULT_SET), // NEED NEW RESPONSE TYPE DISCARD (RequestType.NO_ARG, ResponseType.STATUS), // "Multiple databases handling commands" SELECT (RequestType.KEY, ResponseType.STATUS), FLUSHDB (RequestType.NO_ARG, ResponseType.STATUS), FLUSHALL (RequestType.NO_ARG, ResponseType.STATUS), MOVE (RequestType.KEY_NUM, ResponseType.BOOLEAN), // Sorting SORT (RequestType.MULTI_KEY, ResponseType.MULTI_BULK), /** SORT...STORE */ SORT$STORE (RequestType.MULTI_KEY, ResponseType.NUMBER), // Persistence control commands SAVE (RequestType.NO_ARG, ResponseType.STATUS), BGSAVE (RequestType.NO_ARG, ResponseType.STATUS), BGREWRITEAOF(RequestType.NO_ARG, ResponseType.STRING), LASTSAVE (RequestType.NO_ARG, ResponseType.NUMBER), SHUTDOWN (RequestType.NO_ARG, ResponseType.VIRTUAL), // diagnostics commands ECHO (RequestType.VALUE, ResponseType.BULK), DEBUG (RequestType.KEY_KEY, ResponseType.STRING), // Remote server control commands INFO (RequestType.NO_ARG, ResponseType.BULK), MONITOR (RequestType.NO_ARG, ResponseType.VIRTUAL), // BUG: NOTE: TODO: not virtual .. SLAVEOF (RequestType.KEY_KEY, ResponseType.STATUS), ;// -- end -- /** semantic sugar */ public final String code; public final byte[] bytes; public final RequestType requestType; public final ResponseType responseType; private final int flags_bitset; /** internal */ static final public String OPTCODE = "$"; /** * Each enum member directly corresponds to a Redis command, per * specification. Command semantics is specified by the element * constructor params. * @param reqType the {@link RequestType} of the Command * @param respType the {@link ResponseType} of the Command */ Command (RequestType reqType, ResponseType respType, Flag... flags) { this.code = this.name(); if(code.indexOf(OPTCODE) > 0) this.bytes = code.substring(0, code.indexOf(OPTCODE)).getBytes(); else this.bytes = code.getBytes(); this.requestType = reqType; this.responseType = respType; if(flags != null && flags.length > 0) this.flags_bitset = Flag.bitset(flags); else this.flags_bitset = Flag.OPAQUE_BITMASK_32; } /** * Tests if the specified flag is set for the command. * @param flag the flag * @return true if flag is set. * @see Command.Flag */ final public boolean isSet(Flag flag) { return (flags_bitset & flag.bitmask) != Flag.OPAQUE_BITMASK_32; } // ------------------------------------------------------------------------ // Inner Types // ------------------------------------------------------------------------ /** * Redis Command Options and modifiers * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 20, 2010 * @since alpha.0 * */ public enum Option { WITHSCORES, BY, LIMIT, GET, ASC, DESC, ALPHA, STORE; /** semantic sugar */ public final byte[] bytes; Option () { this.bytes = name().getBytes(); } } // TODO: wtf is this doing here? /** * Defines (32 bit) flags for {@link Command} * * @author joubin (alphazero@sensesay.net) * @date Sep 10, 2010 * */ public enum Flag { TEST, FOO, BAR, ;// -- end -- public final int bitmask; private static final int OPAQUE_BITMASK_32 = 0x0000; Flag (){ this.bitmask = (int)Math.pow(2, ordinal()); } static final public int bitset(Flag...flags){ return bitset(OPAQUE_BITMASK_32, flags); } static final public int bitset(final int bitsetin, Flag...flags){ int bitset = bitsetin; for(Flag f : flags) bitset = bitset | f.bitmask; return bitset; } public static boolean isSet(int bitset, Flag flag) { return (bitset & flag.bitmask) > OPAQUE_BITMASK_32; } static final public int bitclear(final int bitsetin, Flag...flags){ int bitset = bitsetin; for(Flag f : flags) bitset = bitset ^ f.bitmask; return bitset; } } /** * Broad Request Type categorization of the Redis Command per the request's * argument signature. These categories are a more differentiated than the * Redis specification itself to impart further information about the argument * semantics. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Aug 29, 2009 * */ public enum RequestType { /** */ NO_ARG, /** */ KEY, /** */ VALUE, /** */ KEY_KEY, /** */ KEY_NUM, // /** */ // KEY_SPEC, /** */ KEY_NUM_NUM, /** */ KEY_NUM_NUM_OPTS, /** */ KEY_VALUE, /** */ KEY_KEY_VALUE, /** */ KEY_IDX_VALUE, /** */ KEY_CNT_VALUE, // TODO: this should be key value cnt ... /** */ MULTI_KEY, /** */ BULK_SET, KEY_KEY_NUM } /** * Broad Response Type categorization of the Redis Command responses. *

* As with {@link RequestType}, there is further differentiation of the * Redis response types to further inform the semantics. *

* Beyond that, there is also linkage between the {@link ResponseType} and * its associated {@link Response} interface extension. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Aug 29, 2009 * @see Response */ public enum ResponseType { /** */ NOP (StatusResponse.class), /** */ VIRTUAL (StatusResponse.class), /** */ STATUS (StatusResponse.class), /** */ QUEUED (StatusResponse.class), /** */ STRING (ValueResponse.class), /** */ BOOLEAN (ValueResponse.class), /** */ NUMBER (ValueResponse.class), /** */ BULK (BulkResponse.class), /** */ MULTI_BULK (MultiBulkResponse.class), /** */ RESULT_SET (Response.class), ; public Class respClass; /** * For each {@link ResponseType} member, we specify the * corresponding {@link Response} extension interface. * @param respClass */ ResponseType (Class respClass){ this.respClass = respClass; } } } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/CommandNotImplemented.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import org.jredis.ProviderException; import org.jredis._specification; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public final class CommandNotImplemented extends ProviderException { /** */ private static final long serialVersionUID = _specification.Version.major; /** * @param command */ public CommandNotImplemented (Command command) { super(command.code + " is not supported!");} } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/MultiBulkResponse.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import java.io.InputStream; import java.util.List; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface MultiBulkResponse extends Response { /** * @return the List of values returned from the server. List may contain null elements, * reflecting 'nil' values per redis specification. The operation {@link List#size()} will return * the same number that is received from the server, with the exception * * @throws ClientRuntimeException if data access is attempted before the response has been read, or, * if the provided {@link InputStream} presents any problems. * * @throws ProviderException for any other errors beyond system level (stream, network, etc.) or * user errors (such as attempting getData before the response has been read. */ public List getMultiBulkData () throws ClientRuntimeException, ProviderException; } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/Protocol.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.Message; /** *

* Protocol is effectively a {@link Message} factory. Implementations of this interface * provides Message objects that read and write according to a specific Redis protocol * specification. * *

Implementations may use this interface to address issues regarding buffer management. * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface Protocol { /** * * @param version * @return */ public boolean isCompatibleWithVersion (String version); /** * * @param cmd * @param args * @return * @throws ProviderException * @throws IllegalArgumentException */ public Request createRequest (Command cmd, byte[]...args) throws ProviderException, IllegalArgumentException; /** * Creates a response object for the {@link Command} specified. *

Note that this {@link Response} object has not yet been read. * @param cmd the {@link Command} that will be responded to. * * @return the response object that is ready to be read from the network connection. * * @throws ClientRuntimeException if the command is invalid for this version of the protocol * @throws ProviderException if the command is not implemented * * @see Response * @See {@link Response#read(java.io.InputStream)} */ public Response createResponse (Command cmd) throws ProviderException, ClientRuntimeException ; /** * EXPERIMENTAL * @param cmd * @param args * @return * @throws ProviderException * @throws IllegalArgumentException */ public byte[] createRequestBuffer(Command cmd, byte[]...args) throws ProviderException, IllegalArgumentException; public interface Factory { /** * Creates a {@link Protocol} instance for a connection per the specified * {@link ConnectionSpec} * @param connSpec * @return the new {@link Protocol} instance. * @throws NotSupportedException */ public Protocol newProtocol(ConnectionSpec connSpec) throws NotSupportedException; } } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/Request.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import org.jredis.connector.Message; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface Request extends Message {/* nop */} ================================================ FILE: core/api/src/main/java/org/jredis/protocol/Response.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import java.io.InputStream; import java.util.List; import org.jredis.ProviderException; import org.jredis.connector.Message; /** * [TODO: detail the requirements - this is wip.] * *

Response is a {@link Message} object that will read itself from a * {@link InputStream} upon demand. It is provided by {@link Protocol}s * as the result of a call to {@link Protocol#createResponse(Command)}. * * A Response is a generalize contract and does not provide the necessary semantics * corresponding to the data for the various response possibilities (such as bulk data * to a collection, value to bytes, etc.) * *

This specification also does not specify whether a response object can be * reused (to read the same command type response). This is left to the provider * of the implementation. If a provider does NOT wish to re-use responses, * they should raise a {@link ProviderException} in any subsequent calls to * {@link Response#read(InputStream)}. This class, however, does not and will * not provide a 'reset' means. * /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface Response extends Message { // ------------------------------------------------------------------------- // Properties // ------------------------------------------------------------------------- /** * @return true if response was an error response. */ public boolean isError (); /** * @return {@link Response.Type} of this response. */ public Type getType (); /** * @return */ public ResponseStatus getStatus(); /** * @return if response has been read. * @see Response#read(java.io.InputStream) */ public boolean didRead (); // ------------------------------------------------------------------------- // Associated type // ======================================================== Response.Type // ------------------------------------------------------------------------- /** * A redis server responds with: *

    *
  • Status response - such as {@link Command#SET} *
  • Value Data ("String") - such as {@link Command#GET} *
  • Bulk Data - such as {@link Command#KEYS} *
  • Multi Bulk Data - such as {@link Command#LRANGE} *
*

* This enum reflects these types and provides additional information regarding * the expected flavor of the response data. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public enum Type{ /** */ Status (ResponseStatus.class), /** */ Value (Integer.class), /** */ Bulk (byte[].class), /** */ MultiBulk (List.class); /** */ public final Class dataClass; /** @return the data flavor. */ public Class getDataClass() { return dataClass; } /** * @param clazz */ Type (Class clazz){ this.dataClass = clazz; } } } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/ResponseStatus.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public final class ResponseStatus { /** Status code enum -- error or ok */ public enum Code { OK, ERROR, CIAO } /** * Hopefully we have many none error response statuses * and we don't want to keep instantiating them. * Use this singleton instance for non-error status. */ public static final ResponseStatus STATUS_OK = new ResponseStatus (ResponseStatus.Code.OK); public static final ResponseStatus STATUS_CIAO = new ResponseStatus (ResponseStatus.Code.CIAO); /** */ private final Code code; /** */ private String msg; /** * @param code */ public ResponseStatus(ResponseStatus.Code code) { this.code = code; this.msg = ""; } /** * @param code * @param msg */ public ResponseStatus(ResponseStatus.Code code, String msg) { this.code = code; this.msg = msg; } public ResponseStatus.Code code() { return code; } public String message() {return msg; } public boolean isError () { return this.code==Code.ERROR; } } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/StatusResponse.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; /** * This is just a marker interface. All responses include an implied or explicit status. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface StatusResponse extends Response {/* nop */} ================================================ FILE: core/api/src/main/java/org/jredis/protocol/ValueResponse.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; /** * Formally, redis only returns "integers" as values, but in fact, an operation such as * {@link Command#RANDOMKEY} will return a key in a single line reply, and not a bulk reply. * (As of 04/02/09. Ex randomkey response => +woof ) * *

This class deals with all responses that are a single line responses distinct from 'status' * replies. For now, this means the result is either a UTF-8 String key or number. * * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public interface ValueResponse extends Response { /** * Redis number values are "64bit signed integers". * @return * @throws IllegalStateException */ public long getLongValue () throws IllegalStateException; /** * Its gone. Everything is 64 bit signed integer now. * This is deprecated but kept in the initial release just in case things change on the Redis side. * @return * @throws IllegalStateException */ // @Deprecated // public int getIntValue () throws IllegalStateException; /** * @return * @throws IllegalStateException */ public String getStringValue () throws IllegalStateException; /** * @return * @throws IllegalStateException */ public boolean getBooleanValue () throws IllegalStateException; } ================================================ FILE: core/api/src/main/java/org/jredis/protocol/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * This package contains the constructs that reflect the Redis Protocol Specification * * TODO: elaborate on the spec requirements. * * @author joubin (alphazero@sensesay.net) * * @since jredis-a.0 * @compliance redis-1.0 * */ package org.jredis.protocol; ================================================ FILE: core/api/src/main/java/org/jredis/resource/Context.java ================================================ package org.jredis.resource; import java.util.Set; /** * {@link Context} provides basic context support for {@link Resource}s, * by providing methods for {@link String} parameter name/values and basic * namespace operations for generic {@link Object} types. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 16, 2009 * @since alpha.0 * */ public interface Context { public String getParam (String key); public void setParam (String key, String value); Set getParamsKeys(); public Object get (String name); public void bind (String name, Object value); public void rebind (String name, Object value); } ================================================ FILE: core/api/src/main/java/org/jredis/resource/Resource.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.resource; /** * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 16, 2009 * @since alpha.0 * */ public interface Resource { /** * @return the resource type specific interface. */ public T getInterface(); /** * Sets the resource context. * * @see Context * @param context the context for this resource. * @throws ResourceException if the resource context provided is either insufficient, or, * if the new context (regardless of its utility) can not be used. (For example, it already * has a context and re-setting of contexts is not supported or permissible.) */ public void setContext (Context context) throws ResourceException; /** * @see Context * @return the context for this resource. * @throws ResourceException if the resource for whatever reason can not (or will not) * return a reference to its context. (Security considerations, for example.) */ public Context getContext () throws ResourceException; } ================================================ FILE: core/api/src/main/java/org/jredis/resource/ResourceException.java ================================================ package org.jredis.resource; import org.jredis.ClientRuntimeException; /** * The overall superclass for resource related exceptions, itself an * extension of ClientRuntimeException. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 16, 2009 * @since alpha.0 * */ public class ResourceException extends ClientRuntimeException { /** * @param message * @param cause */ public ResourceException(String message, Throwable cause) { super(message, cause); } /** * @param message */ public ResourceException(String message) { super(message); } /** */ private static final long serialVersionUID = _specification.Version.major; } ================================================ FILE: core/api/src/main/java/org/jredis/resource/_specification.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.resource; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 16, 2009 * @since alpha.0 * */ public interface _specification { /** specification level */ public interface Version { /** currently at toplevel spec version */ long major = org.jredis._specification.Version.major; /** currently at toplevel spec version */ long minor = org.jredis._specification.Version.minor; } } ================================================ FILE: core/api/src/main/java/org/jredis/resource/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * Optional package. *

* This package provides the necessary types to support non-trivial usage of JRedis * implementation artifacts as resources in the JRedis usage contexts. */ package org.jredis.resource; ================================================ FILE: core/api/src/main/java/org/jredis/semantics/BasicCodecManager.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.semantics; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jredis.Codec; // REFACTOR: this doesn't belong here // public final class BasicCodecManager implements CodecManager { private final Map, Codec> map = new ConcurrentHashMap, Codec>(); public void foo () { Codec stringCodec = null; map.put(String.class, stringCodec); } @SuppressWarnings("unchecked") @Override public Codec getCodec(Class type) { return (Codec) map.get(type); } @Override public boolean register(Codec code, Class type) { Codec existing = map.get(type); if(null == existing){ if (code.supports(type)){ map.put(type, code); return true; } } return false; } } ================================================ FILE: core/api/src/main/java/org/jredis/semantics/CodecManager.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.semantics; import org.jredis.Codec; import org.jredis.JRedis; /** * A CodecManager maintains a mapping of Java types to {@link Codec} instances * and can be used by a {@link JRedis} implementation to support the optional semantic * methods. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 14, 2009 * @since alpha.0 * */ public interface CodecManager { public Codec getCodec(Class type); public boolean register (Codec code, Class type); } ================================================ FILE: core/api/src/main/java/org/jredis/semantics/KeyCodec.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ // TODO: this is the wrong package -- we'll need something either under // package org.jredis.semantics; import org.jredis.Codec; /** * Encoding and decoding keys is a significant (but not major) performance bottleneck. *

* A KeyCodec is a {@link String} {@link Codec} that is tasked * with the optimal performance of encoding and decoding String keys * for use by connector implementations that support such extended * mechanisms. *

* This interface is projected to be implemented by a key caching * mechanism suitable for employment in usage scenarios where a finite * set of keys is repeatedly employed to accomplish domain specific tasks. *

* For example, a common Redis pattern is the use of atomic counters to * increment IDs. Whenever this pattern is used, the key for the ID(s) * will be repeatedly encoded and decoded by a non-optimized client * which can not make indiscriminate decisions regarding such optimizations. *

* A key cache, implementing {@link KeyCodec}, for example, can be used to indicate to * the client that for keys, the default codec (which is basically delegating to * {@link String}'s {@link String#getBytes()} and {@link String#String(byte[], int, int)}) * should not be used and that the client should use the object implementing this * to accomplish the same task. *

* This is just a marker interface beyond that. * * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ public interface KeyCodec extends Codec {/* nop */} ================================================ FILE: core/api/src/main/java/org/jredis/semantics/SemanticJRedis.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.semantics; import java.util.List; import java.util.Map; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.RedisType; /** * This interface is certainly optional, and as of now simply an idea * that needed to be put in place for review and feedback. * * [TODO: think about me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 14, 2009 * @since alpha.0 * */ public interface SemanticJRedis /* extends JRedis */ extends CodecManager { // ------------------------------------------------------------------------ // Semantic context methods // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Security and User Management // ------------------------------------------------------------------------ /** * Required for authorizing access to the server. This method implements * the AUTH command. It may be used with a non-secured server without * any side effect. * * @param authorization key as defined in the server. * @throws RedisException if the server is in secure more and the * authorization provided */ public JRedis auth (String authorization) throws RedisException; // ------------------------------------------------------------------------ // "Connection Handling" // ------------------------------------------------------------------------ /** * Ping redis * @return true (unless not authorized) * @throws RedisException (as of ver. 0.09) in case of unauthorized access */ public JRedis ping () throws RedisException; public void quit (); // ------------------------------------------------------------------------ // "Commands operating on string values" // ------------------------------------------------------------------------ public void set (String key, T value) throws RedisException; public boolean setnx (String key, T value) throws RedisException; public T get (String key) throws RedisException; public List mget(String key, String...moreKeys) throws RedisException; public long incr (String key) throws RedisException; public long incrby (String key, int delta) throws RedisException; public long decr (String key) throws RedisException; public long decrby (String key, int delta) throws RedisException; public boolean exists(String key) throws RedisException; public boolean del (String key) throws RedisException; public RedisType type (String key) throws RedisException; // ------------------------------------------------------------------------ // "Commands operating on the key space" // ------------------------------------------------------------------------ public List keys () throws RedisException; public List keys (String pattern) throws RedisException; public String randomkey() throws RedisException; public String rename (String oldkey, String newkey) throws RedisException; public boolean renamenx (String oldkey, String brandnewkey) throws RedisException; public long dbsize () throws RedisException; public boolean expire (String key, int ttlseconds) throws RedisException; // ------------------------------------------------------------------------ // Commands operating on lists // ------------------------------------------------------------------------ public void rpush (String listkey, T value) throws RedisException; public void lpush (String listkey, T value) throws RedisException; public void lset (String key, int index, T value) throws RedisException; public long lrem (String listKey, T value, int count) throws RedisException; public long llen (String listkey) throws RedisException; public List lrange (String listkey, int from, int to) throws RedisException; public void ltrim (String listkey, int keepFrom, int keepTo) throws RedisException; public T lindex (String listkey, int index) throws RedisException; public T lpop (String listKey) throws RedisException; public T rpop (String listKey) throws RedisException; // ------------------------------------------------------------------------ // Commands operating on sets // ------------------------------------------------------------------------ public boolean sadd (String setkey, T member) throws RedisException; public boolean srem (String setKey, T member) throws RedisException; public boolean sismember (String setKey, T member) throws RedisException; public long scard (String setKey) throws RedisException; public List sinter (String set1, String...sets) throws RedisException; public void sinterstore (String destSetKey, String...sets) throws RedisException; public List smembers (String setkey) throws RedisException; // ------------------------------------------------------------------------ // Multiple databases handling commands // ------------------------------------------------------------------------ public JRedis select (int index) throws RedisException; public JRedis flushdb () throws RedisException; public JRedis flushall () throws RedisException; public boolean move (String key, int dbIndex) throws RedisException; // ------------------------------------------------------------------------ // Sorting // ------------------------------------------------------------------------ public SemanticSort sort(String key); // ------------------------------------------------------------------------ // Persistence control commands // ------------------------------------------------------------------------ public void save() throws RedisException; public void bgsave () throws RedisException; public long lastsave () throws RedisException; public void shutdown () throws RedisException; //------------------------------------------------------------------------ //Remote server control commands //------------------------------------------------------------------------ public Map info () throws RedisException; } ================================================ FILE: core/api/src/main/java/org/jredis/semantics/SemanticQuery.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.semantics; import java.util.List; import org.jredis.RedisException; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 14, 2009 * @since alpha.0 * */ public interface SemanticQuery /*extends Query*/ { public List exec () throws IllegalStateException, RedisException; } ================================================ FILE: core/api/src/main/java/org/jredis/semantics/SemanticSort.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.semantics; import org.jredis.Sort; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 14, 2009 * @since alpha.0 * */ public interface SemanticSort extends Sort {/* nop */} ================================================ FILE: core/api/src/main/java/org/jredis/semantics/Semantics.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.semantics; import org.jredis.NotSupportedException; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 14, 2009 * @since alpha.0 * */ public interface Semantics extends CodecManager{ public SemanticJRedis forType (Class type) throws NotSupportedException; } ================================================ FILE: core/api/src/main/java/org/jredis/semantics/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * This package is optional. Its entirely debatable whether such mechanisms * have any legitimate place in JRedis, which is a connector to a type-less * system storing blobs of bytes. At this intial point, its place here simply * serves the purpose of keeping the issues that this package attempts to address * in mind. *

* When all is said and done, all these artifacts will provide little more than * what a helper or wrapper object will provide -- JRedis can not be in the * type management business, as it does not control the object store. */ package org.jredis.semantics; ================================================ FILE: core/api/src/main/resources/META-INF/redis-commands-2.4.n.txt ================================================ auth ping echo set setnx setex get del exists setbit getbit setrange getrange incr decr incrby decrby select randomkey keys dbsize lastsave save bgsave bgrewriteaof shutdown move rename renamenx lpush rpush lpushx rpushx linsert lpop rpop llen lindex lrange ltrim type lset sadd srem smove sismember scard spop srandmember sinter sinterstore sunion sunionstore sdiff sdiffstore sync flushdb flushall sort lrem rpoplpush info mget monitor expire expireat getset ttl persist slaveof debug mset msetnx zadd zincrby zrange zrangebyscore zrevrangebyscore zcount zrevrange zcard zrem zscore zremrangebyscore multi exec discard blpop brpop brpoplpush append strlen zrank zrevrank hset hsetnx hget hmset hmget hdel hlen zremrangebyrank zunionstore zinterstore hkeys hvals hgetall hexists config hincrby subscribe unsubscribe psubscribe punsubscribe publish watch unwatch object client ================================================ FILE: core/api/src/test/java/org/jredis/TestBase.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis; import static org.testng.Assert.fail; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.testng.Assert; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 10, 2010 * */ public class TestBase { // ------------------------------------------------------------------------ // Shared resources // ------------------------------------------------------------------------ /** */ final protected Log log = LogFactory.getLog(TestBase.class); // ------------------------------------------------------------------------ // Test support // ------------------------------------------------------------------------ /** * @param * @param test * @param errtype */ protected final void assertDidRaiseRuntimeError (Runnable test, Class errtype){ boolean didRaiseError = false; try { test.run(); } catch (RuntimeException t){ if(errtype.isAssignableFrom(t.getClass())) didRaiseError = true; } catch (Exception e){ fail("Unexpected exception", e); } finally { if(!didRaiseError) { fail("Failed to raise expected RuntimeError " + errtype.getCanonicalName()); } } } // ------------------------------------------------------------------------ // Test support - mildly enhanced TESTNG Assert semantics // ------------------------------------------------------------------------ // notNull public static void assertNotNull(Object object, String msgfmt, Object...optionalFmtArgs){ String message = String.format(msgfmt, optionalFmtArgs); Assert.assertNotNull (object, message); } // null public static void assertNull(Object object, String msgfmt, Object...optionalFmtArgs){ String message = String.format(msgfmt, optionalFmtArgs); Assert.assertNull (object, message); // << has bug. reports a boolean comp result -- TODO: fix and patch. } // equals public static void assertEquals(Object actual, Object expected, String msgfmt, Object...optionalFmtArgs){ String message = String.format(msgfmt, optionalFmtArgs); Assert.assertEquals (actual, expected, message); } public static void assertEquals(byte[] actual, byte[] expected, String msgfmt, Object...optionalFmtArgs){ String message = String.format(msgfmt, optionalFmtArgs); Assert.assertEquals (actual, expected, message); } // true/false public static void assertTrue(boolean condition, String msgfmt, Object...optionalFmtArgs){ String message = String.format(msgfmt, optionalFmtArgs); Assert.assertTrue (condition, message); } public static void assertFalse(boolean condition, String msgfmt, Object...optionalFmtArgs){ String message = String.format(msgfmt, optionalFmtArgs); Assert.assertFalse (condition, message); } } ================================================ FILE: core/api/src/test/java/org/jredis/compliance/CheckRedisCompliance.java ================================================ /* * Copyright 2009-2012 Joubin Houshyar * * This file is part of JRedis. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.compliance; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jredis.JRedis; import org.jredis.JRedisFuture; /** * Informal. A munge of redis.h is used to produce the canonical * Redis commands for a given Redis release. This program reflects * over JRedis to determine missing commands. *

* TODOS: *

  • need to address minor variations introduced for Java readability.
  • *
  • beef up annotations to tighten up the compliance check
  • *
  • apt cycle on release would be best
  • * * * @author Joubin Houshyar * @date: Feb 1, 2012 */ public class CheckRedisCompliance { // TODO: drive this via annotation on the JRedis interface static final String spec_file_path = "META-INF"; static final String spec_file_prefix = "redis-commands"; static final String spec_file_ext = "txt"; final int major; final int minor; final InputStream specfilein; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * @throws Exception */ public CheckRedisCompliance(int major, int minor) throws Exception { this.major = major; this.minor = minor; specfilein = getSpecFileInputStream(major, minor); } private void run() throws Exception { List cmdlist = getSpecCommandList(); checkAndReportCompliance(cmdlist, JRedis.class); checkAndReportCompliance(cmdlist, JRedisFuture.class); } private List checkAndReportCompliance(List cmdlist, Class jredis_class) { Method[] pubmethods = jredis_class.getMethods(); Set uniqueMethodNames = new HashSet(); for(Method m : pubmethods) uniqueMethodNames.add(m.getName()); List notsupported = new ArrayList(); for(String cmd : cmdlist) { if(!uniqueMethodNames.contains(cmd)) notsupported.add(cmd); } if(!notsupported.isEmpty()) { reportNonCompliance(notsupported, jredis_class.getSimpleName()); } return notsupported; } private void reportNonCompliance(List notsupported, String simpleName) { System.out.format("\n /// non-compliance report for %-12s //////////////\n", simpleName); int i = 0; for(String cmd : notsupported){ System.out.format("[%2d] %s\n",i++, cmd); } System.out.format("\n /////////////////////////////////////////////////////////\n", simpleName); } private List getSpecCommandList() throws IOException { List cmdlist = new ArrayList(); BufferedReader reader = new BufferedReader(new InputStreamReader(specfilein)); String cmd = null; while((cmd=reader.readLine()) != null){ cmdlist.add(cmd); } return cmdlist; } private static InputStream getSpecFileInputStream(int major, int minor) { String fname = String.format("%s%s%s-%d.%d.n.%s", spec_file_path, File.separator,spec_file_prefix, major, minor, spec_file_ext) .toString(); InputStream in = JRedis.class.getClassLoader().getResourceAsStream(fname); if(in == null){ String errmsg = String.format("No spec file found: <%s>", fname).toString(); throw new IllegalArgumentException(errmsg); } return in; } public static void main(String[] args) { try { new CheckRedisCompliance(2, 4).run(); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: core/api/src/test/java/org/jredis/connector/TestSpecElements.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.connector; import org.jredis.TestBase; import org.jredis.connector.Connection.Flag; import org.testng.annotations.Test; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 12, 2010 * */ public class TestSpecElements extends TestBase{ /** * [Doc this baby] */ @Test public void testConnectionFlags() { log.info("TEST:CONNECTOR spec semantics - ConnectionFlags"); Flag flags[] = {Flag.CONNECT_IMMEDIATELY, Flag.SHARED, Flag.RELIABLE}; int bitset = Flag.bitset(flags); for(Flag f : flags) assertTrue(Flag.isSet(bitset, f), "%s should have been set!\n", f.name()); int oldbitset = bitset; bitset = Flag.bitclear(bitset, flags[1]); assertFalse(bitset == oldbitset, "clearing flag should have changed bitset"); assertFalse(Flag.isSet(bitset, flags[1]), "%s should have been cleared!\n", flags[1].name() ); int bitset2 = 0x0000; bitset2 = Flag.bitset(bitset2, flags); for(Flag f : flags) assertTrue(Flag.isSet(bitset2, f), "%s should have been set!\n", f.name()); } /** * Test the equivalence of * {@link ConnectionSpec#setCredentials(byte[])} and {@link ConnectionSpec#setCredentials(String)} * using {@link ConnectionSpec#getCredentials()} */ @Test public void testCredentialsOverloads () { String property = Connection.Property.CREDENTIAL.name(); log.info(String.format("TEST:CONNECTOR spec semantics - Credentials %s", property)); // check with actual passwords // String password = "jredis"; byte[] credentials = password.getBytes(); ConnectionSpec spec = new ConnectionSpec.RefImpl(); assertNull(spec.getCredentials(), "RefImpl should not have defined: %s", property); spec.setCredentials(password.getBytes()); byte[] credentials_1 = spec.getCredentials(); assertNotNull(credentials_1, "RefImpl.setCredentials (byte[]) did not set: %s", property); // use the String variant // spec.setCredentials(password); byte[] credentials_2 = spec.getCredentials(); assertNotNull(credentials_2, String.format("RefImpl.setCredentials(String) did not set: %s", property)); // compare them assertEquals(credentials_2, credentials_1, String.format("Overloaded methods for %s setter are not equivalent.", property)); // check for treatment of empty strings -- should be equivalent to null byte[] // spec = new ConnectionSpec.RefImpl(); // ASSUMPTION: tested above for null default value. password = ""; spec.setCredentials(password); assertNull(spec.getCredentials(), "RefImpl should not have set \"\" (empty) credential to non-null value: %s", property); } } ================================================ FILE: core/api/src/test/java/org/jredis/protocol/TestCommand.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.protocol; import org.jredis.TestBase; import static org.jredis.protocol.Command.Flag.*; import org.jredis.protocol.Command.Flag; import org.testng.Assert; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 11, 2010 * */ public class TestCommand extends TestBase { @Test public void testCommandSemanticsRequestType () { log.info("TEST:PROTOCOL Command sematics - RequestType"); for(Command c : Command.values()){ assertTrue(new String(c.bytes).indexOf(Command.OPTCODE) == -1, "Command bytes must not include control characters."); } } @Test public void testCommandFlags() { log.info("TEST:PROTOCOL Command sematics - CommandFlags"); Flag flags[] = {TEST, FOO, BAR}; int bitset = Flag.bitset(flags); for(Flag f : flags) Assert.assertTrue(Flag.isSet(bitset, f), String.format("%s should have been set!\n", f.name())); int oldbitset = bitset; bitset = Flag.bitclear(bitset, flags[1]); Assert.assertFalse(bitset == oldbitset, "clearing flag should have changed bitset"); Assert.assertFalse(Flag.isSet(bitset, flags[1]), String.format("%s should have been cleared!\n", flags[1].name())); } } ================================================ FILE: core/api/src/test/resources/log4j.properties ================================================ # Configure logging for testing log4j.rootLogger=DEBUG, stdout, logfile log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.RollingFileAppender log4j.appender.logfile.File=target/log/jredis-api-test.log log4j.appender.logfile.MaxFileSize=512KB log4j.appender.logfile.MaxBackupIndex=1 # Pattern to output: date priority [category] - message log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %-5p [%c] - %m%n ================================================ FILE: core/bench/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: core/bench/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: core/bench/pom.xml ================================================ 4.0.0 org.jredis jredis-core a.0-SNAPSHOT JRedis - Core - BENCH org.jredis jredis-core-bench a.0-SNAPSHOT jar org.jredis jredis-core-api a.0-SNAPSHOT org.jredis jredis-core-ri a.0-SNAPSHOT maven-assembly-plugin simple-install package attached jar-with-dependencies ================================================ FILE: core/bench/src/main/java/org/jredis/bench/JRedisBenchmark.java ================================================ /* * Copyright 2009 Joubin Mohammad Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.bench; import static org.jredis.bench.Util.getRandomString; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; //import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; //import org.jredis.RedisException; //import org.jredis.bench.Util.Timer; //import org.jredis.protocol.Command; // import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.bench.Util.Timer; import org.jredis.protocol.Command; /** * Runs a few benchmarks for the {@link SocketConnection}, using a concurrent set of workers, each with its own * unique connection to the Redis server. * * @author Joubin Houshyar (alphazero@sensesay.net) */ public abstract class JRedisBenchmark { /** password used to AUTH with redis select -- password is: jredis */ public static final String password = "jredis"; // ------------------------------------------------------------------------ // Helper methods // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // For test data // ------------------------------------------------------------------------ final static Random random = new Random(System.currentTimeMillis()); /** */ byte[] fixedbytes; /** */ static List stringList = new ArrayList(); protected boolean quitOnRunEnd = true; /** * @param b */ protected void quitOnRunEnd (boolean flag) { this.quitOnRunEnd = flag; } // ------------------------------------------------------------------------ // Extension Points // ------------------------------------------------------------------------ protected abstract Class getImplementationClass(); /** * Define this method to test a specific JRedis implementation. * * @param host * @param port * @return */ protected abstract JRedis newConnection(String host, int port, int db, String password) throws ClientRuntimeException; /** * Runs a set of command primitives test runs using concurrent clients. [TODO: add all commands - this is a sampling of a few.] *

    Be advised that this will FLUSH the db specified. Defaults to database at index 13: i.e. jredis.select(13).flushdb(). * This bench will use AUTH to make sure you do not accidentally destroy important data * when runnign the benchmark. (If you don't have a password on your db, it can't be that important, right?) * *

    Further note that this will use the password jredis so either * make sure the redis.conf requirepass is set appropriately or simply comment it out. * * @param host * @param port * @param threadCnt * @param reqCnt * @param size * @param db */ protected final void runBenchmarks(String host, int port, int threadCnt, int reqCnt, int size, int db) { // random = new Random(System.currentTimeMillis()); fixedbytes = new byte[size]; random.nextBytes(fixedbytes); /** setup data */ for(int i=0; i values = JRedisBenchmark.stringList; String key = id + ":stringList:set"; @Override protected void prep() { try { for(int i=0; i %d total requests @ %f seconds\n", threadCnt*reqCnt, (float)timer.deltaAtMark(TimeUnit.SECONDS)); System.out.format(" ==> %f/second\n", (float)timer.opsPerSecAtMark((long)threadCnt*reqCnt)); System.out.println(); // report for each - this is response time long max = Long.MIN_VALUE; long min = Long.MAX_VALUE; for(int i=0; imax ? deltas[i] : max; } System.out.format("\t\t\tmin: %s msecs\n\t\t\tmax: %s msecs\n\n", min, max); } catch (Exception e) { e.printStackTrace(); } } } // ------------------------------------------------------------------------ // Base for all workers // ------------------------------------------------------------------------ /** * Abstract base class for all benchmark workers. This is a runnable that * uses the template pattern in the run method and takes care of common * time keeping tasks for the specific (child class) workers. */ public abstract class BenchmarkWorker implements Runnable { final String host; final int port; long[] deltas; CountDownLatch ready; CountDownLatch completion; CountDownLatch mark; int reqCnt; int id; int db; JRedis jredis = null; public BenchmarkWorker (String host, int port, int db) { this.host = host; this.port = port; this.db = db; } /** *

    1 - connects to the redis server *

    2 - calls the {@link BenchmarkWorker#prep()} -- subclasses will implement this *

    3 - waits for the mark signal to start the calls *

    4 - gets the sys time, and .. *

    5 - calls the {@link BenchmarkWorker#work()} method -- subclasses will implement this *

    6 - gets the sys time -- this is the time delta for this worker *

    7 - closes the connection, and signals completion to the benchmarker. */ public void run() { try { jredis = newConnection (host, 6379, db, password); try { // jredis.auth (password).select(db).flushdb(); jredis.flushdb(); } catch (RedisException e) { System.err.format("BENCHMARK::REDIS %s ERROR => %s\nWorker will stop.\n", e.getCommand(), e.getLocalizedMessage()); System.exit (1); } prep(); ready.countDown(); mark.await(); // wait sig long start=0; start = System.currentTimeMillis(); work(); deltas[id] = System.currentTimeMillis() - start; if(quitOnRunEnd) jredis.quit(); completion.countDown(); // sig done } catch (Exception e){ System.err.format("BENCHMARK::Exception => %s\nWill stop.\n", e.getLocalizedMessage()); } } /** anything that needs to be done to setup the calls -- typically all time consuming data setup tasks go here ...*/ protected abstract void prep(); /** Typically just a tight loop calling the redis server for the given test and using the data prep'd in {@link BenchmarkWorker#prep()} */ protected abstract void work(); } } ================================================ FILE: core/bench/src/main/java/org/jredis/bench/JRedisJProfileSubject.java ================================================ /* * Copyright 2009 Joubin Mohammad Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.bench; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 11, 2009 * @since alpha.0 * */ public class JRedisJProfileSubject { private final JRedis jredis; public JRedisJProfileSubject (JRedis jredis){ this.jredis = jredis; } protected JRedisJProfileSubject () { jredis = null; } /** * In a tight loop, we execute a few select * commands that touch the various permutations of * request complexity, and response type, so that * we can pinpoint the bottlenecks and the general * runtime characteristics of the JRedic provider. * @throws RedisException */ public void run () throws RedisException { Log.log("***** JProfileTestCase ****"); // jredis.auth("jredis").ping().flushall(); int iter = 100000; String key = "foostring"; String cntrkey = "foocntr"; String listkey = "foolist"; String setkey = "fooset"; byte[] data = "meow".getBytes(); long start = System.currentTimeMillis(); for(Long j=0L; jsize using the default * encoding scheme. String content is ASCII characters. * @param size of the string (character count) * @return the generated {@link String} */ static public String getRandomString (int size) { StringBuilder builder = new StringBuilder(size); for(int i = 0; isize. * @param size of the generated array. * @return the generated array */ static public byte[] getRandomBytes(int size) { int len = size; byte[] bigstuff = new byte[len]; random.nextBytes(bigstuff); return bigstuff; } // ------------------------------------------------------------------------ // Inner types // ------------------------------------------------------------------------ /** * A timer utility for benchmarking. Internally uses {@link TimeUnit#MILLISECONDS}. */ public static final class Timer { final static public TimeUnit UNIT = TimeUnit.MILLISECONDS; final long startTime = System.currentTimeMillis(); private long markTime = startTime; private long delta = 0; private Timer () {} public static final Timer startNewTimer() { return new Timer (); } public long mark () { markTime = now(); delta = markTime - startTime; return delta; } public static final long now() { return System.currentTimeMillis(); } /** * @param keyCount * @return */ public float opsPerSecAtDelta (long opCount, long delta) { return (UNIT.convert(1, TimeUnit.SECONDS)*opCount)/(float)delta; } public float opsPerSecAtMark (long opCount) { return opsPerSecAtDelta(opCount, deltaAtMark()); } /** * @return the elapsed time since call to {@link Timer#mark} in the timer's TimeUnit */ public long deltaAtMark () { return delta;} /** * @param unit * @return the elapsed time since call to {@link Timer#mark} in the specified unit. * If the unit provided is finer than the timer's internal time unit, finer precision will * naturally be irrelevant. */ public long deltaAtMark (TimeUnit unit){ return unit.convert(deltaAtMark(), UNIT); } } } ================================================ FILE: core/bench/src/main/java/org/jredis/ri/alphazero/bench/JRedisClientBenchmark.java ================================================ /* * Copyright 2009 Joubin Mohammad Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.bench; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.bench.JRedisBenchmark; import org.jredis.ri.alphazero.JRedisClient; /** * Not fully baked ... just a hack to get things going. * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/10/09 * @since alpha.0 * */ public class JRedisClientBenchmark extends JRedisBenchmark { /** * Runs the benchmark tests. * [TODO: lets do proper usage here and clean this up.] * * Currently, uses 50 concurrent connections and 5000 requests / each connection. * TODO: munch on some commandline args ... * @param args */ public static void main(String[] args) { String host = "127.0.0.1"; int port = 6379; int size = 3; int workerCnt = 10; int reqCnt = 20000; int db = 13; if(args.length > 0) db = Integer.valueOf (args[0]); if(args.length > 1) workerCnt = Integer.valueOf(args[1]); if(args.length > 2) reqCnt = Integer.valueOf(args[2]); if(args.length > 3) size = Integer.parseInt(args[3]); if(args.length > 4) host = args[4]; System.out.format("==> Usage: [db [conn [req [size [host]]]]\n"); new JRedisClientBenchmark().runBenchmarks (host, port, workerCnt, reqCnt, size, db); } @Override protected final JRedis newConnection(String host, int port, int db, String password) throws ClientRuntimeException { return new JRedisClient(host, port, password, db); } @Override protected final Class getImplementationClass() { return JRedisClient.class; } } ================================================ FILE: core/bench/src/main/java/org/jredis/ri/alphazero/bench/JRedisClientJProfileSubject.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.bench; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.bench.JRedisJProfileSubject; import org.jredis.ri.alphazero.JRedisClient; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 24, 2009 * @since alpha.0 * */ public class JRedisClientJProfileSubject extends JRedisJProfileSubject{ public static void main(String[] args) throws RedisException { JRedis jredis; jredis = new JRedisClient("localhost", 6379, "jredis", 0); new JRedisJProfileSubject (jredis).run(); } } ================================================ FILE: core/bench/src/main/java/org/jredis/ri/alphazero/bench/JRedisPipelineServiceBenchmark.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.ri.alphazero.bench; // //import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; //import org.jredis.bench.JRedisBenchmark; //import org.jredis.connector.ConnectionSpec; //import org.jredis.ri.alphazero.JRedisPipelineService; //import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; // ///** // * [TODO: document me!] // * // * @author Joubin Houshyar (alphazero@sensesay.net) // * @version alpha.0, Nov 6, 2009 // * @since alpha.0 // * // */ // //public class JRedisPipelineServiceBenchmark extends JRedisBenchmark { // public static void main(String[] args) { //// host = "192.168.1.222"; // String host = "127.0.0.1"; // String password = "jredis"; // int port = 6379; // int size = 3; // int workerCnt = 100; // int reqCnt = 1000; // int db = 13; // if(args.length > 0) db = Integer.valueOf (args[0]); // if(args.length > 1) workerCnt = Integer.valueOf(args[1]); // if(args.length > 2) reqCnt = Integer.valueOf(args[2]); // if(args.length > 3) size = Integer.parseInt(args[3]); // if(args.length > 4) host = args[4]; // // System.out.format("==> Usage: [db [conn [req [size [host]]]]\n"); // // new JRedisPipelineServiceBenchmark(host, port, db, password).runBenchmarks (host, port, workerCnt, reqCnt, size, db); // } // final JRedis jredisService; // public JRedisPipelineServiceBenchmark (String host, int port, int db, String password) { // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec("localhost", 6379, db, "jredis".getBytes()); // jredisService = new JRedisPipelineService(connectionSpec); // super.quitOnRunEnd(false); // } // @Override // protected final JRedis newConnection (String host, int port, int db, String password) throws ClientRuntimeException { // return jredisService; // } // @Override // protected final Class getImplementationClass() { // return JRedisPipelineService.class; // } //} ================================================ FILE: core/bench/src/main/java/org/jredis/ri/alphazero/bench/JRedisServiceBenchmark.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.ri.alphazero.bench; // //import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; //import org.jredis.bench.JRedisBenchmark; //import org.jredis.connector.ConnectionSpec; //import org.jredis.ri.alphazero.JRedisService; //import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; // ///** // * [TODO: document me!] // * // * @author Joubin (alphazero@sensesay.net) // * @version alpha.0, Sep 2, 2009 // * @since alpha.0 // * // */ // //public class JRedisServiceBenchmark extends JRedisBenchmark { // public static void main(String[] args) { //// host = "192.168.1.222"; // String host = "127.0.0.1"; // String password = "jredis"; // int port = 6379; // int size = 3; // int workerCnt = 100; // int poolCnt = 80; // int reqCnt = 1000; // int db = 13; // if(args.length > 0) db = Integer.valueOf (args[0]); // if(args.length > 1) workerCnt = Integer.valueOf(args[1]); // if(args.length > 2) reqCnt = Integer.valueOf(args[2]); // if(args.length > 3) size = Integer.parseInt(args[3]); // if(args.length > 4) host = args[4]; // // System.out.format("==> Usage: [db [conn [req [size [host]]]]\n"); //// System.out.format("*** host: %s:%d (db: %d) | datasize: %d | connections: %d | request/conn: %d \n\n", host, port, db, size ,connectionCnt, reqCnt); // // new JRedisServiceBenchmark(poolCnt, host, port, db, password).runBenchmarks (host, port, workerCnt, reqCnt, size, db); // } // // final JRedis jredisService; // public JRedisServiceBenchmark (int poolCnt, String host, int port, int db, String password) { // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec("localhost", 6379, db, "jredis".getBytes()); // jredisService = new JRedisService(connectionSpec, poolCnt); // super.quitOnRunEnd(false); // } // @Override // protected final JRedis newConnection (String host, int port, int db, String password) throws ClientRuntimeException { // return jredisService; // } // @Override // protected final Class getImplementationClass() { // return JRedisService.class; // } // //} ================================================ FILE: core/bench/src/main/java/org/jredis/ri/alphazero/bench/SimpleBenchJRedisAsync.java ================================================ package org.jredis.ri.alphazero.bench; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisAsyncClient; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; public class SimpleBenchJRedisAsync { public static void main(String[] args) { new SimpleBenchJRedisAsync().run(); } private void run() { int database = 11; ConnectionSpec connSpec = DefaultConnectionSpec.newSpec("localhost", 6379, database, "jredis".getBytes()); JRedisFuture jredis = new JRedisAsyncClient(connSpec); byte[] key = "bench-jredis-pipeline-key".getBytes(); int iters = 100 * 1000; try { cleandb(jredis); } catch (Throwable e) { e.printStackTrace(); return; } for(;;){ Future frCounter = null; long start = System.nanoTime(); for(int i=0;i frCounter = null; long start = System.nanoTime(); for(int i=0;i fr = jredis.flushdb(); jredis.flush(); fr.get(); } } ================================================ FILE: core/bench/src/main/java/org/jredis/ri/alphazero/bench/SimpleBenchJRedisClient.java ================================================ package org.jredis.ri.alphazero.bench; import java.util.concurrent.TimeUnit; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisClient; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; public class SimpleBenchJRedisClient { public static void main(String[] args) { try { new SimpleBenchJRedisClient().run(); } catch (RedisException e) { e.printStackTrace(); } } private void run() throws RedisException { int database = 11; ConnectionSpec connSpec = DefaultConnectionSpec.newSpec("localhost", 6379, database, "jredis".getBytes()); JRedis jredis = new JRedisClient(connSpec); byte[] key = "bench-jredis-pipeline-key".getBytes(); int iters = 100 * 1000; try { cleandb(jredis); } catch (Throwable e) { e.printStackTrace(); return; } for(;;){ Long counter = null; long start = System.nanoTime(); for(int i=0;i frCounter = null; long start = System.nanoTime(); for(int i=0;i 4.0.0 org.jredis jredis a.0-SNAPSHOT JRedis - Core [Build POM] org.jredis jredis-core a.0-SNAPSHOT pom api ri bench all org.apache.maven.plugins maven-surefire-plugin 2.11 jredis.test.host localhost jredis.test.port 6379 jredis.test.password jredis jredis.test.db.1 13 jredis.test.db.2 10 jredis.test.datasize.small 128 jredis.test.datasize.medium 2048 jredis.test.datasize.large 524288 jredis.test.cnt.small 10 jredis.test.cnt.medium 1000 jredis.test.cnt.large 100000 jredis.test.expire.secs 1 jredis.test.expire.wait.millisecs 2000 jredis.service.connection.cnt 5 Apache 2 http://www.apache.org/licenses/LICENSE-2.0.txt all Copyright 2009 (c) Joubin Houshyar - All Right Resererved joubin Joubin Houshyar alphazero@sensesay.net http://www.sensesay.net ReleaseManager Designer Developer UTC-5 ================================================ FILE: core/ri/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: core/ri/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: core/ri/pom.xml ================================================ 4.0.0 org.jredis jredis-core a.0-SNAPSHOT JRedis - Core - RI org.jredis jredis-core-ri a.0-SNAPSHOT jar org.jredis jredis-core-api a.0-SNAPSHOT org.testng testng 7.7.0 test log4j log4j 1.2.12 commons-logging commons-logging 1.1.1 maven-assembly-plugin 2.1 simple-install package attached jar-with-dependencies ================================================ FILE: core/ri/src/main/java/org/jredis/ri/RI.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 17, 2010 * */ public interface RI { public enum Release { ALPHA, BETA, FINAL } @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PACKAGE, ElementType.CONSTRUCTOR, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Version { int major() default 2; int minor() default 0; Release release() default Release.ALPHA; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/BulkSetMapping.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.jredis.KeyValueSet; import org.jredis.ri.alphazero.semantics.DefaultKeyCodec; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.semantics.KeyCodec; public abstract class BulkSetMapping implements KeyValueSet{ private final Map map = new HashMap(); abstract byte[] toBytes(T value) ; public byte[][] getMappings () { KeyCodec codec = DefaultKeyCodec.provider(); byte[][] mappings = new byte[map.size()*2][]; int i = 0; for (Entry e : map.entrySet()){ mappings[i++] = codec.encode(e.getKey()); mappings[i++] = toBytes(e.getValue()); } return mappings; } public KeyValueSet add (K key, T value) { map.put(key, value); return this; } // public static KeyValueSet.ByteArrays newByteArrayKVSet() { return new BulkSetMapping.Bytes(); } final static class Bytes extends BulkSetMapping implements KeyValueSet.ByteArrays { byte[] toBytes(byte[] value) { return value;} } public static KeyValueSet.Strings newStringKVSet() { return new BulkSetMapping.Strings(); } final static class Strings extends BulkSetMapping implements KeyValueSet.Strings { byte[] toBytes(String value) { return DefaultKeyCodec.provider().encode(value);} } public static KeyValueSet.Numbers newNumberKVSet() { return new BulkSetMapping.Numbers(); } final static class Numbers extends BulkSetMapping implements KeyValueSet.Numbers { byte[] toBytes(Number value) { return String.valueOf(value).getBytes();} } public static KeyValueSet.Objects newObjectKVSet() { return new BulkSetMapping.Objects(); } final static class Objects extends BulkSetMapping implements KeyValueSet.Objects { byte[] toBytes(T value) { return DefaultCodec.encode(value);} } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisAsyncClient.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.FaultedConnection; import org.jredis.connector.Connection.Modality; import org.jredis.connector.Connection.Property; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 6, 2009 * @since alpha.0 * */ public class JRedisAsyncClient extends JRedisFutureSupport { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** */ final private Connection connection; /** */ final protected ConnectionSpec connSpec; // ------------------------------------------------------------------------ // Construct and initialize // ------------------------------------------------------------------------ /** * @param connectionSpec */ public JRedisAsyncClient (ConnectionSpec connectionSpec) { // note: using a shared connection mod connSpec = Assert.notNull(connectionSpec, "ConnectionSpec 'connectionSpec'", ClientRuntimeException.class); connectionSpec.setConnectionFlag(Connection.Flag.RELIABLE, Boolean.TRUE); // REVU: TODO: review all these spot mods. connectionSpec.setConnectionFlag(Connection.Flag.SHARED, Boolean.FALSE); // REVU: TODO: review all these spot mods. connectionSpec.setConnectionFlag(Connection.Flag.PIPELINE, Boolean.FALSE); // REVU: TODO: review all these spot mods. connectionSpec.setModality(Modality.Asynchronous); // connection = new AsyncConnection(connectionSpec, true); connection = createAsyncConnection(); } // ------------------------------------------------------------------------ // Internal ops // ------------------------------------------------------------------------ final private Connection createAsyncConnection() { Connection.Factory cfact = (Connection.Factory) connSpec.getConnectionProperty(Property.CONNECTION_FACTORY); Connection conn = null; try { conn = Assert.notNull(cfact.newConnection(connSpec), "connection delegate", ClientRuntimeException.class); } catch (ProviderException e) { Log.bug("Couldn't create the handler delegate. => " + e.getLocalizedMessage()); throw e; } catch (ClientRuntimeException e) { String msg = String.format("%s\nMake sure your server is running.", e.getMessage()); Log.error ("Error creating connection -> " + e.getLocalizedMessage()); conn = new FaultedConnection(connSpec, msg); } Log.debug ("%s: Using %s", this.getClass().getSimpleName(), conn); return conn; } // ------------------------------------------------------------------------ // Super overrides // ------------------------------------------------------------------------ /** * Requests to server are queued at this point. Any requests after a {@link Command#QUIT} will * raise an exception indicating the pipeline is shutting down. * @see org.jredis.ri.alphazero.JRedisFutureSupport#queueRequest(org.jredis.protocol.Command, byte[][]) */ protected Future queueRequest (Command cmd, byte[]...args) throws ClientRuntimeException, ProviderException { return connection.queueRequest(cmd, args); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisChunkedPipeline.java ================================================ /* * Copyright 2009-2012 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.JRedisFuture; import org.jredis.ProviderException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.connection.ChunkedPipelineConnection; /** * An asynchronous pipeline supporting {@link JRedisFuture} api, * utilizing the {@link ChunkedPipelineConnection}, providing * maximal throughput as a forwarding pipe to Redis server. * * Thread safe. Use only one instance per application. * * * @author Joubin * */ public class JRedisChunkedPipeline extends JRedisFutureSupport { final private Connection connection; public JRedisChunkedPipeline(ConnectionSpec spec){ assert spec != null : "spec is null"; connection = new ChunkedPipelineConnection(spec); } @Override protected Future queueRequest(Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { return connection.queueRequest(cmd, args); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisClient.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.net.UnknownHostException; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.ProviderException; import org.jredis.Redis; import org.jredis.RedisException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Assert; /** * [TODO: check documentation and make necessary changes made during refactoring] * * A basic client, using {@link SocketConnection} and handler delegate *

    * This class is simply an assembly of various other components that address distinct * concerns of a JRedis connection, and effectively defines the connection policy * and use-case patterns by selecting this set of cooperating elements. *

    * This is a simple client object suitable for long-held open connections * to a redis server by single threaded applications. *

    * The components that are used ARE NOT thread-safe and assume * synchronised/sequential access to the api defined by {@link JRedis}. * Both the connection and protocol handler delegates of this class are intended for use * by a single thread, or strictly sequential access by a pool of threads. You can * create multiple instances of this class and use dedicated threads for each to create a * service (if you wish). *

    * Redis protocol is handled by a {@link Protocol} instance obtained from the {@link ProtocolManager}. * This class will by default specify the {@link RedisVersion#current_revision}. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Aug 13, 2009 * @since alpha.0 * */ /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Aug 13, 2009 * @since alpha.0 * */ @Redis(versions={"1.00"}) public class JRedisClient extends SyncJRedisBase { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ private Connection connection; // ------------------------------------------------------------------------ // Construct and initialize // ------------------------------------------------------------------------ public JRedisClient (ConnectionSpec connectionSpec){ connectionSpec.setConnectionFlag(Connection.Flag.RELIABLE, Boolean.TRUE); connectionSpec.setConnectionFlag(Connection.Flag.SHARED, Boolean.FALSE); Connection syncConnection = createSyncConnection (connectionSpec); setConnection (syncConnection); } /** * Connects to the localhost:6379 redis server using the password. * Will select db 0. * @param password used for AUTH * @throws UnknownHostException * @throws ClientRuntimeException */ public JRedisClient (String password) throws ClientRuntimeException { this ("localhost", 6379, password, 0); } /** * and using localhost:6379 as its network addressing parameters. * Database will be selected to db 0 * Assumes no password required. * @throws UnknownHostException * @throws ClientRuntimeException */ public JRedisClient ( ) throws ClientRuntimeException { this ("localhost", 6379, null, 0); } /** * * @param host * @param port * @throws UnknownHostException * @throws ClientRuntimeException */ public JRedisClient(String host, int port) throws ClientRuntimeException { // this(host, port, null, 0, RedisVersion.current_revision); this(host, port, null, 0); } /** * New JRedisClient for the default protocol version {@link RedisVersion} * @param host redis server's host * @param port redis server's port * @param password to use for AUTHentication (can be null) * @param database database to select on connect * @throws ClientRuntimeException * @throws UnknownHostException */ public JRedisClient (String host, int port, String password, int database) throws ClientRuntimeException { this(DefaultConnectionSpec.newSpec(host, port, database, getCredentialBytes(password))); } // ------------------------------------------------------------------------ // Super overrides // ------------------------------------------------------------------------ @Override protected Response serviceRequest(Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { return connection.serviceRequest(cmd, args); } // TODO: what's the use of this? @Override protected final void setConnection (Connection connection) { this.connection = Assert.notNull(connection, "connection on setConnection()", ClientRuntimeException.class); } // ------------------------------------------------------------------------ // Interface // =========================================================== Resource /* * Provides basic Resource support without any state management. Extensions * that use context in a simply manner can rely on these methods. Others may * wish to override. */ // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.resource.Resource#getInterface() */ @Override public JRedis getInterface() { return this; } // ------------------------------------------------------------------------ // Static Utils // ------------------------------------------------------------------------ /** * Internal use only - * @param password * @return the bytes of password or null if none spec'd */ private static byte[] getCredentialBytes (String password){ return (null != password ? password.getBytes() : null); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisFutureSupport.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.jredis.ClientRuntimeException; import org.jredis.JRedisFuture; import org.jredis.KeyValueSet; import org.jredis.ObjectInfo; import org.jredis.ProviderException; import org.jredis.RedisType; import org.jredis.Sort; import org.jredis.ZSetEntry; import org.jredis.connector.Connection; import org.jredis.protocol.BulkResponse; import org.jredis.protocol.Command; import org.jredis.protocol.MultiBulkResponse; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; import org.jredis.protocol.ValueResponse; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.ri.alphazero.support.SortSupport; /** * * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 10, 2009 * @since alpha.0 * */ public abstract class JRedisFutureSupport implements JRedisFuture { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Extension point(s) /* * This class provides the convenience of a uniform implementation wide mapping * of JRedis api semantics to the native protocol level semantics of byte[]s. * * Extensions can use the provided extension points to provide or delegate the * servicing of request calls. */ // ------------------------------------------------------------------------ /** * This method mimics the eponymous {@link Connection#queueRequest(Command, byte[]...)} * which defines the blocking api semantics of Synchronous connections. The extending class * can either directly (a) implement the protocol requirements, or, (b) delegate to a * {@link Connection} instance, or, (c) utilize a pool of {@link Connection}s. * * @param cmd * @param args * @return * @throws ClientRuntimeException * @throws ProviderException */ abstract protected Future queueRequest (Command cmd, byte[]...args) throws ClientRuntimeException, ProviderException; // ------------------------------------------------------------------------ // INTERFACE // ================================================================ Redis /* * Support of all the JRedisFuture interface methods. * * This class uses the UTF-8 character set for all conversions due to its * use of the Convert and Codec support classes. * * All calls are forwarded to an abstract queueRequest method that the * extending classes are expected to implement. */ // ------------------------------------------------------------------------ @Override public FutureStatus bgsave() { return new FutureStatus(this.queueRequest(Command.BGSAVE)); } @Override public FutureString bgrewriteaof() { Future futureResponse = this.queueRequest(Command.BGREWRITEAOF); return new FutureString(futureResponse); } @Override public FutureStatus ping() { return new FutureStatus(this.queueRequest(Command.PING)); } @Override public FutureStatus flushall() { return new FutureStatus(this.queueRequest(Command.FLUSHALL)); } @Override public FutureStatus flushdb() { return new FutureStatus(this.queueRequest(Command.FLUSHDB)); } // public FutureStatus select(int index) { // this.queueRequest(Command.SELECT, Convert.toBytes(index)); // return this; // } @Override public Future slaveof(String host, int port) { byte[] hostbytes = null; if((hostbytes = JRedisSupport.getKeyBytes(host)) == null) throw new IllegalArgumentException ("invalid host => ["+host+"]"); byte[] portbytes = null; if((portbytes = Convert.toBytes(port)) == null) throw new IllegalArgumentException ("invalid port => ["+port+"]"); return new FutureStatus(this.queueRequest(Command.SLAVEOF, hostbytes, portbytes)); } public Future slaveofnone() { return new FutureStatus(this.queueRequest(Command.SLAVEOF, "no".getBytes(), "one".getBytes())); } @Override public FutureStatus rename(K oldkey, K newkey) { byte[] oldkeydata = null; if((oldkeydata = JRedisSupport.getKeyBytes(oldkey)) == null) throw new IllegalArgumentException ("invalid key => ["+oldkey+"]"); byte[] newkeydata = null; if((newkeydata = JRedisSupport.getKeyBytes(newkey)) == null) throw new IllegalArgumentException ("invalid key => ["+newkey+"]"); return new FutureStatus(this.queueRequest(Command.RENAME, oldkeydata, newkeydata)); } @Override public Future renamenx(K oldkey, K newkey){ byte[] oldkeydata = null; if((oldkeydata = JRedisSupport.getKeyBytes(oldkey)) == null) throw new IllegalArgumentException ("invalid key => ["+oldkey+"]"); byte[] newkeydata = null; if((newkeydata = JRedisSupport.getKeyBytes(newkey)) == null) throw new IllegalArgumentException ("invalid key => ["+newkey+"]"); Future futureResponse = this.queueRequest(Command.RENAMENX, oldkeydata, newkeydata); return new FutureBoolean(futureResponse); } public FutureLong rpush(K key, byte[] value) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); if(value == null) throw new IllegalArgumentException ("null value"); return new FutureLong(this.queueRequest(Command.RPUSH, keybytes, value)); } public FutureLong rpushx(K key, byte[] value) { byte[] keybytes = null; if ((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); if (value == null) throw new IllegalArgumentException ("null value"); return new FutureLong(this.queueRequest(Command.RPUSHX, keybytes, value)); } public FutureLong lpushx(K key, byte[] value) { byte[] keybytes = null; if ((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); if (value == null) throw new IllegalArgumentException ("null value"); return new FutureLong(this.queueRequest(Command.LPUSHX, keybytes, value)); } public FutureLong linsert(K key, boolean after, byte[] oldvalue, byte[] newvalue) { byte[] keybytes = null; if ((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[][] bulk = new byte[4][]; bulk[0] = keybytes; bulk[1] = (after ? "AFTER" : "BEFORE").getBytes(); bulk[2] = oldvalue; bulk[3] = newvalue; return new FutureLong(this.queueRequest(Command.LINSERT, bulk)); } public FutureLong linsertAfter(K key, byte[] oldvalue, byte[] newvalue) { return linsert(key, true, oldvalue, newvalue); } public FutureLong linsertBefore(K key, byte[] oldvalue, byte[] newvalue) { return linsert(key, false, oldvalue, newvalue); } @Override public FutureByteArray rpoplpush (String srcList, String destList) { byte[] srckeybytes = null; if((srckeybytes = JRedisSupport.getKeyBytes(srcList)) == null) throw new IllegalArgumentException ("invalid src key => ["+srcList+"]"); byte[] destkeybytes = null; if((destkeybytes = JRedisSupport.getKeyBytes(destList)) == null) throw new IllegalArgumentException ("invalid dest key => ["+destList+"]"); Future futureResponse = this.queueRequest(Command.RPOPLPUSH, srckeybytes, destkeybytes); return new FutureByteArray(futureResponse); } @Override public FutureLong rpush(K key, String value) { // rpush(key, DefaultCodec.encode(value)); return rpush(key, DefaultCodec.encode(value)); } @Override public FutureLong rpush(K key, Number value) { return rpush(key, String.valueOf(value).getBytes()); } @Override public FutureLong rpush (K key, T value) { return rpush(key, DefaultCodec.encode(value)); } @Override public Future sadd(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SADD, keybytes, member); return new FutureBoolean(futureResponse); } @Override public Future sadd (K key, String value) { return sadd (key, DefaultCodec.encode(value)); } @Override public Future sadd (K key, Number value) { return sadd (key, String.valueOf(value).getBytes()); } @Override public Future sadd (K key, T value) { return sadd (key, DefaultCodec.encode(value)); } @Override public Future zadd(K key, double score, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZADD, keybytes, Convert.toBytes(score), member); return new FutureBoolean(futureResponse); } @Override public Future zadd (K key, double score, String value) { return zadd (key, score, DefaultCodec.encode(value)); } @Override public Future zadd (K key, double score, Number value) { return zadd (key, score, String.valueOf(value).getBytes()); } @Override public Future zadd (K key, double score, T value) { return zadd (key, score, DefaultCodec.encode(value)); } @Override public Future zincrby(K key, double score, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZINCRBY, keybytes, Convert.toBytes(score), member); return new FutureDouble(futureResponse); } @Override public Future zincrby (K key, double score, String value) { return zincrby (key, score, DefaultCodec.encode(value)); } @Override public Future zincrby (K key, double score, Number value) { return zincrby (key, score, String.valueOf(value).getBytes()); } @Override public Future zincrby (K key, double score, T value) { return zincrby (key, score, DefaultCodec.encode(value)); } @Override public FutureStatus save() { return new FutureStatus(this.queueRequest(Command.SAVE)); } // -------- set @Override public Future setbit(K key, int offset, boolean value) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SETBIT, keybytes, Convert.toBytes(offset), Convert.toBytes(value ? 1 : 0)); return new FutureBit(futureResponse); } @Override public Future getbit(K key, int offset) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.GETBIT, keybytes, Convert.toBytes(offset)); return new FutureBit(futureResponse); } @Override public FutureStatus set(K key, byte[] value) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); return new FutureStatus(this.queueRequest(Command.SET, keybytes, value)); } @Override public FutureStatus set(K key, String value) { return set(key, DefaultCodec.encode(value)); } @Override public FutureStatus set(K key, Number value) { return set(key, String.valueOf(value).getBytes()); } @Override public FutureStatus set (K key, T value) { return set(key, DefaultCodec.encode(value)); } @Override public Future getset(K key, byte[] value) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.GETSET, keybytes, value); return new FutureByteArray(futureResponse); } @Override public Future getset(K key, String value) { return getset(key, DefaultCodec.encode(value)); } @Override public Future getset(K key, Number value) { return getset(key, String.valueOf(value).getBytes()); } @Override public Future getset (K key, T value) { return getset(key, DefaultCodec.encode(value)); } @Override public Future setnx(K key, byte[] value){ byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SETNX, keybytes, value); return new FutureBoolean(futureResponse); } @Override public Future setnx(K key, String value) { return setnx(key, DefaultCodec.encode(value)); } @Override public Future setnx(K key, Number value) { return setnx(key, String.valueOf(value).getBytes()); } @Override public Future setnx (K key, T value) { return setnx(key, DefaultCodec.encode(value)); } @Override public Future append (K key, byte[] value){ byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.APPEND, keybytes, value); return new FutureLong(futureResponse); } @Override public Future append(K key, String value) { return append(key, DefaultCodec.encode(value)); } @Override public Future append(K key, Number value) { return append(key, String.valueOf(value).getBytes()); } @Override public Future append (K key, T value) { return append(key, DefaultCodec.encode(value)); } @Override public Future sismember(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SISMEMBER, keybytes, member); return new FutureBoolean(futureResponse); } @Override public Future sismember(K key, String value) { return sismember(key, DefaultCodec.encode(value)); } @Override public Future sismember(K key, Number numberValue) { return sismember (key, String.valueOf(numberValue).getBytes()); } @Override public Future sismember(K key, T object) { return sismember(key, DefaultCodec.encode(object)); } public Future smove (K srcKey, K destKey, byte[] member) { byte[] srcKeyBytes = null; if((srcKeyBytes = JRedisSupport.getKeyBytes(srcKey)) == null) throw new IllegalArgumentException ("invalid key => ["+srcKey+"]"); byte[] destKeyBytes = null; if((destKeyBytes = JRedisSupport.getKeyBytes(destKey)) == null) throw new IllegalArgumentException ("invalid key => ["+destKey+"]"); Future futureResponse = this.queueRequest(Command.SMOVE, srcKeyBytes, destKeyBytes, member); return new FutureBoolean(futureResponse); } public Future smove (K srcKey, K destKey, String stringValue) { return smove (srcKey, destKey, DefaultCodec.encode(stringValue)); } public Future smove (K srcKey, K destKey, Number numberValue) { return smove (srcKey, destKey, String.valueOf(numberValue).getBytes()); } public Future smove (K srcKey, K destKey, T object) { return smove (srcKey, destKey, DefaultCodec.encode(object)); } // ------------------------------------------------------------------------ // Commands operating on hashes // ------------------------------------------------------------------------ public Future hset(K key, K field, byte[] value) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] entryBytes = null; if((entryBytes = JRedisSupport.getKeyBytes(field)) == null) throw new IllegalArgumentException ("invalid field => ["+field+"]"); Future futureResponse = this.queueRequest(Command.HSET, keyBytes, entryBytes, value); return new FutureBoolean(futureResponse); } public Future hset(K key, K field, String stringValue) { return hset (key, field, DefaultCodec.encode(stringValue)); } public Future hincrby(K key, K field, long increment) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] entryBytes = null; if((entryBytes = JRedisSupport.getKeyBytes(field)) == null) throw new IllegalArgumentException ("invalid field => ["+field+"]"); Future futureResponse = this.queueRequest(Command.HINCRBY, keyBytes, entryBytes, Convert.toBytes(increment)); return new FutureLong(futureResponse); } public Future hset(K key, K field, Number numberValue) { return hset (key, field, String.valueOf(numberValue).getBytes()); } public Future hset(K key, K field, T object) { return hset (key, field, DefaultCodec.encode(object)); } public Future hget(K key, K entry) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] entryBytes = null; if((entryBytes = JRedisSupport.getKeyBytes(entry)) == null) throw new IllegalArgumentException ("invalid field => ["+entry+"]"); Future futureResponse = this.queueRequest(Command.HGET, keyBytes, entryBytes); return new FutureByteArray(futureResponse); } public Future hexists(K key, K entry) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] entryBytes = null; if((entryBytes = JRedisSupport.getKeyBytes(entry)) == null) throw new IllegalArgumentException ("invalid field => ["+entry+"]"); Future futureResponse = this.queueRequest(Command.HEXISTS, keyBytes, entryBytes); return new FutureBoolean(futureResponse); } public Future hdel(K key, K entry) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] entryBytes = null; if((entryBytes = JRedisSupport.getKeyBytes(entry)) == null) throw new IllegalArgumentException ("invalid field => ["+entry+"]"); Future futureResponse = this.queueRequest(Command.HDEL, keyBytes, entryBytes); return new FutureBoolean(futureResponse); } public Future hlen(K key) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.HLEN, keyBytes); return new FutureLong(futureResponse); } public Future> hkeys(K key) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.HKEYS, keyBytes); return new FutureKeyList (futureResponse); } @Override public Future> hvals(K key) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.HKEYS, keyBytes); return new FutureByteArrayList (futureResponse); } @Override public Future> hgetall(K key) { byte[] keyBytes = null; if((keyBytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.HGETALL, keyBytes); return new FutureDataDictionary (futureResponse); } /* ------------------------------- commands returning int value --------- */ @Override public Future incr(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.INCR, keybytes); return new FutureLong (futureResponse); } @Override public Future incrby(K key, int delta) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.INCRBY, keybytes, Convert.toBytes(delta)); return new FutureLong (futureResponse); } @Override public Future decr(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.DECR, keybytes); return new FutureLong (futureResponse); } @Override public Future decrby(K key, int delta) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.DECRBY, keybytes, Convert.toBytes(delta)); return new FutureLong (futureResponse); } @Override public Future llen(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.LLEN, keybytes); return new FutureLong (futureResponse); } @Override public Future scard(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SCARD, keybytes); return new FutureLong (futureResponse); } @Override public Future zcard(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZCARD, keybytes); return new FutureLong (futureResponse); } public Future srandmember (K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SRANDMEMBER, keybytes); return new FutureByteArray (futureResponse); } public Future spop (K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SPOP, keybytes); return new FutureByteArray (futureResponse); } /* ------------------------------- commands returning long value --------- */ @Override public Future dbsize() { Future futureResponse = this.queueRequest(Command.DBSIZE); return new FutureLong (futureResponse); } @Override public Future lastsave() { Future futureResponse = this.queueRequest(Command.LASTSAVE); return new FutureLong (futureResponse); } /* ------------------------------- commands returning byte[] --------- */ @Override public Future get(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.GET, keybytes); return new FutureByteArray(futureResponse); } @Override public Future lindex(K key, long index) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.LINDEX, keybytes, Convert.toBytes(index)); return new FutureByteArray(futureResponse); } @Override public Future lpop(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.LPOP, keybytes); return new FutureByteArray(futureResponse); } @Override public Future rpop(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.RPOP, keybytes); return new FutureByteArray(futureResponse); } /* ------------------------------- commands returning String--------- */ @Override public Future randomkey() { Future futureResponse = this.queueRequest(Command.RANDOMKEY); return new FutureByteArray(futureResponse); } @Override public Future type(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); return new FutureRedisType(this.queueRequest(Command.TYPE, keybytes)); } /* ------------------------------- commands returning Maps --------- */ @Override public Future> info() { return new FutureInfo(this.queueRequest(Command.INFO)); } @Override public Future debug (K key) { byte[] keybytes = JRedisSupport.getKeyBytes(key); // if(key.length() == 0) // throw new IllegalArgumentException ("invalid zero length key => ["+key+"]"); return new FutureObjectInfo (this.queueRequest(Command.DEBUG, "OBJECT".getBytes(), keybytes)); } /* ------------------------------- commands returning Lists --------- */ @Override public Future> mget(String ... keys) { if(null == keys || keys.length == 0) throw new IllegalArgumentException("no keys specified"); byte[] keydata = null; byte[][] keybytes = new byte[keys.length][]; int i=0; for(String k : keys) { if((keydata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"] @ index: " + i); keybytes[i++] = keydata; } return new FutureByteArrayList(this.queueRequest(Command.MGET, keybytes)); } /* MSETs */ private FutureStatus mset(byte[][] mappings){ Future futureResponse = this.queueRequest(Command.MSET, mappings); return new FutureStatus(futureResponse); } public FutureStatus mset(Map keyValueMap){ // KeyCodec codec = DefaultKeyCodec.provider(); byte[][] mappings = new byte[keyValueMap.size()*2][]; int i = 0; for (Entry e : keyValueMap.entrySet()){ mappings[i++] = JRedisSupport.getKeyBytes(e.getKey()); mappings[i++] = e.getValue(); } return mset(mappings); } public FutureStatus mset(KeyValueSet.ByteArrays keyValueMap){ return mset(keyValueMap.getMappings()); } public FutureStatus mset(KeyValueSet.Strings keyValueMap){ return mset(keyValueMap.getMappings()); } public FutureStatus mset(KeyValueSet.Numbers keyValueMap){ return mset(keyValueMap.getMappings()); } public FutureStatus mset(KeyValueSet.Objects keyValueMap){ return mset(keyValueMap.getMappings()); } /* MSETNXs */ private Future msetnx(byte[][] mappings){ Future futureResponse = this.queueRequest(Command.MSETNX, mappings); return new FutureBoolean(futureResponse); } public Future msetnx(Map keyValueMap){ // KeyCodec codec = DefaultKeyCodec.provider(); byte[][] mappings = new byte[keyValueMap.size()*2][]; int i = 0; for (Entry e : keyValueMap.entrySet()){ mappings[i++] = JRedisSupport.getKeyBytes(e.getKey()); mappings[i++] = e.getValue(); } return msetnx(mappings); } public Future msetnx(KeyValueSet.ByteArrays keyValueMap){ return msetnx(keyValueMap.getMappings()); } public Future msetnx(KeyValueSet.Strings keyValueMap){ return msetnx(keyValueMap.getMappings()); } public Future msetnx(KeyValueSet.Numbers keyValueMap){ return msetnx(keyValueMap.getMappings()); } public Future msetnx(KeyValueSet.Objects keyValueMap){ return msetnx(keyValueMap.getMappings()); } @Override public Future> smembers(K key) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("null key."); return new FutureByteArrayList(this.queueRequest(Command.SMEMBERS, keydata)); } @Override public Future> keys() { return this.keys("*"); } @Override public Future> keys(K pattern) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(pattern)) == null) throw new IllegalArgumentException ("null key."); Future futureResponse = this.queueRequest(Command.KEYS, keydata); return new FutureKeyList(futureResponse); } public Future keystolist(String pattern, String listname) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(pattern)) == null) throw new IllegalArgumentException ("null key."); byte[] listnamedata = null; if((listnamedata = JRedisSupport.getKeyBytes(listname)) == null) throw new IllegalArgumentException ("null list name."); return new FutureLong(this.queueRequest(Command.KEYSTOLIST, keydata, listnamedata)); } @Override public Future> lrange(K key, long from, long to) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); return new FutureByteArrayList(this.queueRequest(Command.LRANGE, keybytes, fromBytes, toBytes)); } @Override public Future substr(K key, long from, long to) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); return new FutureByteArray(this.queueRequest(Command.SUBSTR, keybytes, fromBytes, toBytes)); } @Override public Future> zrange(K key, long from, long to) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); return new FutureByteArrayList(this.queueRequest(Command.ZRANGE, keybytes, fromBytes, toBytes)); } @Override public Future> zrangebyscore(K key, double minScore, double maxScore) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] minScoreBytes = Convert.toBytes(minScore); byte[] maxScoreBytes = Convert.toBytes(maxScore); return new FutureByteArrayList(this.queueRequest(Command.ZRANGEBYSCORE, keybytes, minScoreBytes, maxScoreBytes)); } @Override public Future> zrangebyscoreSubset(K key, double minScore, double maxScore) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] minScoreBytes = Convert.toBytes(minScore); byte[] maxScoreBytes = Convert.toBytes(maxScore); return new FutureZSetList(this.queueRequest(Command.ZRANGEBYSCORE$OPTS, keybytes, minScoreBytes, maxScoreBytes, Command.Option.WITHSCORES.bytes)); } @Override public Future zremrangebyscore(K key, double minScore, double maxScore) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] minScoreBytes = Convert.toBytes(minScore); byte[] maxScoreBytes = Convert.toBytes(maxScore); return new FutureLong(this.queueRequest(Command.ZREMRANGEBYSCORE, keybytes, minScoreBytes, maxScoreBytes)); } @Override public Future zcount(K key, double minScore, double maxScore) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] minScoreBytes = Convert.toBytes(minScore); byte[] maxScoreBytes = Convert.toBytes(maxScore); return new FutureLong(this.queueRequest(Command.ZCOUNT, keybytes, minScoreBytes, maxScoreBytes)); } @Override public Future zremrangebyrank(K key, long minRank, long maxRank) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] minScoreBytes = Convert.toBytes(minRank); byte[] maxScoreBytes = Convert.toBytes(maxRank); return new FutureLong(this.queueRequest(Command.ZREMRANGEBYRANK, keybytes, minScoreBytes, maxScoreBytes)); } @Override public Future> zrevrange(K key, long from, long to) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); return new FutureByteArrayList(this.queueRequest(Command.ZREVRANGE, keybytes, fromBytes, toBytes)); } @Override public Future> zrangeSubset(K key, long from, long to) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); return new FutureZSetList(this.queueRequest(Command.ZRANGE$OPTS, keybytes, fromBytes, toBytes, Command.Option.WITHSCORES.bytes)); } @Override public Future> zrevrangeSubset(K key, long from, long to) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); return new FutureZSetList(this.queueRequest(Command.ZREVRANGE$OPTS, keybytes, fromBytes, toBytes, Command.Option.WITHSCORES.bytes)); } @Override public Sort sort(final K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); final JRedisFutureSupport client = this; Sort sortQuery = new SortSupport (keybytes) { @Override protected Future> execAsyncSort(byte[]... fullSortCmd) { return new FutureByteArrayList(client.queueRequest(Command.SORT, fullSortCmd)); } protected Future> execAsyncSortStore(byte[]... fullSortCmd) { Future fResp = client.queueRequest(Command.SORT$STORE, fullSortCmd); // new FutureLong(fResp); // hah? return new FutureSortStoreResp(fResp); } protected List execSort(byte[]... fullSortCmd) { throw new IllegalStateException("JRedisFuture does not support synchronous sort."); } protected List execSortStore(byte[]... fullSortCmd) { throw new IllegalStateException("JRedisFuture does not support synchronous sort."); } }; return sortQuery; } /* ------------------------------- commands that don't get a response --------- */ @Override public FutureStatus quit() { return new FutureStatus(this.queueRequest(Command.QUIT)); } @Override public FutureStatus flush() { return new FutureStatus(this.queueRequest(Command.CONN_FLUSH)); } /* ------------------------------- commands that don't get a response END --------- */ @Override public Future> sinter(K set1, K... sets) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(set1)) == null) throw new IllegalArgumentException ("invalid key => ["+set1+"]"); byte[][] keybytes = new byte[1+sets.length][]; int i=0; keybytes[i++] = keydata; for(K k : sets) { if((keydata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); keybytes[i++] = keydata; } return new FutureByteArrayList(this.queueRequest(Command.SINTER, keybytes)); } @Override public Future> sunion(K set1, K... sets) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(set1)) == null) throw new IllegalArgumentException ("invalid key => ["+set1+"]"); byte[][] keybytes = new byte[1+sets.length][]; int i=0; keybytes[i++] = keydata; for(K k : sets) { if((keydata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); keybytes[i++] = keydata; } return new FutureByteArrayList(this.queueRequest(Command.SUNION, keybytes)); } @Override public Future> sdiff(K set1, K... sets) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(set1)) == null) throw new IllegalArgumentException ("invalid key => ["+set1+"]"); byte[][] keybytes = new byte[sets.length+1][]; int i=0; keybytes[i++] = keydata; for(K k : sets) { if((keydata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); keybytes[i++] = keydata; } return new FutureByteArrayList(this.queueRequest(Command.SDIFF, keybytes)); } @Override public FutureStatus sinterstore(K dest, K... sets) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(dest)) == null) throw new IllegalArgumentException ("invalid key => ["+dest+"]"); byte[][] setbytes = new byte[1+sets.length][]; int i=0; setbytes[i++] = keydata; byte[] setdata =null; for(K k : sets) { if((setdata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); setbytes[i++] = setdata; } return new FutureStatus(this.queueRequest(Command.SINTERSTORE, setbytes)); } @Override public FutureStatus sunionstore(K dest, K... sets) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(dest)) == null) throw new IllegalArgumentException ("invalid key => ["+dest+"]"); byte[][] setbytes = new byte[1+sets.length][]; int i=0; setbytes[i++] = keydata; byte[] setdata =null; for(K k : sets) { if((setdata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); setbytes[i++] = setdata; } return new FutureStatus(this.queueRequest(Command.SUNIONSTORE, setbytes)); } @Override public FutureStatus sdiffstore(K dest, K... sets) { byte[] keydata = null; if((keydata = JRedisSupport.getKeyBytes(dest)) == null) throw new IllegalArgumentException ("invalid key => ["+dest+"]"); byte[][] setbytes = new byte[1+sets.length][]; int i=0; setbytes[i++] = keydata; byte[] setdata =null; for(K k : sets) { if((setdata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); setbytes[i++] = setdata; } return new FutureStatus(this.queueRequest(Command.SDIFFSTORE, setbytes)); } @Override public Future del(K ... keys) { if(null == keys || keys.length == 0) throw new IllegalArgumentException("no keys specified"); byte[] keydata = null; byte[][] keybytes = new byte[keys.length][]; int i=0; for(K k : keys) { if((keydata = JRedisSupport.getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"] @ index: " + i); keybytes[i++] = keydata; } Future futureResponse = this.queueRequest(Command.DEL, keybytes); return new FutureLong(futureResponse); } @Override public Future exists(K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.EXISTS, keybytes); return new FutureBoolean(futureResponse); } @Override public FutureLong lpush(K key, byte[] value) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); if(value == null) throw new IllegalArgumentException ("null value"); return new FutureLong(this.queueRequest(Command.LPUSH, keybytes, value)); } @Override public FutureLong lpush(K key, String value) { return lpush(key, DefaultCodec.encode(value)); } @Override public FutureLong lpush(K key, Number value) { return lpush(key, String.valueOf(value).getBytes()); } @Override public FutureLong lpush (K key, T value) { return lpush(key, DefaultCodec.encode(value)); } @Override public Future lrem(K key, byte[] value, int count) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] countBytes = Convert.toBytes(count); Future futureResponse = this.queueRequest(Command.LREM, keybytes, countBytes, value); return new FutureLong(futureResponse); } @Override public Future lrem (K listKey, String value, int count){ return lrem (listKey, DefaultCodec.encode(value), count); } @Override public Future lrem (K listKey, Number numberValue, int count) { return lrem (listKey, String.valueOf(numberValue).getBytes(), count); } @Override public Future lrem (K listKey, T object, int count){ return lrem (listKey, DefaultCodec.encode(object), count); } @Override public FutureStatus lset(K key, long index, byte[] value) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] indexBytes = Convert.toBytes(index); return new FutureStatus(this.queueRequest(Command.LSET, keybytes, indexBytes, value)); } @Override public FutureStatus lset (K key, long index, String value) { return lset (key, index, DefaultCodec.encode(value)); } @Override public FutureStatus lset (K key, long index, Number numberValue){ return lset (key, index, String.valueOf(numberValue).getBytes()); } @Override public FutureStatus lset (K key, long index, T object){ return lset (key, index, DefaultCodec.encode(object)); } @Override public Future move(K key, int dbIndex) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.MOVE, keybytes, Convert.toBytes(dbIndex)); return new FutureBoolean(futureResponse); } @Override public Future srem(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.SREM, keybytes, member); return new FutureBoolean(futureResponse); } @Override public Future srem (K key, String value) { return srem (key, DefaultCodec.encode(value)); } @Override public Future srem (K key, Number value) { return srem (key, String.valueOf(value).getBytes()); } @Override public Future srem (K key, T value) { return srem (key, DefaultCodec.encode(value)); } @Override public Future zrem(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZREM, keybytes, member); return new FutureBoolean(futureResponse); } @Override public Future zrem (K key, String value) { return zrem (key, DefaultCodec.encode(value)); } @Override public Future zrem (K key, Number value) { return zrem (key, String.valueOf(value).getBytes()); } @Override public Future zrem (K key, T value) { return zrem (key, DefaultCodec.encode(value)); } @Override public Future zscore(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZSCORE, keybytes, member); return new FutureDouble(futureResponse); } @Override public Future zscore (K key, String value) { return zscore (key, DefaultCodec.encode(value)); } @Override public Future zscore (K key, Number value) { return zscore (key, String.valueOf(value).getBytes()); } @Override public Future zscore (K key, T value) { return zscore (key, DefaultCodec.encode(value)); } @Override public Future zrank(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZRANK, keybytes, member); return new FutureLong(futureResponse); } @Override public Future zrank (K key, String value) { return zrank (key, DefaultCodec.encode(value)); } @Override public Future zrank (K key, Number value) { return zrank (key, String.valueOf(value).getBytes()); } @Override public Future zrank (K key, T value) { return zrank (key, DefaultCodec.encode(value)); } @Override public Future zrevrank(K key, byte[] member) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.ZREVRANK, keybytes, member); return new FutureLong(futureResponse); } @Override public Future zrevrank (K key, String value) { return zrevrank (key, DefaultCodec.encode(value)); } @Override public Future zrevrank (K key, Number value) { return zrevrank (key, String.valueOf(value).getBytes()); } @Override public Future zrevrank (K key, T value) { return zrevrank (key, DefaultCodec.encode(value)); } @Override public FutureStatus ltrim(K key, long keepFrom, long keepTo) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(keepFrom); byte[] toBytes = Convert.toBytes(keepTo); return new FutureStatus(this.queueRequest(Command.LTRIM, keybytes, fromBytes, toBytes)); } @Override public Future expire(K key, int ttlseconds) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] ttlbytes = Convert.toBytes(ttlseconds); Future futureResponse = this.queueRequest(Command.EXPIRE, keybytes, ttlbytes); return new FutureBoolean(futureResponse); } @Override public Future expireat(K key, long epochtime) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long expiretime = TimeUnit.SECONDS.convert(epochtime, TimeUnit.MILLISECONDS); byte[] expiretimeBytes = Convert.toBytes(expiretime); Future futureResponse = this.queueRequest(Command.EXPIREAT, keybytes, expiretimeBytes); return new FutureBoolean(futureResponse); } @Override public Future ttl (K key) { byte[] keybytes = null; if((keybytes = JRedisSupport.getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Future futureResponse = this.queueRequest(Command.TTL, keybytes); return new FutureLong(futureResponse); } // TODO: integrate using KeyCodec and a CodecManager at client spec and init time. // TODO: (implied) ClientSpec (impls. ConnectionSpec) // this isn't cooked yet -- lets think more about the implications... // // static final private Map keyByteCache = new ConcurrentHashMap(); public static final boolean CacheKeys = false; public static class FutureResultBase { final protected Future pendingRequest; protected FutureResultBase(Future pendingRequest){ this.pendingRequest = pendingRequest;} public boolean cancel (boolean mayInterruptIfRunning) { return pendingRequest.cancel(mayInterruptIfRunning); } public boolean isCancelled () { return pendingRequest.isCancelled(); } public boolean isDone () { return pendingRequest.isDone(); } } public static class FutureStatus extends FutureResultBase implements Future { protected FutureStatus (Future pendingRequest) { super(pendingRequest); } public ResponseStatus get () throws InterruptedException, ExecutionException { // StatusResponse statusResponse = (StatusResponse) pendingRequest.get(); // HANDLE VIRTUALS HERE // return statusResponse.getStatus(); return pendingRequest.get().getStatus(); } public ResponseStatus get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { // StatusResponse statusResponse = (StatusResponse) pendingRequest.get(timeout, unit); // return statusResponse.getStatus(); return pendingRequest.get(timeout, unit).getStatus(); } } public static class FutureBoolean extends FutureResultBase implements Future{ protected FutureBoolean (Future pendingRequest) { super(pendingRequest); } @SuppressWarnings("boxing") public Boolean get () throws InterruptedException, ExecutionException { ValueResponse valResp = (ValueResponse) pendingRequest.get(); return valResp.getBooleanValue(); } @SuppressWarnings("boxing") public Boolean get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse valResp = (ValueResponse) pendingRequest.get(timeout, unit); return valResp.getBooleanValue(); } } public static class FutureBit extends FutureResultBase implements Future { protected FutureBit (Future pendingRequest) { super(pendingRequest); } @SuppressWarnings("boxing") public Boolean get () throws InterruptedException, ExecutionException { ValueResponse valResp = (ValueResponse) pendingRequest.get(); return valResp.getLongValue() == 1; } @SuppressWarnings("boxing") public Boolean get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse valResp = (ValueResponse) pendingRequest.get(timeout, unit); return valResp.getLongValue() == 1; } } public static class FutureString extends FutureResultBase implements Future{ protected FutureString (Future pendingRequest) { super(pendingRequest); } public String get () throws InterruptedException, ExecutionException { ValueResponse valResp = (ValueResponse) pendingRequest.get(); return valResp.getStringValue(); } public String get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse valResp = (ValueResponse) pendingRequest.get(timeout, unit); return valResp.getStringValue(); } } public static class FutureRedisType extends FutureResultBase implements Future{ protected FutureRedisType (Future pendingRequest) { super(pendingRequest); } private final RedisType getRedisType(ValueResponse resp){ String stringValue = resp.getStringValue(); return RedisType.valueOf(stringValue); } public RedisType get () throws InterruptedException, ExecutionException { ValueResponse valResp = (ValueResponse) pendingRequest.get(); return getRedisType(valResp); } public RedisType get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse valResp = (ValueResponse) pendingRequest.get(timeout, unit); return getRedisType(valResp); } } public static class FutureLong extends FutureResultBase implements Future{ protected FutureLong (Future pendingRequest) { super(pendingRequest); } @SuppressWarnings("boxing") public Long get () throws InterruptedException, ExecutionException { ValueResponse valResp = (ValueResponse) pendingRequest.get(); return valResp.getLongValue(); } @SuppressWarnings("boxing") public Long get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse valResp = (ValueResponse) pendingRequest.get(timeout, unit); return valResp.getLongValue(); } } public static class FutureDouble extends FutureResultBase implements Future{ protected FutureDouble (Future pendingRequest) { super(pendingRequest); } @SuppressWarnings("boxing") public Double get () throws InterruptedException, ExecutionException { BulkResponse bulkResp = (BulkResponse) pendingRequest.get(); if(bulkResp.getBulkData() != null) return Convert.toDouble(bulkResp.getBulkData()); return null; } @SuppressWarnings("boxing") public Double get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { BulkResponse bulkResp = (BulkResponse) pendingRequest.get(timeout, unit); if(bulkResp.getBulkData() != null) return Convert.toDouble(bulkResp.getBulkData()); return null; } } public static class FutureByteArray extends FutureResultBase implements Future{ protected FutureByteArray (Future pendingRequest) { super(pendingRequest); } public byte[] get () throws InterruptedException, ExecutionException { BulkResponse resp = (BulkResponse) pendingRequest.get(); return resp.getBulkData(); } public byte[] get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { BulkResponse resp = (BulkResponse) pendingRequest.get(timeout, unit); return resp.getBulkData(); } } public static class FutureSortStoreResp extends FutureResultBase implements Future>{ protected FutureSortStoreResp (Future pendingRequest) { super(pendingRequest); } public List get () throws InterruptedException, ExecutionException { ValueResponse resp = (ValueResponse) pendingRequest.get(); return packValueResult(resp.getLongValue()); } public List get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse resp = (ValueResponse) pendingRequest.get(timeout, unit); return packValueResult(resp.getLongValue()); } private static List packValueResult(long number) { List list = new ArrayList(1); list.add(Convert.toBytes(number)); return list; } } public static class FutureByteArrayList extends FutureResultBase implements Future>{ protected FutureByteArrayList (Future pendingRequest) { super(pendingRequest); } public List get () throws InterruptedException, ExecutionException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(); return resp.getMultiBulkData(); } public List get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(timeout, unit); return resp.getMultiBulkData(); } } public static class FutureDataDictionary extends FutureResultBase implements Future>{ protected FutureDataDictionary (Future pendingRequest) { super(pendingRequest); } public Map get () throws InterruptedException, ExecutionException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(); return convert(resp.getMultiBulkData()); } public Map get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(timeout, unit); return convert(resp.getMultiBulkData()); } private static final Map convert (List bulkdata) { Map map = null; if(null != bulkdata) { map = new HashMap(bulkdata.size()/2); for(int i=0; i>{ protected FutureKeyList (Future pendingRequest) { super(pendingRequest); } // private List getResultList (BulkResponse resp) { // StringTokenizer tokenizer = new StringTokenizer(new String(resp.getBulkData()), " "); // List list = new ArrayList (12); // while (tokenizer.hasMoreTokens()){ // list.add(tokenizer.nextToken()); // } // return list; // } public List get () throws InterruptedException, ExecutionException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(); List multibulkdata = resp.getMultiBulkData(); // List list = null; // if(null != multibulkdata) // list = DefaultCodec.toStr(multibulkdata); return multibulkdata; } public List get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(timeout, unit); List multibulkdata = resp.getMultiBulkData(); // List list = null; // if(null != multibulkdata) // list = DefaultCodec.toStr(multibulkdata); return multibulkdata; } } public static class FutureInfo extends FutureResultBase implements Future>{ protected FutureInfo (Future pendingRequest) { super(pendingRequest); } private Map getResultMap (BulkResponse resp) { StringTokenizer tokenizer = new StringTokenizer(new String(resp.getBulkData()), "\r\n"); Map infomap = new HashMap(48); while (tokenizer.hasMoreTokens()){ final String info = tokenizer.nextToken(); // ignore comments "# heading" if(info.startsWith("#")) continue; int c = info.indexOf(':'); String _key =info.substring(0, c); String _value = info.substring(c+1); infomap.put(_key, _value); } return infomap; } public Map get () throws InterruptedException, ExecutionException { BulkResponse resp = (BulkResponse) pendingRequest.get(); return getResultMap(resp); } public Map get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { BulkResponse resp = (BulkResponse) pendingRequest.get(timeout, unit); return getResultMap(resp); } } public static class FutureObjectInfo extends FutureResultBase implements Future{ protected FutureObjectInfo (Future pendingRequest) { super(pendingRequest); } private final ObjectInfo getObjectInfo(ValueResponse resp){ String stringValue = resp.getStringValue(); return ObjectInfo.valueOf(stringValue); } public ObjectInfo get () throws InterruptedException, ExecutionException { ValueResponse valResp = (ValueResponse) pendingRequest.get(); return getObjectInfo(valResp); } public ObjectInfo get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { ValueResponse valResp = (ValueResponse) pendingRequest.get(timeout, unit); return getObjectInfo(valResp); } } public static class FutureZSetList extends FutureResultBase implements Future>{ protected FutureZSetList (Future pendingRequest) { super(pendingRequest); } public List get () throws InterruptedException, ExecutionException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(); return convert(resp.getMultiBulkData()); } public List get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { MultiBulkResponse resp = (MultiBulkResponse) pendingRequest.get(timeout, unit); return convert(resp.getMultiBulkData()); } private static final List convert (List mbulkdata) { List zset = null; if(mbulkdata.size() > 0){ zset = new ArrayList(mbulkdata.size()/2); for(int i=0; i Future echo (byte[] msg) { if(msg == null) throw new IllegalArgumentException ("invalid value for echo => [null]"); Future futureResponse = this.queueRequest(Command.ECHO, msg); return new FutureByteArray(futureResponse); } public Future echo (String msg) { return echo(DefaultCodec.encode(msg)); } public Future echo (Number msg) { return echo(String.valueOf(msg).getBytes()); } public Future echo (T msg) { return echo (DefaultCodec.encode(msg)); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisPipeline.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.JRedisFuture; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.connection.AsyncPipelineConnection; /** * Asynchronous Redis client implementing {@link JRedisFuture} and using * an {@link AsyncPipelineConnection} for command processing. *

    * TODO: details the usage and characteristics. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Sep 21, 2009 * @since alpha.0 * */ //@Redis(versions={"1.00"}) public class JRedisPipeline extends JRedisFutureSupport { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** */ final private Connection connection; // ------------------------------------------------------------------------ // Construct and initialize // ------------------------------------------------------------------------ /** * @param connectionSpec */ public JRedisPipeline (ConnectionSpec connectionSpec) { // note: using a non shared connection mod connection = new AsyncPipelineConnection(connectionSpec); } // ------------------------------------------------------------------------ // Super overrides // ------------------------------------------------------------------------ /** * Requests to server are queued at this point. Any requests after a {@link Command#QUIT} will * raise an exception indicating the pipeline is shutting down. * @see org.jredis.ri.alphazero.JRedisFutureSupport#queueRequest(org.jredis.protocol.Command, byte[][]) */ protected Future queueRequest (Command cmd, byte[]...args) throws ClientRuntimeException, ProviderException { return connection.queueRequest(cmd, args); } // ------------------------------------------------------------------------ // public interface // ------------------------------------------------------------------------ /** * Provides a synchronous semantics interface ({@link JRedis}) to this pipeline. * Note that this is not a thread-safe mechanism. If you need a pipeline * connection with support for concurrent synchronous semantics, use a {@link JRedisPipelineService}. * * @return the {@link JRedis} interface for this pipeline. */ public JRedis sync () { return new JRedisSupport() { @Override protected Response serviceRequest (Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { Response response = null; try { response = JRedisPipeline.this.queueRequest(cmd, args).get(); } catch (InterruptedException e) { throw new ClientRuntimeException("Interrupted!", e); } catch (ExecutionException e) { Throwable cause = e.getCause(); if(cause instanceof RedisException) throw (RedisException) cause; else if(cause instanceof ProviderException) throw (ProviderException) cause; else if(cause instanceof ClientRuntimeException) throw (ClientRuntimeException)cause; else throw new ClientRuntimeException("Exception in pipeline exec of requested command", cause); } return response; } }; } /** * * @param timeout * @param unit * @return */ public JRedis sync (final long timeout, final TimeUnit unit) { return new JRedisSupport() { @Override protected Response serviceRequest (Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { Response response = null; try { response = JRedisPipeline.this.queueRequest(cmd, args).get(timeout, unit); } catch (InterruptedException e) { throw new ClientRuntimeException("Interrupted!", e); } catch (TimeoutException e) { throw new ClientRuntimeException("timedout waiting for response"); } catch (ExecutionException e) { Throwable cause = e.getCause(); if(cause instanceof RedisException) throw (RedisException) cause; else if(cause instanceof ProviderException) throw (ProviderException) cause; else if(cause instanceof ClientRuntimeException) throw (ClientRuntimeException)cause; else throw new ClientRuntimeException("Exception in pipeline exec of requested command", cause); } return response; } }; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisPipelineService.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.connection.SyncPipelineConnection; /** * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 6, 2009 * @since alpha.0 * */ public class JRedisPipelineService extends SyncJRedisBase { // ------------------------------------------------------------------------ // Consts // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** connection spec shared by all connections in pool */ @SuppressWarnings("unused") private final ConnectionSpec connectionSpec; private final Connection connection; // ------------------------------------------------------------------------ // Construct and initialize // ------------------------------------------------------------------------ /** * @param host * @param port * @param password * @param database * @param connectionCount */ public JRedisPipelineService (String host, int port, String password, int database) { this(DefaultConnectionSpec.newSpec(host, port, database, (password != null ? password.getBytes() : null))); } /** * @param connectionSpec * @param connectionCount */ public JRedisPipelineService (ConnectionSpec connectionSpec) { this.connectionSpec = connectionSpec; connectionSpec.setConnectionFlag(Connection.Flag.SHARED, Boolean.TRUE); connection = new SyncPipelineConnection(connectionSpec); } // ------------------------------------------------------------------------ // super overrides. // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.ri.alphazero.SynchJRedisBase#setConnection(org.jredis.connector.Connection) */ @Override protected final void setConnection (Connection connection) { throw new RuntimeException("who called me?"); } /* (non-Javadoc) * @see org.jredis.ri.alphazero.JRedisSupport#serviceRequest(org.jredis.protocol.Command, byte[][]) */ @Override protected Response serviceRequest (Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { return connection.serviceRequest(cmd, args); } /* (non-Javadoc) * @see org.jredis.resource.Resource#getInterface() */ public JRedis getInterface () { return this; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisService.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.ri.alphazero; // //import java.util.concurrent.Semaphore; //import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; //import org.jredis.ProviderException; //import org.jredis.RedisException; //import org.jredis.connector.Connection; //import org.jredis.connector.ConnectionSpec; //import org.jredis.protocol.Command; //import org.jredis.protocol.Response; //import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; //import org.jredis.ri.alphazero.support.Assert; //import org.jredis.ri.alphazero.support.Log; // ///** // * This class utilizes a (configurable) number of {@link Connection}s in a pool // * and can be utilized in multi-threaded usage contexts, such as web containers, // * etc. // *

    // * The service uses an internal pool of connections (see {@link JRedisService#default_connection_count}. If // * the number of service requests being processed reaches that limit, then any further calls will block until // * a connection becomes available. // *

    // * // * @author Joubin Houshyar (alphazero@sensesay.net) // * @version alpha.0, Apr 21, 2009 // * @since alpha.0 // * // */ // //public class JRedisService extends SynchJRedisBase { // // // ------------------------------------------------------------------------ // // Consts // // ------------------------------------------------------------------------ // /** Default value: 5 */ // public static final int default_connection_count = 5; // // // ------------------------------------------------------------------------ // // Properties // // ------------------------------------------------------------------------ // // /** counting semaphore for limiting concurrent access to pool to the count */ // private Semaphore connPoolAccess; // /** in use flag for the connections */ // private boolean connInUse[]; // /** the connections */ // private Connection conns[]; // // /** connection spec shared by all connections in pool */ // private final ConnectionSpec connectionSpec; // /** number of connections in pool */ // private final int connCount; // // // ------------------------------------------------------------------------ // // Construct and initialize // // ------------------------------------------------------------------------ // // /** // * @param host // * @param port // */ // public JRedisService (String host, int port) { // this(host, port, null, 0, default_connection_count); // } // // /** // * @param host // * @param port // * @param password // * @param database // * @param connectionCount // */ // public JRedisService (String host, int port, String password, int database, int connectionCount) { // byte[] credentials = password != null ? password.getBytes() : null; // connectionSpec = DefaultConnectionSpec.newSpec(host, port, database, credentials); // connCount = connectionCount; // initialize(); // } // // /** // * @param connectionSpec // * @param connectionCount // */ // public JRedisService (ConnectionSpec connectionSpec, int connectionCount) { // this.connectionSpec = connectionSpec; // // regardless of user spec, service has to assume shared connections // connectionSpec.setConnectionFlag(Connection.Flag.SHARED, true); // connCount = connectionCount; // // initialize(); // } // // /** // * Initialize the connection pool using the connection spec. // */ // private final void initialize () { // connPoolAccess = new Semaphore(connCount); // conns = new Connection[connCount]; // connInUse = new boolean [connCount]; // Connection conn = null; // connectionSpec.setConnectionFlag(Connection.Flag.RELIABLE, true); // connectionSpec.setConnectionFlag(Connection.Flag.SHARED, true); // for(int i=0; i< connCount;i++) { // try { // conn = Assert.notNull(createSynchConnection(connectionSpec), "Connection " + i, ClientRuntimeException.class); // conns[i] = conn; // connInUse[i] = false; // } // catch (Exception e) { // // TODO: remove this stacktrace // e.printStackTrace(); // throw new ClientRuntimeException("Could not create connection for service", e); // } // } // } // // // ------------------------------------------------------------------------ // // super overrides. // // ------------------------------------------------------------------------ // /* (non-Javadoc) // * @see org.jredis.ri.alphazero.SynchJRedisBase#setConnection(org.jredis.connector.Connection) // */ // @Override // protected void setConnection(Connection connection) { // throw new RuntimeException("who called me?"); // } // // // /* (non-Javadoc) // * @see org.jredis.ri.alphazero.JRedisSupport#serviceRequest(org.jredis.protocol.Command, byte[][]) // */ // @Override // protected Response serviceRequest(Command cmd, byte[]... args) // throws RedisException, ClientRuntimeException, ProviderException // { // Response response = null; // // try { // // we're using a counting semaphore to remain within // // bounds of the connection count // // if more than cnt requests are being serviced, we block here // connPoolAccess.acquire(); // // int i = 0; // try { // // bare bones connection pool - mark the first available // // connection as inUse and then use it // // this is faster than using the synch collections of JDK and sufficient // synchronized(connInUse){ // Note: could use finer grained sync on conns[i] in loop instead .. // for(; i=connCount) // throw new ProviderException("BUG: JRedisService connection pool manager - index exceeds bounds!"); // // response = conns[i].serviceRequest(cmd, args); // } // finally { // // return the connection to the pool // synchronized(connInUse){ // if(connInUse[i] != true) // throw new ProviderException("BUG: JRedisService connection pool manager - connection should have been marked in use!"); // connInUse[i] = false; // } // // // release the bounds sem. // connPoolAccess.release(); // } // } // catch (InterruptedException e) { // // TODO: need meaningful action here // // its not clear what can be done since the interrupt is occurring on the // // user (calling) thread and is not connection specific, and the situation // // is ambiguous. We can note it for now. // e.printStackTrace(); // Log.log("Thread <%s> was interrupted in JRedisService.serviceRequest", Thread.currentThread().getName()); // } // return response; // } // // ------------------------------------------------------------------------ // // Interface // // =========================================================== Resource // /* // * Provides basic Resource support without any state management. Extensions // * that use context in a simply manner can rely on these methods. Others may // * wish to override. // */ // // ------------------------------------------------------------------------ // // /* (non-Javadoc) // * @see org.jredis.resource.Resource#getInterface() // */ //// @Override // public JRedis getInterface() { // return this; // } //} ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/JRedisSupport.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.KeyValueSet; import org.jredis.ObjectInfo; import org.jredis.ProviderException; import org.jredis.Redis; import org.jredis.RedisException; import org.jredis.RedisType; import org.jredis.Sort; import org.jredis.ZSetEntry; import org.jredis.connector.Connection; import org.jredis.protocol.BulkResponse; import org.jredis.protocol.Command; import org.jredis.protocol.MultiBulkResponse; import org.jredis.protocol.Response; import org.jredis.protocol.ValueResponse; import org.jredis.ri.RI.Release; import org.jredis.ri.RI.Version; import org.jredis.ri.alphazero.semantics.DefaultKeyCodec; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.ri.alphazero.support.SortSupport; /** * * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 10, 2009 * @since alpha.0 * */ public abstract class JRedisSupport implements JRedis { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Extension point(s) /* * This class provides the convenience of a uniform implementation wide mapping * of JRedis api semantics to the native protocol level semantics of byte[]s. * * Extensions can use the provided extension points to provide or delegate the * servicing of request calls. */ // ------------------------------------------------------------------------ /** * This method mimics the eponymous {@link Connection#serviceRequest(Command, byte[]...)} * which defines the blocking api semantics of Synchronous connections. The extending class * can either directly (a) implement the protocol requirements, or, (b) delegate to a * {@link Connection} instance, or, (c) utilize a pool of {@link Connection}s. * * @param cmd * @param args * @return * @throws RedisException * @throws ClientRuntimeException * @throws ProviderException */ protected abstract Response serviceRequest (Command cmd, byte[]...args) throws RedisException, ClientRuntimeException, ProviderException; // ------------------------------------------------------------------------ // INTERFACE // ================================================================ Redis /* * Support of all the JRedis interface methods. * * This class uses the UTF-8 character set for all conversions due to its * use of the Convert and Codec support classes. * * All calls are forwarded to an abstract serviceRequest method that the * extending classes are expected to implement. * * Implementation note: * The methods in this class use redundant code in marshalling request params * and in unmarshalling the response data. We certainly can use a few helper * functions to reduce the redundancy, but given that such methods would be * repeatedly called, it was decided to effectively inline these statements in * each method body. */ // ------------------------------------------------------------------------ @Override public boolean setbit(K key, int offset, boolean value) throws ProviderException, ClientRuntimeException, RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SETBIT, keybytes, Convert.toBytes(offset), Convert.toBytes(value ? 1 : 0)); return valResponse.getLongValue() == 1; } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } } @Override public boolean getbit(K key, int offset) throws ProviderException, ClientRuntimeException, RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.GETBIT, keybytes, Convert.toBytes(offset)); return valResponse.getLongValue() == 1; } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } } // @Override // public JRedis auth(K key) throws RedisException { // byte[] keydata = null; // if((keydata = getKeyBytes(key)) == null) // throw new IllegalArgumentException ("invalid key => ["+key+"]"); // // this.serviceRequest(Command.AUTH, keydata); // return this; // } @Override public void bgsave() throws RedisException { this.serviceRequest(Command.BGSAVE); } @Override public String bgrewriteaof() throws RedisException { /* boolean ValueRespose */ String value = null; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.BGREWRITEAOF); value = valResponse.getStringValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public JRedis ping() throws RedisException { this.serviceRequest(Command.PING); return this; } @Override public JRedis flushall() throws RedisException { this.serviceRequest(Command.FLUSHALL).getStatus(); return this; } @Override public JRedis flushdb() throws RedisException { this.serviceRequest(Command.FLUSHDB).getStatus(); return this; } // @Override // public JRedis select(int index) throws RedisException { // this.serviceRequest(Command.SELECT, Convert.toBytes(index)); // return this; // } public void slaveof(String host, int port) throws RedisException{ byte[] hostbytes = null; if((hostbytes = getKeyBytes(host)) == null) throw new IllegalArgumentException ("invalid host => ["+host+"]"); byte[] portbytes = null; if((portbytes = Convert.toBytes(port)) == null) throw new IllegalArgumentException ("invalid port => ["+port+"]"); this.serviceRequest(Command.SLAVEOF, hostbytes, portbytes); } public void slaveofnone() throws RedisException{ this.serviceRequest(Command.SLAVEOF, "no".getBytes(), "one".getBytes()); } @Override public void rename(K oldkey, K newkey) throws RedisException { byte[] oldkeydata = null; if((oldkeydata = getKeyBytes(oldkey)) == null) throw new IllegalArgumentException ("invalid key => ["+oldkey+"]"); byte[] newkeydata = null; if((newkeydata = getKeyBytes(newkey)) == null) throw new IllegalArgumentException ("invalid key => ["+newkey+"]"); this.serviceRequest(Command.RENAME, oldkeydata, newkeydata); } @Override public boolean renamenx(K oldkey, K newkey) throws RedisException{ byte[] oldkeydata = null; if((oldkeydata = getKeyBytes(oldkey)) == null) throw new IllegalArgumentException ("invalid key => ["+oldkey+"]"); byte[] newkeydata = null; if((newkeydata = getKeyBytes(newkey)) == null) throw new IllegalArgumentException ("invalid key => ["+newkey+"]"); /* boolean ValueRespose */ boolean value = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.RENAMENX, oldkeydata, newkeydata); value = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public byte[] rpoplpush(K srcList, K destList) throws RedisException { byte[] srckeybytes = null; if((srckeybytes = getKeyBytes(srcList)) == null) throw new IllegalArgumentException ("invalid src key => ["+srcList+"]"); byte[] destkeybytes = null; if((destkeybytes = getKeyBytes(destList)) == null) throw new IllegalArgumentException ("invalid dest key => ["+destList+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.RPOPLPUSH, srckeybytes, destkeybytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public void rpush(K key, byte[] value) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); if(value == null) throw new IllegalArgumentException ("null value for list op"); this.serviceRequest(Command.RPUSH, keybytes, value); } @Override public void rpush(K key, String value) throws RedisException { // rpush(key, DefaultCodec.encode(value)); rpush(key, DefaultCodec.encode(value)); } @Override public void rpush(K key, Number value) throws RedisException { rpush(key, String.valueOf(value).getBytes()); } @Override public void rpush (K key, T value) throws RedisException { rpush(key, DefaultCodec.encode(value)); } @Override public boolean sadd(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* boolean ValueRespose */ boolean res = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SADD, keybytes, member); res = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return res; } @Override public boolean sadd (K key, String value) throws RedisException { return sadd (key, DefaultCodec.encode(value)); } @Override public boolean sadd (K key, Number value) throws RedisException { return sadd (key, String.valueOf(value).getBytes()); } @Override public boolean sadd (K key, T value) throws RedisException { return sadd (key, DefaultCodec.encode(value)); } @Override public boolean zadd(K key, double score, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* boolean ValueRespose */ boolean res = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.ZADD, keybytes, Convert.toBytes(score), member); res = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return res; } @Override public boolean zadd (K key, double score, String value) throws RedisException { return zadd (key, score, DefaultCodec.encode(value)); } @Override public boolean zadd (K key, double score, Number value) throws RedisException { return zadd (key, score, String.valueOf(value).getBytes()); } @Override public boolean zadd (K key, double score, T value) throws RedisException { return zadd (key, score, DefaultCodec.encode(value)); } @SuppressWarnings("boxing") @Override public Double zincrby(K key, double score, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* Double BulkResponse */ Double resvalue = null; try { BulkResponse bulkResponse = (BulkResponse) this.serviceRequest(Command.ZINCRBY, keybytes, Convert.toBytes(score), member); if (bulkResponse.getBulkData() != null) resvalue = Convert.toDouble(bulkResponse.getBulkData()); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public Double zincrby (K key, double score, String value) throws RedisException { return zincrby (key, score, DefaultCodec.encode(value)); } @Override public Double zincrby (K key, double score, Number value) throws RedisException { return zincrby (key, score, String.valueOf(value).getBytes()); } @Override public Double zincrby (K key, double score, T value) throws RedisException { return zincrby (key, score, DefaultCodec.encode(value)); } @Override public void save() throws RedisException { this.serviceRequest(Command.SAVE); } // -------- set @Override public void set(K key, byte[] value) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); this.serviceRequest(Command.SET, keybytes, value); } @Override public void set(K key, String value) throws RedisException { set(key, DefaultCodec.encode(value)); } @Override public void set(K key, Number value) throws RedisException { set(key, String.valueOf(value).getBytes()); } @Override public void set (K key, T value) throws RedisException { set(key, DefaultCodec.encode(value)); } @Override public byte[] getset(K key, byte[] value) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.GETSET, keybytes, value); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public byte[] getset(K key, String value) throws RedisException { return getset(key, DefaultCodec.encode(value)); } @Override public byte[] getset(K key, Number value) throws RedisException { return getset(key, String.valueOf(value).getBytes()); } @Override public byte[] getset (K key, T value) throws RedisException { return getset(key, DefaultCodec.encode(value)); } @Override public boolean setnx(K key, byte[] value) throws RedisException{ byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SETNX, keybytes, value); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean setnx(K key, String value) throws RedisException { return setnx(key, DefaultCodec.encode(value)); } @Override public boolean setnx(K key, Number value) throws RedisException { return setnx(key, String.valueOf(value).getBytes()); } @Override public boolean setnx (K key, T value) throws RedisException { return setnx(key, DefaultCodec.encode(value)); } @Override public long append(K key, byte[] value) throws RedisException{ byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long resvalue = -1; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.APPEND, keybytes, value); resvalue = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public long append(K key, String value) throws RedisException { return append(key, DefaultCodec.encode(value)); } @Override public long append(K key, Number value) throws RedisException { return append(key, String.valueOf(value).getBytes()); } @Override public long append (K key, T value) throws RedisException { return append(key, DefaultCodec.encode(value)); } private boolean msetnx(byte[][] mappings) throws RedisException { boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.MSETNX, mappings); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean msetnx(Map keyValueMap) throws RedisException { // KeyCodec codec = DefaultKeyCodec.provider(); byte[][] mappings = new byte[keyValueMap.size()*2][]; int i = 0; for (Entry e : keyValueMap.entrySet()){ mappings[i++] = getKeyBytes(e.getKey()); mappings[i++] = e.getValue(); } return msetnx(mappings); } @Override public boolean msetnx(KeyValueSet.ByteArrays keyValueMap) throws RedisException { return msetnx(keyValueMap.getMappings()); } @Override public boolean msetnx(KeyValueSet.Strings keyValueMap) throws RedisException{ return msetnx(keyValueMap.getMappings()); } @Override public boolean msetnx(KeyValueSet.Numbers keyValueMap) throws RedisException{ return msetnx(keyValueMap.getMappings()); } @Override public boolean msetnx(KeyValueSet.Objects keyValueMap) throws RedisException{ return msetnx(keyValueMap.getMappings()); } @Override public boolean sismember(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* boolean ValueRespose */ boolean value = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SISMEMBER, keybytes, member); value = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public boolean sismember(K key, String value) throws RedisException { return sismember(key, DefaultCodec.encode(value)); } @Override public boolean sismember(K key, Number numberValue) throws RedisException { return sismember (key, String.valueOf(numberValue).getBytes()); } @Override public boolean sismember(K key, T object) throws RedisException { return sismember(key, DefaultCodec.encode(object)); } public boolean smove (K srcKey, K destKey, byte[] member) throws RedisException { byte[] srcKeyBytes = null; if((srcKeyBytes = getKeyBytes(srcKey)) == null) throw new IllegalArgumentException ("invalid key => ["+srcKey+"]"); byte[] destKeyBytes = null; if((destKeyBytes = getKeyBytes(destKey)) == null) throw new IllegalArgumentException ("invalid key => ["+destKey+"]"); /* boolean ValueRespose */ boolean value = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SMOVE, srcKeyBytes, destKeyBytes, member); value = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } public boolean smove (K srcKey, K destKey, String stringValue) throws RedisException { return smove (srcKey, destKey, DefaultCodec.encode(stringValue)); } public boolean smove (K srcKey, K destKey, Number numberValue) throws RedisException { return smove (srcKey, destKey, String.valueOf(numberValue).getBytes()); } public boolean smove (K srcKey, K destKey, T object) throws RedisException { return smove (srcKey, destKey, DefaultCodec.encode(object)); } // ------------------------------------------------------------------------ // Commands operating on hashes // ------------------------------------------------------------------------ public boolean hset(K hashKey, K hashField, byte[] value) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); byte[] hashFieldBytes = null; if((hashFieldBytes = getKeyBytes(hashField)) == null) throw new IllegalArgumentException ("invalid field => ["+hashField+"]"); /* boolean ValueRespose */ boolean response = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.HSET, hashKeyBytes, hashFieldBytes, value); response = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return response; } public boolean hset(K key, K field, String stringValue) throws RedisException { return hset (key, field, DefaultCodec.encode(stringValue)); } public boolean hset(K key, K field, Number numberValue) throws RedisException { return hset (key, field, String.valueOf(numberValue).getBytes()); } public boolean hset(K key, K field, T object) throws RedisException { return hset (key, field, DefaultCodec.encode(object)); } public byte[] hget(K hashKey, K hashField) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); byte[] hashFieldBytes = null; if((hashFieldBytes = getKeyBytes(hashField)) == null) throw new IllegalArgumentException ("invalid field => ["+hashField+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.HGET, hashKeyBytes, hashFieldBytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } public boolean hexists(K hashKey, K hashField) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); byte[] hashFieldBytes = null; if((hashFieldBytes = getKeyBytes(hashField)) == null) throw new IllegalArgumentException ("invalid field => ["+hashField+"]"); boolean resp = false; try { ValueResponse response = (ValueResponse) this.serviceRequest(Command.HEXISTS, hashKeyBytes, hashFieldBytes); resp = response.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Override public boolean hdel(K hashKey, K hashField) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); byte[] hashFieldBytes = null; if((hashFieldBytes = getKeyBytes(hashField)) == null) throw new IllegalArgumentException ("invalid field => ["+hashField+"]"); boolean resp = false; try { ValueResponse response = (ValueResponse) this.serviceRequest(Command.HDEL, hashKeyBytes, hashFieldBytes); resp = response.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Override public long hlen(K hashKey) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); long resp = 0; try { ValueResponse response = (ValueResponse) this.serviceRequest(Command.HLEN, hashKeyBytes); resp = response.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Redis(versions="1.3.n") @Override public List hkeys(K hashKey) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); List multibulkData = null; try { MultiBulkResponse response = (MultiBulkResponse) this.serviceRequest(Command.HKEYS, hashKeyBytes); // if(null != response.getMultiBulkData()) resp = DefaultCodec.toStr(response.getMultiBulkData()); multibulkData = response.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multibulkData; } @Redis(versions="1.3.n") @Override public List hvals(K hashKey) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); List resp = null; try { MultiBulkResponse response = (MultiBulkResponse) this.serviceRequest(Command.HVALS, hashKeyBytes); resp = response.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Redis(versions="1.3.n") @Override public Map hgetall(K hashKey) throws RedisException { byte[] hashKeyBytes = null; if((hashKeyBytes = getKeyBytes(hashKey)) == null) throw new IllegalArgumentException ("invalid key => ["+hashKey+"]"); Map resp = null; try { MultiBulkResponse response = (MultiBulkResponse) this.serviceRequest(Command.HGETALL, hashKeyBytes); List bulkdata = response.getMultiBulkData(); if(null != bulkdata) { resp = new HashMap(bulkdata.size()/2); for(int i=0; i " + e.getLocalizedMessage(), e); } return resp; } /* ------------------------------- commands returning int value --------- */ @Override public long incr(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.INCR, keybytes); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long incrby(K key, int delta) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.INCRBY, keybytes, Convert.toBytes(delta)); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long decr(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ long value = Long.MAX_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.DECR, keybytes); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long decrby(K key, int delta) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ long value = Long.MAX_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.DECRBY, keybytes, Convert.toBytes(delta)); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long llen(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); /* ValueRespose */ /* int since max size is 1GB, an integer 1,073,741,824 */ long value = Integer.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.LLEN, keybytes); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long scard(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SCARD, keybytes); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long zcard(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.ZCARD, keybytes); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } public byte[] srandmember (K setkey) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(setkey)) == null) throw new IllegalArgumentException ("invalid key => ["+setkey+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.SRANDMEMBER, keybytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } public byte[] spop (K setkey) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(setkey)) == null) throw new IllegalArgumentException ("invalid key => ["+setkey+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.SPOP, keybytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } /* ------------------------------- commands returning long value --------- */ @Override public long dbsize() throws RedisException { long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.DBSIZE); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public long lastsave() throws RedisException { long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.LASTSAVE); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } /* ------------------------------- commands returning byte[] --------- */ @Override public byte[] get(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.GET, keybytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public byte[] lindex(K key, long index) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.LINDEX, keybytes, Convert.toBytes(index)); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public byte[] lpop(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.LPOP, keybytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public byte[] rpop(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.RPOP, keybytes); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } /* ------------------------------- commands returning String--------- */ @Override public byte[] randomkey() throws RedisException { /* ValueRespose */ byte[] bulkData = null; // String stringValue = null; try { BulkResponse valResponse = (BulkResponse) this.serviceRequest(Command.RANDOMKEY); bulkData = valResponse.getBulkData(); // if (null != bulkData) { // stringValue = new String(bulkData); // } } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } // return stringValue; return bulkData; } @Override public RedisType type(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); RedisType type = null; /* ValueRespose */ try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.TYPE, keybytes); String stringValue = valResponse.getStringValue(); type = RedisType.valueOf(stringValue); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return type; } @Override public ObjectInfo debug (K key) throws RedisException { byte[] keybytes = getKeyBytes(key); // if(key.length() == 0) // throw new IllegalArgumentException ("invalid zero length key => ["+key+"]"); ObjectInfo objectInfo = null; /* ValueRespose */ try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.DEBUG, "OBJECT".getBytes(), keybytes); String stringValue = valResponse.getStringValue(); objectInfo = ObjectInfo.valueOf(stringValue); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return objectInfo; } /* ------------------------------- commands returning Maps --------- */ @Override public Map info() throws RedisException { byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.INFO); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } StringTokenizer tokenizer = new StringTokenizer(new String(bulkData), "\r\n"); Map infomap = new HashMap(48); while (tokenizer.hasMoreTokens()){ String info = tokenizer.nextToken(); System.out.format("line:<%s>\n", info); // ignore comments "# heading" if(info.startsWith("#")) continue; int c = info.indexOf(':'); String key =info.substring(0, c); String value = info.substring(c+1); infomap.put(key, value); } return infomap; } private void mset(byte[][] mappings) throws RedisException { this.serviceRequest(Command.MSET, mappings); } public void mset(Map keyValueMap) throws RedisException { // KeyCodec codec = DefaultKeyCodec.provider(); byte[][] mappings = new byte[keyValueMap.size()*2][]; int i = 0; for (Entry e : keyValueMap.entrySet()){ mappings[i++] = getKeyBytes(e.getKey()); mappings[i++] = e.getValue(); } mset(mappings); } public void mset(KeyValueSet.ByteArrays keyValueMap) throws RedisException { mset(keyValueMap.getMappings()); } public void mset(KeyValueSet.Strings keyValueMap) throws RedisException{ mset(keyValueMap.getMappings()); } public void mset(KeyValueSet.Numbers keyValueMap) throws RedisException{ mset(keyValueMap.getMappings()); } public void mset(KeyValueSet.Objects keyValueMap) throws RedisException{ mset(keyValueMap.getMappings()); } @Override public List mget(K...keys) throws RedisException { if(null == keys || keys.length == 0) throw new IllegalArgumentException("no keys specified"); byte[] keydata = null; byte[][] keybytes = new byte[keys.length][]; int i=0; for(K k : keys) { if((keydata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"] @ index: " + i); keybytes[i++] = keydata; } List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.MGET, keybytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List smembers(K key) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(key)) == null) throw new RedisException (Command.KEYS, "ERR Invalid key."); List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.SMEMBERS, keydata); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List keys() throws RedisException { return this.keys("*"); } @Override public List keys(K pattern) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(pattern)) == null) throw new RedisException (Command.KEYS, "ERR Invalid key."); List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.KEYS, keydata); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } // return DefaultCodec.toStr(multiBulkData); return multiBulkData; /* byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.KEYS, keydata); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } StringTokenizer tokenizer = new StringTokenizer(new String(bulkData), " "); List keyList = new ArrayList (12); while (tokenizer.hasMoreTokens()){ keyList.add(tokenizer.nextToken()); } return keyList; */ } @Override public List lrange(K key, long from, long to) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); List multiBulkData= null; try { MultiBulkResponse multiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.LRANGE, keybytes, fromBytes, toBytes); multiBulkData = multiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public byte[] substr(K key, long from, long to) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); byte[] bulkData= null; try { BulkResponse bulkResponse = (BulkResponse) this.serviceRequest(Command.SUBSTR, keybytes, fromBytes, toBytes); bulkData = bulkResponse.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public List zrangebyscore (K key, double minScore, double maxScore) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(minScore); byte[] toBytes = Convert.toBytes(maxScore); List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.ZRANGEBYSCORE, keybytes, fromBytes, toBytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List zrangebyscoreSubset (K key, double minScore, double maxScore) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(minScore); byte[] toBytes = Convert.toBytes(maxScore); List list= null; try { MultiBulkResponse multiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.ZRANGEBYSCORE$OPTS, keybytes, fromBytes, toBytes, Command.Option.WITHSCORES.bytes); List bulkData = multiBulkResponse.getMultiBulkData(); if(null != bulkData){ list = new ArrayList(bulkData.size()/2); for(int i=0; i " + e.getLocalizedMessage(), e); } return list; } @Override public long zremrangebyscore (K key, double minScore, double maxScore) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(minScore); byte[] toBytes = Convert.toBytes(maxScore); long resp = Long.MIN_VALUE; try { ValueResponse valueResponse = (ValueResponse) this.serviceRequest(Command.ZREMRANGEBYSCORE, keybytes, fromBytes, toBytes); resp = valueResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a numeric ValueResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Override public long zcount (K key, double minScore, double maxScore) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(minScore); byte[] toBytes = Convert.toBytes(maxScore); long resp = Long.MIN_VALUE; try { ValueResponse valueResponse = (ValueResponse) this.serviceRequest(Command.ZCOUNT, keybytes, fromBytes, toBytes); resp = valueResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a numeric ValueResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Override public long zremrangebyrank (K key, long minRank, long maxRank) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(minRank); byte[] toBytes = Convert.toBytes(maxRank); long resp = Long.MIN_VALUE; try { ValueResponse valueResponse = (ValueResponse) this.serviceRequest(Command.ZREMRANGEBYRANK, keybytes, fromBytes, toBytes); resp = valueResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a numeric ValueResponse here => " + e.getLocalizedMessage(), e); } return resp; } @Override public List zrange(K key, long from, long to) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.ZRANGE, keybytes, fromBytes, toBytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List zrevrange(K key, long from, long to) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.ZREVRANGE, keybytes, fromBytes, toBytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List zrangeSubset(K key, long from, long to) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); List list= null; try { MultiBulkResponse multiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.ZRANGE$OPTS, keybytes, fromBytes, toBytes, Command.Option.WITHSCORES.bytes); List bulkData = multiBulkResponse.getMultiBulkData(); if(null != bulkData){ list = new ArrayList(bulkData.size()/2); for(int i=0; i " + e.getLocalizedMessage(), e); } return list; } @Override public List zrevrangeSubset(K key, long from, long to) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(from); byte[] toBytes = Convert.toBytes(to); List list= null; try { MultiBulkResponse multiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.ZREVRANGE$OPTS, keybytes, fromBytes, toBytes, Command.Option.WITHSCORES.bytes); List bulkData = multiBulkResponse.getMultiBulkData(); if(null != bulkData){ list = new ArrayList(bulkData.size()/2); for(int i=0; i " + e.getLocalizedMessage(), e); } return list; } @Override public Sort sort(final K key) { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); final JRedisSupport client = this; // Sort sortQuery = new SortSupport (key, keybytes) { Sort sortQuery = new SortSupport (keybytes) { @Override protected List execSort(byte[]... fullSortCmd) throws IllegalStateException, RedisException { List multiBulkData= null; try { MultiBulkResponse multiBulkResponse = (MultiBulkResponse) client.serviceRequest(Command.SORT, fullSortCmd); multiBulkData = multiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } protected List execSortStore(byte[]... fullSortCmd) throws IllegalStateException, RedisException { List multiBulkData= new ArrayList(1); try { ValueResponse valueResp = (ValueResponse) client.serviceRequest(Command.SORT$STORE, fullSortCmd); long resSize = valueResp.getLongValue(); multiBulkData.add(Convert.toBytes(resSize)); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override protected Future> execAsyncSort (byte[]... fullSortCmd) { throw new IllegalStateException("JRedis does not support asynchronous sort."); } @Override protected Future> execAsyncSortStore (byte[]... fullSortCmd) { throw new IllegalStateException("JRedis does not support asynchronous sort."); } }; return sortQuery; } /* ------------------------------- commands that don't get a response --------- */ @Override public void quit() { try { this.serviceRequest(Command.QUIT); } catch (RedisException e) { /* NotConnectedException is OK */ e.printStackTrace(); throw new ProviderException ("Quit raised an unexpected RedisException -- Bug"); } // return true; } // @Override // public void shutdown() { // try { // this.serviceRequest(Command.SHUTDOWN); // } // catch (RedisException e) { /* NotConnectedException is OK */ // e.printStackTrace(); // throw new ProviderException ("Shutdown raised an unexpected RedisException -- Bug"); // } //// return true; // } @Override public List sinter(K set1, K... sets) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(set1)) == null) throw new IllegalArgumentException ("invalid key => ["+set1+"]"); byte[][] keybytes = new byte[1+sets.length][]; int i=0; keybytes[i++] = keydata; for(K k : sets) { if((keydata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); keybytes[i++] = keydata; } List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.SINTER, keybytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List sunion(K set1, K... sets) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(set1)) == null) throw new IllegalArgumentException ("invalid key => ["+set1+"]"); byte[][] keybytes = new byte[1+sets.length][]; int i=0; keybytes[i++] = keydata; for(K k : sets) { if((keydata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); keybytes[i++] = keydata; } List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.SUNION, keybytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public List sdiff(K set1, K... sets) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(set1)) == null) throw new IllegalArgumentException ("invalid key => ["+set1+"]"); byte[][] keybytes = new byte[1+sets.length][]; int i=0; keybytes[i++] = keydata; for(K k : sets) { if((keydata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); keybytes[i++] = keydata; } List multiBulkData= null; try { MultiBulkResponse MultiBulkResponse = (MultiBulkResponse) this.serviceRequest(Command.SDIFF, keybytes); multiBulkData = MultiBulkResponse.getMultiBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a MultiBulkResponse here => " + e.getLocalizedMessage(), e); } return multiBulkData; } @Override public void sinterstore(K dest, K... sets) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(dest)) == null) throw new IllegalArgumentException ("invalid key => ["+dest+"]"); byte[][] setbytes = new byte[1+sets.length][]; int i=0; setbytes[i++] = keydata; byte[] setdata =null; for(K k : sets) { if((setdata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); setbytes[i++] = setdata; } this.serviceRequest(Command.SINTERSTORE, setbytes); } @Override public void sunionstore(K dest, K... sets) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(dest)) == null) throw new IllegalArgumentException ("invalid key => ["+dest+"]"); byte[][] setbytes = new byte[1+sets.length][]; int i=0; setbytes[i++] = keydata; byte[] setdata =null; for(K k : sets) { if((setdata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); setbytes[i++] = setdata; } this.serviceRequest(Command.SUNIONSTORE, setbytes); } @Override public void sdiffstore(K dest, K... sets) throws RedisException { byte[] keydata = null; if((keydata = getKeyBytes(dest)) == null) throw new IllegalArgumentException ("invalid key => ["+dest+"]"); byte[][] setbytes = new byte[1+sets.length][]; int i=0; setbytes[i++] = keydata; byte[] setdata =null; for(K k : sets) { if((setdata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"]"); setbytes[i++] = setdata; } this.serviceRequest(Command.SDIFFSTORE, setbytes); } @Override public long del(K ...keys ) throws RedisException { if(null == keys || keys.length == 0) throw new IllegalArgumentException("no keys specified"); byte[] keydata = null; byte[][] keybytes = new byte[keys.length][]; int i=0; for(K k : keys) { if((keydata = getKeyBytes(k)) == null) throw new IllegalArgumentException ("invalid key => ["+k+"] @ index: " + i); keybytes[i++] = keydata; } long resvalue = -1; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.DEL, keybytes); resvalue = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean exists(K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.EXISTS, keybytes); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public void lpush(K key, byte[] value) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); if(value == null) throw new IllegalArgumentException ("null value for list op"); this.serviceRequest(Command.LPUSH, keybytes, value); } @Override public void lpush(K key, String value) throws RedisException { lpush(key, DefaultCodec.encode(value)); } @Override public void lpush(K key, Number value) throws RedisException { lpush(key, String.valueOf(value).getBytes()); } @Override public void lpush (K key, T value) throws RedisException { lpush(key, DefaultCodec.encode(value)); } @Override public long lrem(K key, byte[] value, int count) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] countBytes = Convert.toBytes(count); long remcnt = 0; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.LREM, keybytes, countBytes, value); remcnt = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return remcnt; } @Override public long lrem (K listKey, String value, int count) throws RedisException{ return lrem (listKey, DefaultCodec.encode(value), count); } @Override public long lrem (K listKey, Number numberValue, int count) throws RedisException { return lrem (listKey, String.valueOf(numberValue).getBytes(), count); } @Override public long lrem (K listKey, T object, int count) throws RedisException{ return lrem (listKey, DefaultCodec.encode(object), count); } @Override public void lset(K key, long index, byte[] value) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] indexBytes = Convert.toBytes(index); this.serviceRequest(Command.LSET, keybytes, indexBytes, value); } @Override public void lset (K key, long index, String value) throws RedisException { lset (key, index, DefaultCodec.encode(value)); } @Override public void lset (K key, long index, Number numberValue) throws RedisException{ lset (key, index, String.valueOf(numberValue).getBytes()); } @Override public void lset (K key, long index, T object) throws RedisException{ lset (key, index, DefaultCodec.encode(object)); } @Override public boolean move(K key, int dbIndex) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] toBytes = Convert.toBytes(dbIndex); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.MOVE, keybytes, toBytes); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean srem(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.SREM, keybytes, member); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean srem (K key, String value) throws RedisException { return srem (key, DefaultCodec.encode(value)); } @Override public boolean srem (K key, Number value) throws RedisException { return srem (key, String.valueOf(value).getBytes()); } @Override public boolean srem (K key, T value) throws RedisException { return srem (key, DefaultCodec.encode(value)); } @Override public boolean zrem(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.ZREM, keybytes, member); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean zrem (K key, String value) throws RedisException { return zrem (key, DefaultCodec.encode(value)); } @Override public boolean zrem (K key, Number value) throws RedisException { return zrem (key, String.valueOf(value).getBytes()); } @Override public boolean zrem (K key, T value) throws RedisException { return zrem (key, DefaultCodec.encode(value)); } @SuppressWarnings("boxing") @Override public Double zscore(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); Double resvalue = null; try { BulkResponse bulkResponse = (BulkResponse) this.serviceRequest(Command.ZSCORE, keybytes, member); if (bulkResponse.getBulkData() != null) resvalue = Convert.toDouble(bulkResponse.getBulkData()); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public Double zscore (K key, String value) throws RedisException { return zscore (key, DefaultCodec.encode(value)); } @Override public Double zscore (K key, Number value) throws RedisException { return zscore (key, String.valueOf(value).getBytes()); } @Override public Double zscore (K key, T value) throws RedisException { return zscore (key, DefaultCodec.encode(value)); } @Override public long zrank(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long resvalue = -1; try { ValueResponse bulkResponse = (ValueResponse) this.serviceRequest(Command.ZRANK, keybytes, member); resvalue = bulkResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public long zrank (K key, String value) throws RedisException { return zrank (key, DefaultCodec.encode(value)); } @Override public long zrank (K key, Number value) throws RedisException { return zrank (key, String.valueOf(value).getBytes()); } @Override public long zrank (K key, T value) throws RedisException { return zrank (key, DefaultCodec.encode(value)); } @Override public long zrevrank(K key, byte[] member) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long resvalue = -1; try { ValueResponse bulkResponse = (ValueResponse) this.serviceRequest(Command.ZREVRANK, keybytes, member); resvalue = bulkResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public long zrevrank (K key, String value) throws RedisException { return zrevrank (key, DefaultCodec.encode(value)); } @Override public long zrevrank (K key, Number value) throws RedisException { return zrevrank (key, String.valueOf(value).getBytes()); } @Override public long zrevrank (K key, T value) throws RedisException { return zrevrank (key, DefaultCodec.encode(value)); } @Override public void ltrim(K key, long keepFrom, long keepTo) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] fromBytes = Convert.toBytes(keepFrom); byte[] toBytes = Convert.toBytes(keepTo); this.serviceRequest(Command.LTRIM, keybytes, fromBytes, toBytes); } @Override public boolean expire(K key, int ttlseconds) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); byte[] ttlbytes = Convert.toBytes(ttlseconds); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.EXPIRE, keybytes, ttlbytes); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public boolean expireat(K key, long epochtime) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long expiretime = TimeUnit.SECONDS.convert(epochtime, TimeUnit.MILLISECONDS); byte[] expiretimeBytes = Convert.toBytes(expiretime); boolean resvalue = false; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.EXPIREAT, keybytes, expiretimeBytes); resvalue = valResponse.getBooleanValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return resvalue; } @Override public long ttl (K key) throws RedisException { byte[] keybytes = null; if((keybytes = getKeyBytes(key)) == null) throw new IllegalArgumentException ("invalid key => ["+key+"]"); long value = Long.MIN_VALUE; try { ValueResponse valResponse = (ValueResponse) this.serviceRequest(Command.TTL, keybytes); value = valResponse.getLongValue(); } catch (ClassCastException e){ throw new ProviderException("Expecting a ValueResponse here => " + e.getLocalizedMessage(), e); } return value; } @Override public byte[] echo (byte[] value) throws RedisException { if(value ==null) throw new IllegalArgumentException ("invalid echo value => [null]"); byte[] bulkData= null; try { BulkResponse response = (BulkResponse) this.serviceRequest(Command.ECHO, value); bulkData = response.getBulkData(); } catch (ClassCastException e){ throw new ProviderException("Expecting a BulkResponse here => " + e.getLocalizedMessage(), e); } return bulkData; } @Override public byte[] echo(String value) throws RedisException { return echo(DefaultCodec.encode(value)); } @Override public byte[] echo(Number value) throws RedisException { return echo(String.valueOf(value).getBytes()); } @Override public byte[] echo (T value) throws RedisException { return echo(DefaultCodec.encode(value)); } // ------------------------------------------------------------------------ // Transactional commands // ------------------------------------------------------------------------ /** * one option is to return a subclass of JRedis (e.g. JRedisCommandSequence) * and have that interface declare discard and multi. Benefit is being able * to associate state with the transaction. * @throws RedisException */ @Version(major=2, minor=0, release=Release.ALPHA) public JRedis multi() throws RedisException { throw new ProviderException("NOT IMPLEMENTED"); // // works // this.serviceRequest(Command.MULTI); // return this; } /** * @throws RedisException */ @Version(major=2, minor=0, release=Release.ALPHA) public JRedis discard () throws RedisException { throw new ProviderException("NOT IMPLEMENTED"); // // works // this.serviceRequest(Command.DISCARD); // return this; } // ------------------------------------------------------------------------ // utility // ------------------------------------------------------------------------ // TODO: integrate using KeyCodec and a CodecManager at client spec and init time. // TODO: (implied) ClientSpec (impls. ConnectionSpec) // this isn't cooked yet -- lets think more about the implications... // // static final private Map keyByteCache = new ConcurrentHashMap(); public static boolean CacheKeys = false; public static byte[] getKeyBytes(K key) throws IllegalArgumentException { return DefaultKeyCodec.provider().encode(key); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/Pair.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; /** * Generic immutable 2-tuple data struct. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 20, 2010 * @since alpha.0 * */ class Pair { public final T1 t1; public final T2 t2; public Pair(T1 t1, T2 t2){ this.t1 = t1; this.t2 = t2; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/RedisVersion.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.ri.alphazero; // ///** // * TODO: think about depcrecating this -- there really would be only one version // * of Redis. // *

    // * [TODO: document me!] // * // * @author Joubin Houshyar (alphazero@sensesay.net) // * @version alpha.0, Apr 12, 2009 // * @since alpha.0 // * // */ ////public enum RedisVersion { //// current_revision ("0.09"), //// beta_0_09 ("0.09"); //// public String id; //// public String getId() { return id; } //// private RedisVersion(String id) { //// this.id = id; //// } //// ////} ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/SyncJRedisBase.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.ProviderException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.FaultedConnection; import org.jredis.connector.Connection.Property; import org.jredis.resource.Context; import org.jredis.resource.Resource; import org.jredis.resource.ResourceException; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ public abstract class SyncJRedisBase extends JRedisSupport implements Resource { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Inner ops // ------------------------------------------------------------------------ /** * This extension point is really only necessary to allow this class to * set the {@link FaultedConnection} when necessary, in course of the * {@link SyncJRedisBase#createSyncConnection(String)} * method operation. * * @param connection */ protected abstract void setConnection (Connection connection) ; /** * Creates a {@link Connection} with {@link Connection.Modality#Synchronous} semantics * suitable for use by synchronous (blocking) JRedis clients. * * @param connSpec connection's specification * @param redisVersion redis protocol compliance * @return */ protected Connection createSyncConnection(ConnectionSpec connSpec){ Connection.Factory cfact = (Connection.Factory) connSpec.getConnectionProperty(Property.CONNECTION_FACTORY); Connection conn = null; try { conn = Assert.notNull(cfact.newConnection(connSpec), "connection delegate", ClientRuntimeException.class); } catch (ProviderException e) { Log.bug("Couldn't create the handler delegate. => " + e.getLocalizedMessage()); throw e; } catch (ClientRuntimeException e) { String msg = String.format("%s\nMake sure your server is running.", e.getMessage()); Log.error ("Error creating connection -> " + e.getLocalizedMessage()); setConnection(new FaultedConnection(connSpec, msg)); } Log.debug ("%s: Using %s", this.getClass().getSimpleName(), conn); return conn; } // ------------------------------------------------------------------------ // Interface // =========================================================== Resource /* * Provides basic Resource support without any state management. Extensions * that use context in a simply manner can rely on these methods. Others may * wish to override. */ // ------------------------------------------------------------------------ private Context context; @Override public final Context getContext() throws ResourceException { return context; } @Override public final void setContext(Context context) throws ResourceException { this.context = context; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/ZSetEntryImpl.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import org.jredis.ZSetEntry; import org.jredis.ri.alphazero.support.DefaultCodec; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 20, 2010 * @since alpha.0 * */ class ZSetEntryImpl extends Pair implements ZSetEntry { public ZSetEntryImpl (byte[] valueBytes, byte[] scoreBytes) { super(valueBytes, scoreBytes); } /* (non-Javadoc) @see org.jredis.ZSetEntry#getScore() */ public double getScore () { return DefaultCodec.toDouble(t2); } /* (non-Javadoc) @see org.jredis.ZSetEntry#getValue() */ public byte[] getValue () { return t1;} } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/_specification.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ public interface _specification { /** specification level */ public interface Version { long major = 0xA; long minor = 0x0; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/AsyncConnection.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.io.InputStream; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.NotConnectedException; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Request; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.protocol.ConcurrentSyncProtocol; import org.jredis.ri.alphazero.protocol.VirtualResponse; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.FastBufferedInputStream; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 6, 2009 * @since alpha.0 * */ public class AsyncConnection extends ConnectionBase implements Connection { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ private RequestProcessor processor; /** */ private Thread processerThread; /** */ private BlockingQueue pendingQueue; BlockingQueue getPendingQueue() { return pendingQueue; } // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ public AsyncConnection ( ConnectionSpec connectionSpec ) throws ClientRuntimeException, ProviderException { super (connectionSpec.setModality(Modality.Asynchronous)); // super (connectionSpec); // should be - really need an assert } // ------------------------------------------------------------------------ // Extension // ------------------------------------------------------------------------ /** * */ protected void initializeComponents () { super.initializeComponents(); // serviceLock = new Object(); pendingQueue = new LinkedBlockingQueue(); processor = new RequestProcessor(); processerThread = new Thread(processor, "request-processor"); processerThread.start(); } /** * Pipeline must use a concurrent protocol handler. * * @see org.jredis.ri.alphazero.connection.ConnectionBase#newProtocolHandler() */ @Override protected Protocol newProtocolHandler () { return new ConcurrentSyncProtocol(); } /** * Just make sure its a {@link FastBufferedInputStream}. */ @SuppressWarnings("boxing") @Override protected final InputStream newInputStream (InputStream socketInputStream) throws IllegalArgumentException { InputStream in = super.newInputStream(socketInputStream); if(!(in instanceof FastBufferedInputStream)){ Log.log(String.format("WARN: input was: %s\n", in.getClass().getCanonicalName())); in = new FastBufferedInputStream (in, spec.getSocketProperty(Connection.Socket.Property.SO_RCVBUF)); } return in; } // ------------------------------------------------------------------------ // Interface // ======================================================= ProtocolHandler // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.connector.Connection#getModality() */ public final Modality getModality() { return Connection.Modality.Asynchronous; } /* (non-Javadoc) * @see org.jredis.ri.alphazero.connection.ConnectionBase#queueRequest(org.jredis.protocol.Command, byte[][]) */ @Override public Future queueRequest (Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { if(!isConnected()) throw new NotConnectedException ("Not connected!"); PendingRequest pending = new PendingRequest(cmd, args); pendingQueue.add(pending); return pending; } // ------------------------------------------------------------------------ // Inner Class // ------------------------------------------------------------------------ public final class RequestProcessor implements Runnable { /** * Keeps processing the {@link PendingRequest}s in the pending {@link Queue} * until a QUIT is encountered in the pending queue. Thread will stop after * processing the QUIT response (which is expected to be a {@link VirtualResponse}. *

    * TODO: not entirely clear what is the best way to handle exceptions. *

    * TODO: socket Reconnect in the context of pipelining is non-trivial, and maybe * not even practically possible. (e.g. request n is sent but pipe breaks on * some m (m!=n) response. non trivial. */ public void run () { Log.log("AsyncConnection processor thread <%s> started.", Thread.currentThread().getName()); /** Response handler thread specific protocol handler -- optimize fencing */ Protocol protocol = Assert.notNull (newProtocolHandler(), "the delegate protocol handler", ClientRuntimeException.class); PendingRequest pending = null; final BlockingQueue _pendingQueue = getPendingQueue(); while(true){ try { pending = _pendingQueue.take(); try { Request request = Assert.notNull(protocol.createRequest (pending.cmd, pending.args), "request object from handler", ProviderException.class); request.write(getOutputStream()); pending.response = protocol.createResponse(pending.cmd); pending.response.read(getInputStream()); pending.completion.signal(); if(pending.response.getStatus().isError()) { Log.error ("(Asynch) Error response for " + pending.cmd.code + " => " + pending.response.getStatus().message()); } } catch (ProviderException bug){ Log.error ("ProviderException: " + bug.getLocalizedMessage()); bug.printStackTrace(); pending.setCRE(bug); } catch (ClientRuntimeException cre) { Log.error ("ClientRuntimeException: " + cre.getLocalizedMessage()); cre.printStackTrace(); pending.setCRE(cre); } catch (RuntimeException e){ Log.error("Unexpected RuntimeException ", e); e.printStackTrace(); pending.setCRE(new ProviderException("Unexpected runtime exception in response handler")); pending.setResponse(null); break; } // redis (1.00) simply shutsdown connection even if pending responses // are expected, so quit is NOT sent. we simply close connection on this // end. if(pending.cmd == Command.QUIT) { AsyncConnection.this.disconnect(); break; } } catch (InterruptedException e1) { e1.printStackTrace(); } } Log.log("AsyncConnection processor thread <%s> stopped.", Thread.currentThread().getName()); } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/AsyncPipelineConnection.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import org.jredis.ClientRuntimeException; import org.jredis.connector.ConnectionSpec; /** * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Sep 7, 2009 * @since alpha.0 * */ public class AsyncPipelineConnection extends PipelineConnectionBase{ // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ /** * @param spec * @throws ClientRuntimeException */ public AsyncPipelineConnection (ConnectionSpec spec) throws ClientRuntimeException { super(spec.setModality(Modality.Asynchronous)); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/ChunkedPipelineConnection.java ================================================ /* * Copyright 2009-2012 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import static org.jredis.ri.alphazero.protocol.ProtocolBase.ASCII_ZERO; import static org.jredis.ri.alphazero.protocol.ProtocolBase.COUNT_BYTE; import static org.jredis.ri.alphazero.protocol.ProtocolBase.CRLF; import static org.jredis.ri.alphazero.protocol.ProtocolBase.CRLF_LEN; import static org.jredis.ri.alphazero.protocol.ProtocolBase.SIZE_BYTE; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.NotConnectedException; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Response; import org.jredis.protocol.Command.ResponseType; import org.jredis.ri.alphazero.protocol.ConcurrentSyncProtocol; import org.jredis.ri.alphazero.protocol.ProtocolBase; import org.jredis.ri.alphazero.protocol.VirtualResponse; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.FastBufferedInputStream; import org.jredis.ri.alphazero.support.Log; //import org.jredis.ri.alphazero.support.Concurrent2LockQueue.Node; /** * WIP NOTES: *

    * intended to replace existing pipeline per further tests. This * pipeline: * *

  • - provides maximal throughput for asynchronous feedsto Redis server.
  • *
  • - it is thread-safe.
  • * *

    * Design: *

    * We're basically delegating output throttling concerns to the OS * and the TCP/IP layer using blocking write semantics. The TCP layer * will write MTU sized packets, regardless of actual user data, so * clearly the more we pack per packet, the higher will be the throughput * of the connector. * * @author Joubin * */ public class ChunkedPipelineConnection extends ConnectionBase // TODO: doesn't need the ThreadLocals -- re-think super via ConnectionSpec settings. { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** */ private ResponseHandler respHandler; /** */ private Thread respHandlerThread; /** */ BlockingQueue pendingResponseQueue; /** synchronization object used to serialize request queuing */ private Lock requestlock; /** * flag (default false) indicates if a pending QUIT command is being processed. * If true, any calls to queueRequests will result in a raise runtime exception */ private boolean pendingQuit = false; /** used by the Pipeline to indicate its state. Set to true on connect and false on Quit/Close */ private AtomicBoolean isActive; /** counted down on notifyConnect */ private CountDownLatch connectionEstablished; /** MTU multiples to use as upper bound of the size of the chunk buffer */ private static final int MTU_FACTOR = 2; // TODO: ConnectionSpec me. /** Assuming TCP MTU of 1500 - ~tcp header overhead rounded to nearest power of 8 */ static final int MTU_SIZE = 1488; /** chunk buffer size */ static final int CHUNK_BUFF_SIZE = Math.min(MTU_SIZE * MTU_FACTOR, 0xFFFF); /** minimum request size in bytes -- using PING e.g. 14 b */ static final int MIN_REQ_SIZE = 14; /** Chunk Queue size (slots) */ static final int CHUNK_Q_SIZE = CHUNK_BUFF_SIZE / MIN_REQ_SIZE; /** chunk buffer */ private byte[] chunkbuff; /** chunk [hi:idx | lo:off] control long word */ private int ctl_word; /** Chunk Queue of requests in Chunk buffer */ private PendingCPRequest[] chunkqueue; // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ /** * @param spec * @throws ClientRuntimeException */ // protected public ChunkedPipelineConnection (ConnectionSpec spec) throws ClientRuntimeException { super(spec.setModality(Modality.Asynchronous)); } // ------------------------------------------------------------------------ // Extension // ------------------------------------------------------------------------ /** * */ @SuppressWarnings("boxing") @Override protected void initializeComponents () { spec.setConnectionFlag(Flag.PIPELINE, true); spec.setConnectionFlag(Flag.RELIABLE, true); spec.setConnectionFlag(Flag.SHARED, true); chunkbuff = new byte[CHUNK_BUFF_SIZE]; chunkqueue = new PendingCPRequest[CHUNK_Q_SIZE]; ctl_word = 0; requestlock = new ReentrantLock(false); super.initializeComponents(); // REVU: this is a bit oddly placed .. isActive = new AtomicBoolean(false); connectionEstablished = new CountDownLatch(1); pendingResponseQueue = new Concurrent2LockQueue(); respHandler = new ResponseHandler(); respHandlerThread = new Thread(respHandler, "response-handler"); respHandlerThread.start(); isActive.set(false); // REVU: ? superstitious ? } @Override protected void notifyConnected () { super.notifyConnected(); Log.log("Pipeline <%s> connected", this); isActive.set(true); connectionEstablished.countDown(); } @Override protected void notifyDisconnected () { super.notifyDisconnected(); Log.log("Pipeline <%s> disconnected", this); isActive.set(true); connectionEstablished.countDown(); } /** * Pipeline must use a concurrent protocol handler. * * @see org.jredis.ri.alphazero.connection.ConnectionBase#newProtocolHandler() */ @Override protected Protocol newProtocolHandler () { return new ConcurrentSyncProtocol(); // return new SynchProtocol(); } // @Override // protected OutputStream newOutputStream(OutputStream socketOutputStream) { // return new BufferedOutputStream(socketOutputStream, MTU_SIZE); // } /** * Just make sure its a {@link FastBufferedInputStream}. */ @SuppressWarnings("boxing") @Override protected final InputStream newInputStream (InputStream socketInputStream) throws IllegalArgumentException { InputStream in = super.newInputStream(socketInputStream); if(!(in instanceof FastBufferedInputStream)){ System.out.format("WARN: input was: %s\n", in.getClass().getCanonicalName()); in = new FastBufferedInputStream (in, spec.getSocketProperty(Connection.Socket.Property.SO_RCVBUF)); } return in; } // ------------------------------------------------------------------------ // Interface: Connection // ------------------------------------------------------------------------ /** * This is a true asynchronous method. The actual request write to server * possibly does occur in this method if the connection determines it is * optimal to flush the pipeline buffer, or, if you explicitly had requested * flush via {@link Command#CONN_FLUSH}. *

    * Other item of note is that once a QUIT request has been queued, no further * requests are accepted and a ClientRuntimeException is thrown. * * @see org.jredis.ri.alphazero.connection.ConnectionBase#queueRequest(org.jredis.protocol.Command, byte[][]) */ @Override public final Future queueRequest (Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { if(!isConnected()) throw new NotConnectedException ("Not connected!"); if(pendingQuit) throw new ClientRuntimeException("Pipeline shutting down: Quit in progess; no further requests are accepted."); Protocol protocol = Assert.notNull(getProtocolHandler(), "thread protocol handler", ProviderException.class); // Log.log("protocol %d@%s", protocol.hashCode(), protocol.getClass()); final boolean sendreq = cmd.responseType != ResponseType.VIRTUAL && cmd.responseType != ResponseType.NOP; /* setup send buffer if necessary */ int reqbyteslen = 0; if(sendreq) { reqbyteslen = ProtocolHelper.calcReqBuffSize(cmd, args); } /* PendingCPRequest provides transparent hook to force flush on future get(..) */ final PendingCPRequest queuedRequest = new PendingCPRequest(this, cmd); /* possibly silly optimization, pulled out of sync block */ final OutputStream out = getOutputStream(); final boolean isflush = cmd == Command.CONN_FLUSH; final boolean exceeds = reqbyteslen > CHUNK_BUFF_SIZE; final boolean isquit = cmd == Command.QUIT; /* auth is used on connector initialization and must be sent asap */ final boolean doflush = cmd == Command.AUTH || cmd == Command.SELECT || isquit || isflush; try { requestlock.lock(); /* ======== CRITICAL BLOCK ====== */ /* 4 byte control word is [ idx | off ] */ /* chunk_ctl_word is contended and must be accessed only inside of critical block */ final int __ctl_word = ctl_word; // REVU: opportunity .. int idx = __ctl_word >> 16; int off = __ctl_word & 0x0000FFFF; boolean overflows = exceeds || off + reqbyteslen > CHUNK_BUFF_SIZE ? true : false; if(overflows) { out.write(chunkbuff, 0, off); out.flush(); off = 0; pendingResponseQueue.add(chunkqueue); chunkqueue = new PendingCPRequest[CHUNK_Q_SIZE]; idx = 0; } if(sendreq){ if(exceeds) { /* can optimize and dispense with new byte[] -- only for large payloads */ /* chunkqueue should be empty and idx 0 : assert for now */ out.write(protocol.createRequestBuffer(cmd, args)); out.flush(); chunkqueue[0] = queuedRequest; final PendingCPRequest[] oneoffitem = new PendingCPRequest[1]; oneoffitem[0] = queuedRequest; pendingResponseQueue.add(oneoffitem); } else { // REVU: next optimization step ! // NOTE: this 'new' here is not necessary and is only because of copy/paste from // ProtocolBase (see ProtocolHelper.writeReq..(). ProtocolHelper.writeRequestToBuffer(new ProtocolHelper.Buffer(chunkbuff, off), cmd, args); off+=reqbyteslen; chunkqueue[idx] = queuedRequest; idx++; } } if(doflush) { if(!isquit){ if(off>0){ out.write(chunkbuff, 0, off); out.flush(); off = 0; pendingResponseQueue.add(chunkqueue); chunkqueue = new PendingCPRequest[CHUNK_Q_SIZE]; idx = 0; } } else { pendingQuit = true; isActive.set(false); final PendingCPRequest[] oneoffitem = new PendingCPRequest[1]; oneoffitem[0] = queuedRequest; pendingResponseQueue.add(oneoffitem); } } /* update 4 byte control word [ idx | off ] */ ctl_word = (idx << 16) | (off | 0x0); /* ==END=== CRITICAL BLOCK ====== */ /* REVU: both of these exceptions require setting fault on the cached pendings */ } catch (IOException e) { Log.error("IOException cmd:%s isConnected:%b", cmd.code, isConnected()); this.onConnectionFault(String.format("IOFault (cmd: %s)", cmd.code), true); } catch (ArrayIndexOutOfBoundsException e){ Log.error("on %s", cmd.code); throw new ProviderException("BUG - recheck assumptions ..", e); } finally { requestlock.unlock(); } return queuedRequest; } void onResponseHandlerError (ClientRuntimeException cre, PendingRequest request) { Log.error("Pipeline response handler encountered an error: " + cre.getMessage()); // signal fault onConnectionFault(cre.getMessage(), false); // set execution error for future object request.setCRE(cre); // PendingCPRequest pending = null; while(true){ try { // pending = pendingResponseQueue.remove(); PendingCPRequest[] items = null; items = pendingResponseQueue.remove(); for(PendingCPRequest item : items){ if(item == null) { break; } item.setCRE(cre); Log.error("set pending %s response to error with CRE", item.cmd); } // pending.setCRE(cre); // Log.error("set pending %s response to error with CRE", pending.cmd); } catch (NoSuchElementException empty){ break; } } } // ======================================================================== // Inner Class // ======================================================================== /** * This was pretty much based on Maged M. Michael, and Michael L. Scott's * 2 Lock concurrent queue as described in their paper, "Simple, Fast, * and Practical Non-Blocking and Blocking Concurrent Algorithms" * ({michael, scott}@cs.rochester.edu). * * For the specific use case of {@link ChunkedPipelineConnection}, * we will have 1:1 producer-consumer concurrency and can do * away with the 2 locks of their algorithm and get better performance. * * This class partially supports the {@link BlockingQueue} for the * purposes of the pipeline. It is not intended for general use. * * @author joubin (alphazero@sensesay.net) * @date Dec 9, 2009 (original) * @date Jan 23, 2012 (this version) * */ final static class Concurrent2LockQueue implements BlockingQueue{ /** * @param */ private static final class Node { private volatile E item; private volatile Node next; @SuppressWarnings("unchecked") private static final AtomicReferenceFieldUpdater nextUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Node.class, "next"); @SuppressWarnings("unchecked") private static final AtomicReferenceFieldUpdater itemUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Object.class, "item"); private Node(E x, Node n) { item = x; next = n; } private final E getItem() { return item; } private final void setItem(E update) { itemUpdater.set(this, update); } private final void setNext(Node update) { nextUpdater.set(this, update); } private final Node getNext() { return next; } } // -------------------------------------------------------------- // Properties // -------------------------------------------------------------- private transient volatile Node head = new Node(null, null); private transient volatile Node tail = head; public Concurrent2LockQueue () {} // -------------------------------------------------------------- // INTERFACE: BlockingQueue // -------------------------------------------------------------- /* (non-Javadoc) @see java.util.concurrent.BlockingQueue#offer(java.lang.Object) */ public final boolean offer(E item) { if(null == item) throw new NullPointerException("item"); final Node n = new Node(item, null); Node t = tail; t.setNext(n); tail = n; return true; } /* (non-Javadoc) @see java.util.concurrent.BlockingQueue#poll(long, java.util.concurrent.TimeUnit) */ @Override final public E poll(long timeout, TimeUnit unit) throws InterruptedException { throw new RuntimeException("not supported"); } /* (non-Javadoc) @see java.util.Queue#poll() */ public final E poll () { E item = null; final Node h = head; final Node newhead = h.getNext(); if(newhead != null) { item = newhead.getItem(); head = newhead; newhead.setItem(null); h.setNext(null); } return item; } /* (non-Javadoc) @see java.util.Queue#peek() */ @Override final public E peek () { E item = null; final Node h = head; final Node f = h.getNext(); if(f != null) { item = f.getItem(); } return item; } /* (non-Javadoc) @see java.util.concurrent.BlockingQueue#take() */ @Override final public E take() throws InterruptedException { E item = null; while(true){ item = poll(); if(item != null) break; LockSupport.parkNanos(1L); // magic number -- let's configure this. if(Thread.interrupted()){ Log.error("%s interrupted!", Thread.currentThread().getName()); throw new InterruptedException(); } } return item; } /* (non-Javadoc) @see java.util.Queue#remove() */ @Override final public E remove() { final E item = poll(); if(item == null) throw new NoSuchElementException(); return item; } /* (non-Javadoc) @see java.util.concurrent.BlockingQueue#add(java.lang.Object) */ @Override final public boolean add(E e) { return this.offer(e); } /* -- NOT SUPPORTED -- BEGIN */ @Override final public E element() { throw new RuntimeException("not supported"); } @Override final public boolean addAll(Collection c) { throw new RuntimeException("not supported"); } @Override final public void clear() { throw new RuntimeException("not supported"); } @Override final public boolean contains(Object o) { throw new RuntimeException("not supported"); } @Override final public boolean containsAll(Collection c) { throw new RuntimeException("not supported"); } @Override final public boolean isEmpty() { throw new RuntimeException("not supported"); } @Override final public Iterator iterator() { throw new RuntimeException("not supported"); } @Override final public boolean remove(Object o) { throw new RuntimeException("not supported"); } @Override final public boolean removeAll(Collection c) { throw new RuntimeException("not supported"); } @Override final public boolean retainAll(Collection c) { throw new RuntimeException("not supported"); } @Override final public int size() { throw new RuntimeException("not supported"); } @Override final public Object[] toArray() { throw new RuntimeException("not supported"); } @Override final public T[] toArray(T[] a) { throw new RuntimeException("not supported"); } @Override final public int drainTo(Collection c) { throw new RuntimeException("not supported"); } @Override final public int drainTo(Collection c, int maxElements) { throw new RuntimeException("not supported"); } @Override final public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { throw new RuntimeException("not supported"); } @Override final public void put(E e) throws InterruptedException { throw new RuntimeException("not supported"); } @Override final public int remainingCapacity() { throw new RuntimeException("not supported"); } /* -- NOT SUPPORTED -- END */ } // ======================================================================== // Inner Class // ======================================================================== /** * A not so KISSy reproduction of necessary {@link Protocol} support * from {@link ProtocolBase}. Stays here until update of the interface * to allow for sharing of the codebase. * @author Joubin * */ final static class ProtocolHelper { /* hackedy hack -- to go bye bye soon */ static class Buffer { byte[] b = null; private int off = 0; Buffer(int size){ b = new byte[size]; } Buffer(byte[] b, int off){ this.b = b; this.off = off; } void write (byte[] d){ final int dlen = d.length; System.arraycopy(d, 0, b, off, dlen); off+=dlen; } void write (byte d){ b[off] = d; off++; } byte[] getBytes() { return b; } } public static int calcReqBuffSize (Command cmd, byte[] ... args) throws IllegalArgumentException { byte[] cmdLenBytes = Convert.toBytes(cmd.bytes.length); byte[] lineCntBytes = Convert.toBytes(args.length+1); int bsize = 1 + lineCntBytes.length + CRLF_LEN + 1 + cmdLenBytes.length + CRLF_LEN + cmd.bytes.length + CRLF_LEN; for(int i=0;i */ final static class PendingCPRequest extends PendingRequest { private final ChunkedPipelineConnection pipeline; PendingCPRequest(ChunkedPipelineConnection pipeline, Command cmd) { super(cmd); this.pipeline = pipeline; } @Override final public Response get() throws InterruptedException, ExecutionException { requestFlush(); return super.get(); } @Override final public Response get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { requestFlush(); return super.get(timeout, unit); } final private void requestFlush() { if(cmd != Command.QUIT) { pipeline.queueRequest(Command.CONN_FLUSH); } else { new Exception().printStackTrace(); } } } // ======================================================================== // Inner Class // ======================================================================== /** * Provides the response processing logic as a {@link Runnable}. *

    * TODD: Needs to have a more regulated operating cycle. Right now its just * infinite loop until something goes boom. Not good. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 18, 2009 * @since alpha.0 * */ public final class ResponseHandler implements Runnable, Connection.Listener { // private final AtomicBoolean work_flag; private volatile boolean work_flag; // REVU: checked in every loop - no need for atomic. private final AtomicBoolean alive_flag; private final AtomicReference thread; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ // REVU: this is a complete mess -- TODO: /** * Adds self to the listeners of the enclosing {@link Connection} instance. */ public ResponseHandler () { ChunkedPipelineConnection.this.addListener(this); // this.work_flag = new AtomicBoolean(true); this.work_flag = true; this.alive_flag = new AtomicBoolean(false); this.thread = new AtomicReference(null); } // ------------------------------------------------------------------------ // INTERFACE: Runnable // ------------------------------------------------------------------------ /** * Keeps processing the {@link PendingRequest}s in the pending {@link Queue} * until a QUIT is encountered in the pending queue. Thread will stop after * processing the QUIT response (which is expected to be a {@link VirtualResponse}. *

    * TODO: not entirely clear what is the best way to handle exceptions. *

    * TODO: socket Reconnect in the context of pipelining is non-trivial, and maybe * not even practically possible. (e.g. request n is sent but pipe breaks on * some m (m!=n) response. non trivial. Perhaps its best to assume broken connection * means faulted server, specially given the fact that a pipeline has a heartbeat * so the issue can not be timeout. */ @Override public void run () { thread.compareAndSet(null, Thread.currentThread()); alive_flag.compareAndSet(false, true); /** Response handler thread specific protocol handler -- optimize fencing */ Protocol protocol = Assert.notNull (newProtocolHandler(), "the delegate protocol handler", ClientRuntimeException.class); Log.log("Pipeline <%s> thread for <%s> started.", Thread.currentThread().getName(), ChunkedPipelineConnection.this.toString()); PendingCPRequest pending = null; // while(work_flag.get()){ while(work_flag){ Response response = null; try { PendingCPRequest[] items = null; items = pendingResponseQueue.take(); for(PendingCPRequest item : items) { if(item == null) { break; } pending = item; try { // TODO: here -- simplify REVU: ? response = protocol.createResponse(pending.cmd); response.read(getInputStream()); pending.response = response; pending.completion.signal(); if(response.getStatus().isError()) { Log.error ("(Asynch) Error response for " + pending.cmd.code + " => " + response.getStatus().message()); } } // TODO: REVU: this in context of both connection and (general) errors. catch (ProviderException bug){ Log.bug ("ProviderException: " + bug.getMessage()); onResponseHandlerError(bug, pending); break; } catch (ClientRuntimeException cre) { Log.problem ("ClientRuntimeException: " + cre.getMessage()); onResponseHandlerError(cre, pending); break; } catch (RuntimeException e){ Log.problem ("Unexpected (and not handled) RuntimeException: " + e.getMessage()); onResponseHandlerError(new ClientRuntimeException("Unexpected (and not handled) RuntimeException", e), pending); break; } // REVU: this should be noted on the API spec : TODO: /* QUITs are not sent in pipelines to Redis as it immediately will * drop the connection and there goes the pending stuff. */ if(pending.cmd == Command.QUIT) { ChunkedPipelineConnection.this.disconnect(); break; } } } // REVU: why the general catch -- related to coherence issues of the volatile flags // TODO: review and clean this shit up // catch (InterruptedException e1) { catch (Throwable e1) { Log.log("Pipeline thread interrupted."); break; } } Log.log("Pipeline <%s> thread for <%s> stopped.", Thread.currentThread().getName(), ChunkedPipelineConnection.this); alive_flag.compareAndSet(true, false); } // REVU: TODO: final private void stopHandler() { Log.log("%s stopping handler thread", this); // work_flag.set(false); work_flag = false; thread.get().interrupt(); // PipelineConnectionBase.this.respHandlerThread.interrupt(); } final private void shutdownHandler() { /* * It is not expected that shutdown would get called before * stop, but if it has, this makes sure we first go through * the stop sequence. */ // if(work_flag.get() != false || alive_flag.get() != false) if(work_flag != false || alive_flag.get() != false) stopHandler(); alive_flag.set(false); ChunkedPipelineConnection.this.removeListener(this); Log.log("%s response handler has shutdown", this); } // ------------------------------------------------------------------------ // INTERFACE: Connection.Listener /* * hooks for integrating the response handler thread's state with the * wrapping connection's state through event callbacks. */ // ------------------------------------------------------------------------ // REVU: clean this shit up. TODO: /** * @see org.jredis.connector.Connection.Listener#onEvent(org.jredis.connector.Connection.Event) */ @Override public void onEvent (Event event) { if(event.getSource() != ChunkedPipelineConnection.this) { String msg = String.format("event source [%s] is not this pipeline [%s]", event.getSource(), ChunkedPipelineConnection.this); Log.bug(msg); throw new ProviderException(msg); } // (new Exception()).printStackTrace(); Log.log("Pipeline.ResponseHandler: onEvent %s source: %s", event.getType().name(), event.getSource()); switch (event.getType()){ case CONNECTED: // (re)start break; case DISCONNECTED: // should be stopped now break; case CONNECTING: // no op break; case FAULTED: case DISCONNECTING: stopHandler(); break; case SHUTDOWN: shutdownHandler(); break; } } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/ConnectionBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_BANDWIDTH; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_CONN_TIME; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_LATENCY; import static org.jredis.connector.Connection.Socket.Property.SO_RCVBUF; import static org.jredis.connector.Connection.Socket.Property.SO_SNDBUF; import static org.jredis.connector.Connection.Socket.Property.SO_TIMEOUT; import static org.jredis.ri.alphazero.support.Assert.notNull; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.Connection.Event.Type; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.protocol.DefaultProtocolFactory; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.FastBufferedInputStream; import org.jredis.ri.alphazero.support.Log; /** * This abstract class is responsible for managing the socket connection, and, defining * the template of the Connection for concrete extensions. Given that, it basically * manages all the details of dealing with {@link Socket}, and maintains the reference to * the handler. *

    * Further, it provides the default {@link NotSupportedException} response for the * {@link Connection}'s methods that the extending classes of various {@link Connection.Modality} * are expected to support. (They would simply implement the method that they support.) * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 20, 2009-2011 * @since alpha.0 * */ public abstract class ConnectionBase implements Connection{ // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** * Thread specific protocol handler -- optimize fencing * Protocol specific matters are delegated to an instance of {@link Protocol} */ ThreadLocal thrdProtocol = new ThreadLocal(); /** Connection specs used to create this {@link Connection} */ final protected ConnectionSpec spec; private boolean isConnected = false; /** socket reference -- a new instance obtained in {@link ConnectionBase#newSocketConnect()} */ private java.net.Socket socket; private InputStream instream; private OutputStream outstream; /** Connector Listeners */ final private Set listeners = new HashSet(); // /** Keep-alive heartbeat daemon thread */ // private HeartbeatJinn heartbeat; /** address of the socket connection */ private final InetSocketAddress socketAddress; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Will create and initialize a socket per the connection spec. * @See {@link ConnectionSpec} * @param spec * @param connectImmediately will connect the socket immediately if true * @throws ClientRuntimeException if connection attempt to specified host is not possible and * connect immediate was requested. */ protected ConnectionBase (ConnectionSpec spec) throws ClientRuntimeException { try { this.spec = notNull(spec, "ConnectionSpec init parameter", ClientRuntimeException.class); socketAddress = new InetSocketAddress(spec.getAddress(), spec.getPort()); initializeComponents(); } catch (IllegalArgumentException e) { throw new ClientRuntimeException ("invalid connection spec parameters: " + e.getLocalizedMessage(), e); } catch (Exception e) { throw new ProviderException("Unexpected error on initialize -- BUG", e); } if (spec.getConnectionFlag(Flag.CONNECT_IMMEDIATELY)) { connect (); } } // ------------------------------------------------------------------------ // Interface // ============================================================ Connection /* * These are the extension points for the concrete connection classes. */ // ------------------------------------------------------------------------ @Override public ConnectionSpec getSpec () { return this.spec; } @Override public Response serviceRequest(Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { throw new NotSupportedException ( "Response.serviceRequest(Command cmd, " + "byte[]...) is not supported."); } @Override public Future queueRequest(Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { throw new NotSupportedException ( "Response.serviceRequest(RequestListener requestListener, " + "Object , Command, byte[]...) is not supported."); } // ------------------------------------------------------------------------ // Event management /** * Optional * @param connListener * @return */ final public boolean addListener(Listener connListener){ return listeners.add(connListener); } /** * Optional * @param connListener * @return */ final public boolean removeListener(Listener connListener){ return listeners.remove(connListener); } // ------------------------------------------------------------------------ // Internal ops : Extension points // ------------------------------------------------------------------------ /** * Extension point: child classes may override for additional components: *

         * In the extended class:
         * 
         * protected void initializeComponents() {
         *    super.initializeComponents();
         *    // my components here ...
         *    //
         * }
         * 
         * 
    */ protected void initializeComponents () { // setProtocolHandler (Assert.notNull (newProtocolHandler(), "the delegate protocol handler", ClientRuntimeException.class)); if(spec.getConnectionFlag(Connection.Flag.RELIABLE)){ Log.log("WARNING: heartbeat is disabled."); // heartbeat = new HeartbeatJinn(this, this.spec.getHeartbeat(), " [" + this + "] heartbeat"); // heartbeat.start(); } } /** * Extension point -- callback on this method when {@link ConnectionBase} has connected to server. * It is important to note that the extension must call super.notifyConnected if reliable service (using * heartbeats) is required!. */ protected void notifyConnected () { notifyListeners(new Event(this, Type.CONNECTED)); } /** * Extension point -- callback on this method when {@link ConnectionBase} has disconnected from server. * It is important to note that the extension must call super.notifyDisconnected if reliable service (using * heartbeats) is required!. */ protected void notifyDisconnected () { notifyListeners(new Event(this, Type.DISCONNECTED)); } protected void notifyFaulted (String info) { notifyListeners(new Event(this, Type.FAULTED, info)); } protected void notifyShuttingDown () { notifyListeners(new Event(this, Type.SHUTDOWN)); } /** * Extension point: child classes may override to return specific {@link Protocol} implementations per their requirements. * @return */ protected Protocol newProtocolHandler () { Protocol.Factory protfac = (Protocol.Factory) spec.getConnectionProperty(Property.PROTOCOL_FACTORY); if(protfac == null) protfac = new DefaultProtocolFactory(); return protfac.newProtocol(spec); // // return (new DefaultProtocolFactory()).newProtocol(spec); } /** * Extension point: override to return stream per requirement. Base implementation uses {@link FastBufferedInputStream} by default, * with buffer size matching the SO_RCVBUF property of the {@link Connection}'s {@link ConnectionSpec} * @param socketInputStream * @return */ @SuppressWarnings("boxing") protected InputStream newInputStream(InputStream socketInputStream) { return new FastBufferedInputStream(socketInputStream, spec.getSocketProperty(SO_RCVBUF)); } /** * Extension point: override to return stream per requirement. Base implementation simply returns the input param * @param socketOutputStream * @return */ protected OutputStream newOutputStream(OutputStream socketOutputStream) { return socketOutputStream; } // ------------------------------------------------------------------------ // Inner ops: event management // ------------------------------------------------------------------------ final protected void notifyListeners(Connection.Event e) { for(Connection.Listener l : listeners) l.onEvent(e); } // ------------------------------------------------------------------------ // Inner ops: socket and connection management // ------------------------------------------------------------------------ /** @return connected status*/ protected final boolean isConnected () { return isConnected; } /** * Attempt reconnect. Must be in a (previously) connected state when called. * @throws IllegalStateException if not (logically) connected. */ protected final void reconnect () { Log.log("RedisConnection - reconnecting"); int attempts = 0; while(true){ try { disconnect(); connect (); break; } catch (RuntimeException e){ Log.error("while attempting reconnect: " + e.getMessage()); if(++attempts == spec.getReconnectCnt()) { Log.problem("Retry limit exceeded attempting reconnect."); String faultmsg = String.format("Reconnect retry limit exceeded. Failed to reconnect to the server after %d reconnect attempts", attempts); onConnectionFault(faultmsg, true); } } } } /** * Will throw a {@link ClientRuntimeException} if raiseEx is true * @throws IllegalStateException */ protected final void onConnectionFault (String fault, boolean raiseEx) throws ClientRuntimeException { notifyFaulted(fault); Log.problem("Shutting down due to connection FAULT: %s - %s", fault, this); // notifyShuttingDown(); shutdown(); // REVU: best to add a pending shutdown flag .. if(raiseEx) throw new ClientRuntimeException(fault); } /** * @throws IOException * @throws IllegalStateException */ protected final void connect () throws IllegalStateException, ClientRuntimeException { // we're not connected Assert.isTrue (!isConnected(), IllegalStateException.class); // create new socket and connect // try { newSocketConnect(); } catch (IOException e) { String faultmsg = String.format("Socket connect failed [cause: %s] -- make sure the server is running at %s:%d", e, spec.getAddress().getHostName(), spec.getPort()); onConnectionFault(faultmsg, true); // throw new ClientRuntimeException( // "Socket connect failed -- make sure the server is running at " + spec.getAddress().getHostName(), e); } // get the streams // try { initializeSocketStreams (); } catch (IOException e) { throw new ClientRuntimeException("Error obtaining connected socket's streams ", e); } isConnected = true; try { initializeOnConnect(); } catch (RedisException e) { // either authorize or db select is using invalid parameters // which is user error throw new IllegalArgumentException("Failed to connect -- check credentials and/or database settings for the connection spec", e); } Log.debug ("CONNECTED | conn: %s", toString()); notifyConnected(); } /** * @throws IllegalStateException */ protected final void disconnect () throws IllegalStateException { Assert.isTrue (isConnected(), IllegalStateException.class); // REVU: client threads will keep banging on faulted pipelines ... socketClose(); isConnected = false; notifyDisconnected(); Log.debug ("DISCONNECTED | conn: %s", toString()); } /** * @throws IllegalStateException */ protected final void shutdown () throws IllegalStateException { notifyShuttingDown(); /* client threads can be banging on a connection and queued up * on a lock, so isConnected() may have been true * before they acquired it. */ if(isConnected) disconnect(); } /** * Instantiates a new {@link Socket}, sets its properties and flags using the {@link ConnectionBase#spec} * and finally connects to the {@link ConnectionBase#socketAddress}. *

    * Note that if the platform default send and receive buffers are larger than that specified, this method * will not use the (smaller) values defined in the spec. *

    * Further note that method will not check connection state. * * @throws IOException thrown by the socket object. */ @SuppressWarnings("boxing") private final void newSocketConnect () throws IOException { socket = new java.net.Socket (); socket.setKeepAlive ( spec.getSocketFlag (Connection.Socket.Flag.SO_KEEP_ALIVE)); socket.setPerformancePreferences( spec.getSocketProperty (SO_PREF_CONN_TIME), spec.getSocketProperty (SO_PREF_LATENCY), spec.getSocketProperty (SO_PREF_BANDWIDTH)); socket.setSoTimeout( spec.getSocketProperty(SO_TIMEOUT)); if(socket.getSendBufferSize() < spec.getSocketProperty(SO_SNDBUF)) socket.setSendBufferSize(spec.getSocketProperty(SO_SNDBUF)); if(socket.getReceiveBufferSize() < spec.getSocketProperty(SO_RCVBUF)) socket.setReceiveBufferSize(spec.getSocketProperty(SO_RCVBUF)); socket.connect(socketAddress); // Log.log("RedisConnection - socket connected to %s:%d", socketAddress.getHostName(), port); } /** * */ private final void socketClose () { try { if(null != socket) socket.close(); } catch (IOException e) { Log.error("[IO] on closeSocketConnect -- socketClose() continues ..." + e.getLocalizedMessage()); } finally { socket = null; instream = null; outstream = null; } } /** * @throws IllegalStateException if socket is null * @throws IOException thrown by socket instance stream accessors */ protected final void initializeSocketStreams() throws IllegalArgumentException, IOException { instream = newInputStream (Assert.notNull(socket.getInputStream(), "socket input stream", IllegalArgumentException.class)); Assert.notNull(instream, "input stream provided by extended class", IllegalArgumentException.class); outstream = newOutputStream (Assert.notNull(socket.getOutputStream(), "socket output stream", IllegalArgumentException.class)); } /** * @throws RedisException * @throws ClientRuntimeException * @throws ProviderException * */ protected final void initializeOnConnect () throws ProviderException, ClientRuntimeException, RedisException{ switch (spec.getModality()){ case Asynchronous: initializeAsyncConnection(); break; case Synchronous: initializeSyncConnection(); break; default: throw new ProviderException("Modality " + spec.getModality().name() + " is not supported."); } } /** * @throws ProviderException * @throws ClientRuntimeException * @throws RedisException */ protected final void initializeSyncConnection () throws ProviderException, ClientRuntimeException, RedisException{ if(null!=spec.getCredentials()) { this.serviceRequest(Command.AUTH, spec.getCredentials()); } if(spec.getDatabase() != 0) { this.serviceRequest(Command.SELECT, Convert.toBytes(spec.getDatabase())); } } /** * @throws ProviderException * @throws ClientRuntimeException * @throws RedisException */ protected final void initializeAsyncConnection () throws ProviderException, ClientRuntimeException, RedisException{ try { if(null!=spec.getCredentials()) { this.queueRequest(Command.AUTH, spec.getCredentials()).get(); } if(spec.getDatabase() != 0) { this.queueRequest(Command.SELECT, Convert.toBytes(spec.getDatabase())).get(); } } catch (InterruptedException e) { e.printStackTrace(); throw new ClientRuntimeException("Interrupted while initializing asynchronous connection", e); } catch (ExecutionException e) { e.printStackTrace(); if(e.getCause() != null){ if(e.getCause() instanceof RedisException) throw (RedisException) e.getCause(); else if(e.getCause() instanceof ProviderException) throw (ProviderException) e.getCause(); else if(e.getCause() instanceof ClientRuntimeException) throw (ClientRuntimeException) e.getCause(); } throw new ProviderException("Exception while initializing asynchronous connection", e); } } @SuppressWarnings("boxing") @Override public String toString() { // String selfdesc = String.format("%s@%d", getClass().getSimpleName(), hashCode()); return String.format("Connection: %-12s %s:%d db:%d | %s@%d", spec.getModality().name().toUpperCase(), spec.getAddress(), spec.getPort(), spec.getDatabase(), getClass().getSimpleName(), hashCode()); } // ------------------------------------------------------------------------ // Property accessors // ------------------------------------------------------------------------ // final protected void setProtocolHandler(Protocol protocolHandler) { // new Exception().printStackTrace(); // System.out.format(">>>>>>>>>>> PROTOCOL HANDLER: %s\n", protocolHandler); // if(protocolHandler == null) throw new ProviderException("protocol handler is null - BUG"); // thrdProtocol.set(protocolHandler); // this.protocol = notNull(protocolHandler, "protocolHandler for ConnectionBase", ClientRuntimeException.class); // } final protected Protocol getProtocolHandler() { if(thrdProtocol.get() == null) { final Protocol protocol = Assert.notNull (newProtocolHandler(), "the delegate protocol handler", ClientRuntimeException.class); thrdProtocol.set(protocol); } // System.out.format("<<<<<<<<<<< PROTOCOL HANDLER: %s\n", thrdProtocol.get()); return notNull(thrdProtocol.get(), "protocolHandler for ConnectionBase", ClientRuntimeException.class); } final protected OutputStream getOutputStream() { return outstream; } final protected InputStream getInputStream() { return instream; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/DefaultConnectionFactory.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.security.ProviderException; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.connector.Connection; import org.jredis.connector.Connection.Flag; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 15, 2010 * */ public class DefaultConnectionFactory implements Connection.Factory { /* (non-Javadoc) @see org.jredis.connector.Connection.Factory#newConnection(org.jredis.connector.ConnectionSpec) */ public Connection newConnection (ConnectionSpec spec) throws ClientRuntimeException, NotSupportedException { Connection conn = null; switch (spec.getModality()){ case Monitor: throw new ProviderException("NOT IMPLEMENTED!"); case PubSub: throw new ProviderException("NOT IMPLEMENTED!"); case Asynchronous: conn = newAsyncConnection(spec); break; case Synchronous: conn = new SyncConnection(spec); break; } // TODO: factories create completed products -- // this class needs to set conn settings for ALL connection types // // if(spec.getConnectionFlag(Flag.CONNECT_IMMEDIATELY)) { // ((ConnectionBase)conn).initialize (); // } Log.debug("Created new %s", conn); return conn; } /** * Creates a new {@link Connection.Modality#Asynchronous} {@link Connection} * per {@link ConnectionSpec} settings. * @param spec */ private Connection newAsyncConnection (ConnectionSpec spec) { Connection conn = null; if(spec.getConnectionFlag(Flag.PIPELINE)){ conn = new AsyncPipelineConnection(spec); // why not for all asyncs? } else { if(spec.getConnectionFlag(Flag.SHARED)){ throw new ProviderException("NOT IMPLEMENTED! [Asynch|SHARED|not_PIPELINE]"); } else { conn = new AsyncConnection(spec); // conn = new AsyncPipelineConnection(spec); // why not for all asyncs? } } return conn; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/DefaultConnectionSpec.java ================================================ package org.jredis.ri.alphazero.connection; import static org.jredis.connector.Connection.Flag.CONNECT_IMMEDIATELY; import static org.jredis.connector.Connection.Flag.PIPELINE; import static org.jredis.connector.Connection.Flag.RELIABLE; import static org.jredis.connector.Connection.Flag.SHARED; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_BANDWIDTH; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_CONN_TIME; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_LATENCY; import static org.jredis.connector.Connection.Socket.Property.SO_RCVBUF; import static org.jredis.connector.Connection.Socket.Property.SO_SNDBUF; import static org.jredis.connector.Connection.Socket.Property.SO_TIMEOUT; import java.net.InetAddress; import java.net.UnknownHostException; import org.jredis.ClientRuntimeException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.Connection.Flag; import org.jredis.connector.Connection.Modality; import org.jredis.ri.alphazero.protocol.DefaultProtocolFactory; import org.jredis.ri.alphazero.support.Assert; /** * Default connection spec provides the following default values for a connection. See * {@link ConnectionSpec} for details of these properties and flags. *

    * The default {@link Modality} for the {@link Connection} is {@link Modality#Synchronous}. *

    * This {@link ConnectionSpec} is configured to prefer bandwidth and relatively large (48K) * buffers (which is probably less than your OS's default buffer sizes anyway, but you never know). *

    * Connection Retry limit is {@link DefaultConnectionSpec#DEFAULT_RECONNECT_CNT}. *

    * Connection is spec'd for Keep Alive. *

    * Socket timeout is {@link DefaultConnectionSpec#DEFAULT_READ_TIMEOUT_MSEC} *

    * No codecs and/or compression classes are defined. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 20, 2009 * @since alpha.0 * */ final public class DefaultConnectionSpec extends ConnectionSpec.RefImpl { // ------------------------------------------------------------------------ // Consts // ------------------------------------------------------------------------ static final int DEFAULT_REDIS_PORT = 6379; static final String DEFAULT_REDIS_HOST_NAME = "localhost"; static final int DEFAULT_REDIS_DB = 0; static final byte[] DEFAULT_REDIS_PASSWORD = null; /** def value: 48KB */ private static final int DEFAULT_RCV_BUFF_SIZE = 1024 * 48; /** def value: 48KB */ private static final int DEFAULT_SND_BUFF_SIZE = 1024 * 48; /** def value: 5000 msecs */ static final int DEFAULT_READ_TIMEOUT_MSEC = 5000; /** def value: 1 sec. (min timeout in redis conf */ static final int DEFAULT_HEARTBEAT_SEC = 1; /** def value: 0 e.g. higher priority pref is bandwidth */ private static final int DEFAULT_SO_PREF_BANDWIDTH = 0; /** def value: 1 e.g. second priority pref is latency */ private static final int DEFAULT_SO_PREF_LATENCY = 1; /** def value: 3 e.g. third priority pref is connection time */ private static final int DEFAULT_SO_PREF_CONN_TIME = 2; /** def value: true */ private static final boolean DEFAULT_CF_SHARED = true; /** def value: false */ private static final boolean DEFAULT_CF_RELIABLE = false; /** def value: false */ private static final boolean DEFAULT_CF_PIPELINE = false; /** def value: true */ private static final boolean DEFAULT_CF_CONNECT_IMMEDIATELY = true; /** def value: true */ private static final boolean DEFAULT_CF_STATEFUL = false; /** def value: Modality.Synchronous */ private static final Modality DEFAULT_CP_CONN_MODALITY = Modality.Synchronous; /** def value: 3 */ private static final int DEFAULT_CP_MAX_CONNECT_ATTEMPT = 3; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Instantiates a default connection spec for the given host and port. * @param address * @param port * @throws ClientRuntimeException for invalid port, or null address values */ public DefaultConnectionSpec () throws ClientRuntimeException { // Log.debug("Yo!"); setDefaultValues(); } /** * Instantiates a default connection spec for the given host and port. * @param address * @param port * @throws ClientRuntimeException for invalid port, or null address values */ @Deprecated public DefaultConnectionSpec (InetAddress address, int port) throws ClientRuntimeException { this (address, port, 0, null); } /** * Instantiates a default connection spec for the given host and port. * @param address * @param port * @param database * @param credentials * @throws ClientRuntimeException for invalid port, or null address values */ @Deprecated public DefaultConnectionSpec (InetAddress address, int port, int database, byte[] credentials) throws ClientRuntimeException { this(); setPort(Assert.inRange (port, 1, 65534, "port init parameter for DefaultConnectionSpec", ClientRuntimeException.class)); setAddress(Assert.notNull(address, "address init parameter for DefaultConnectionSpec", ClientRuntimeException.class)); setDatabase(database); setCredentials(credentials); } /** * Set the default values for the {@link ConnectionSpec} * other properties. * @see ConnectionSpec * @see Connection.Property * @see Connection.Property * @see Connection.Flag * @see Connection.Socket.Flag * @see Connection.Socket.Property * @see DefaultConnectionFactory * @see DefaultProtocolFactory */ private void setDefaultValues () { // // reconnect try count // setReconnectCnt(DEFAULT_CP_MAX_CONNECT_ATTEMPT); // tcp socket flags setSocketFlag(Connection.Socket.Flag.SO_KEEP_ALIVE, true); // tcp socket flags setSocketProperty(SO_TIMEOUT, DEFAULT_READ_TIMEOUT_MSEC); setSocketProperty(SO_RCVBUF, DEFAULT_RCV_BUFF_SIZE); setSocketProperty(SO_SNDBUF, DEFAULT_SND_BUFF_SIZE); setSocketProperty(SO_PREF_BANDWIDTH, DEFAULT_SO_PREF_BANDWIDTH); setSocketProperty(SO_PREF_CONN_TIME, DEFAULT_SO_PREF_CONN_TIME); setSocketProperty(SO_PREF_LATENCY, DEFAULT_SO_PREF_LATENCY); setConnectionFlag(RELIABLE, DEFAULT_CF_RELIABLE); setConnectionFlag(SHARED, DEFAULT_CF_SHARED); setConnectionFlag(PIPELINE, DEFAULT_CF_PIPELINE); setConnectionFlag(CONNECT_IMMEDIATELY, DEFAULT_CF_CONNECT_IMMEDIATELY); setConnectionFlag(Flag.STATEFUL, DEFAULT_CF_STATEFUL); setConnectionProperty(Connection.Property.MODALITY, DEFAULT_CP_CONN_MODALITY); setConnectionProperty(Connection.Property.MAX_CONNECT_ATTEMPT, DEFAULT_CP_MAX_CONNECT_ATTEMPT); setConnectionProperty(Connection.Property.PROTOCOL_FACTORY, new DefaultProtocolFactory()); setConnectionProperty(Connection.Property.CONNECTION_FACTORY, new DefaultConnectionFactory()); setHeartbeat(DEFAULT_HEARTBEAT_SEC); } // ------------------------------------------------------------------------ // Static methods // ------------------------------------------------------------------------ /** * @return * @throws ClientRuntimeException */ public static final ConnectionSpec newSpec () throws ClientRuntimeException { return newSpec (DEFAULT_REDIS_HOST_NAME, DEFAULT_REDIS_PORT, DEFAULT_REDIS_DB, DEFAULT_REDIS_PASSWORD); } /** * Returns an instance of the {@link ConnectionSpec} used by this {@link Connection} * as default spec, for the provided params. * @param host * @param port * @param database * @param credentials * @param redisversion * @return * @throws ClientRuntimeException */ public static final ConnectionSpec newSpec ( String host, int port, int database, byte[] credentials ) throws ClientRuntimeException { InetAddress address; try { address = InetAddress.getByName(host); } catch (UnknownHostException e) { throw new ClientRuntimeException("unknown host: " + host, e); } return newSpec(address, port, database, credentials); } /** * Returns an instance of the {@link ConnectionSpec} used by this {@link Connection} * as default spec, for the provided params. * @param address * @param port * @param database * @param credentials * @param redisversion * @return * @throws ClientRuntimeException */ public static final ConnectionSpec newSpec ( InetAddress address, int port, int database, byte[] credentials ) throws ClientRuntimeException { // return new DefaultConnectionSpec(address, port, database, credentials); ConnectionSpec spec = new DefaultConnectionSpec(); return spec.setAddress(address).setPort(port).setDatabase(database).setCredentials(credentials); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/HeartbeatJinn.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.security.ProviderException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import org.jredis.ClientRuntimeException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.Connection.Event; import org.jredis.connector.Connection.Modality; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.support.Log; /** * A demon thread tasked with PINGing the associated connection * per its {@link ConnectionSpec#getHeartbeat()} heartbeat interval. *

    * {@link HeartbeatJinn}s are {@link Connection.Listener}s, and rely * on {@link Connection} event propagation to synchronize their activity * with the associated connection. *

    * The connection must only call {@link Thread#start()} when it has * established its connectivity. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 22, 2009 * @since alpha.0 * */ public class HeartbeatJinn extends Thread implements Connection.Listener{ /** */ AtomicBoolean connected; /** */ AtomicBoolean mustBeat; /** */ private final Modality modality; /** */ private final Connection conn; /** */ private final int period; /** * Instantiate and initialize the HeartbeatJinn. On return, this instance * is: *

  • sets flag to work *
  • added to the listeners for the connection *
  • assumes connection is not yet established * * @param conn associated with this instnace * @param periodInSecs a reasonable value is 1. Internally converted to millisecs. * @param name associated with this (heartbeat) thread. */ public HeartbeatJinn (Connection conn, int periodInSecs, String name) { super (name); setDaemon(true); this.conn = conn; conn.addListener(this); this.modality = conn.getSpec().getModality(); this.period = periodInSecs * 1000; this.connected = new AtomicBoolean(false); this.mustBeat = new AtomicBoolean(true); } /** * */ public void shutdown() { mustBeat.set(false); this.interrupt(); } // ------------------------------------------------------------------------ // INTERFACE /* ====================================================== Thread (Runnable) * */ // ------------------------------------------------------------------------ /** * Your basic infinite loop with branchings on connection state and modality *

    * TODO: run loop should be a proper state machine. * TODO: delouse this baby .. * * @see java.lang.Thread#run() */ public void run () { // if (conn.getSpec().getLogLevel()==ConnectionSpec.LogLevel.DEBUG) Log.debug("HeartbeatJinn thread <%s> started.", Thread.currentThread().getName()); while (mustBeat.get()) { try { if(connected.get()){ // << buggy: quit needs to propagate down here. Response response = null; try { switch (modality){ case Asynchronous: Future fResponse = conn.queueRequest(Command.PING); response = fResponse.get(); break; case Synchronous: response = conn.serviceRequest(Command.PING); break; case Monitor: case PubSub: throw new ProviderException(String.format("%s connector not supported", modality.name())); } if(!response.isError()){ // if(conn.getSpec().getLogLevel().equals(LogLevel.DEBUG)) Log.debug (String.format("<%s> is alive", conn)); } else { String errmsg = String.format("Error response on PING: %s", response.getStatus().toString()); Log.error(errmsg); throw new ClientRuntimeException(errmsg); // NOTE: can't be sure this is a protocol BUG .. so CRE instead } } catch (Exception e) { // addressing buggy above. notifyDisconnected gets called after we have checked it but before we // made the call - it is disconnected by the time the call is made and we end up here // checking the flag again and if it is indeed not the above scenario then there is something wrong, // otherwise ignore it and basically loop on sleep until we get notify on connect again (if ever). if(connected.get()){ // how now brown cow? we'll log it for now and assume reconnect try in progress and wait for the flag change. Log.problem("HeartbeatJinn thread <" + Thread.currentThread().getName() + "> encountered exception on PING: " + e.getMessage() ); // connected.set(false); } } } // Log.debug("Looping : <%s>", conn); sleep (period); // sleep regardless - } catch (InterruptedException e) { // if (conn.getSpec().getLogLevel()==ConnectionSpec.LogLevel.DEBUG) Log.debug ("HeartbeatJinn thread <%s> interrupted.", Thread.currentThread().getName()); break; } } Log.log("HeartbeatJinn thread <%s> stopped.", Thread.currentThread().getName()); } // ------------------------------------------------------------------------ // INTERFACE /* =================================================== Connection.Listener * * hooks for integrating the heartbeat thread's state with the associated * connection's state through event callbacks. */ // ------------------------------------------------------------------------ /** * * @see org.jredis.connector.Connection.Listener#onEvent(org.jredis.connector.Connection.Event) */ // TODO: let's hook this up. public void onEvent (Event event) { // if (conn.getSpec().getLogLevel()==ConnectionSpec.LogLevel.DEBUG) Log.debug("onEvent %s : <%s>", event.getType().name(), this); switch (event.getType()){ case CONNECTING: break; case CONNECTED: connected.set(true); break; case DISCONNECTING: case DISCONNECTED: connected.set(false); break; case FAULTED: // shutdown(); // REVU: this is wrong. break; case SHUTDOWN: shutdown(); break; // case STOPPING: // break; } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/PendingRequest.java ================================================ /* * Copyright 2009-2011 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.protocol.Command; import org.jredis.protocol.Request; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.support.Signal; /** * An implementation of {@link Future} for parameteric T type {@link Response} * used for processing of pipelined responses from the server. *

    * Note that this implementation does NOT support canceling of {@link Request}s. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Sep 7, 2009 * @since alpha.0 * */ public class PendingRequest implements Future { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** Used to signal completion of the request */ // final private BooleanLatch completion = new BooleanLatch(); final Signal completion = new Signal(); /** awaited response */ Response response; /** Pending command */ final Command cmd; /** true if response processor encountered exceptions */ private boolean excepted = false; /** if {@link PendingRequest#excepted} is true, this will be set to the cause. */ private ClientRuntimeException cre = null; final byte[][] args; // ------------------------------------------------------------------------ // constructor(s) // ------------------------------------------------------------------------ public PendingRequest(Command cmd){ this.cmd = cmd; this.args = null; } public PendingRequest(Command cmd, byte[]... args){ this.cmd = cmd; this.args = args; } // ------------------------------------------------------------------------ // package scoped methods used by request processors // ------------------------------------------------------------------------ final Command getCommand () { return cmd; } /** * Signals completion without error. *

    * Sets the response, which also signals the completion of this {@link Future} * object. When this method is invoked, a call to {@link PendingRequest#get()} * will immediately return with the response. * @param response */ final void setResponse(Response response){ this.response = response; this.completion.signal(); } /** * Signals completion with error -- response will be null * @param cre */ final void setCRE (ClientRuntimeException cre){ this.cre = cre; excepted = true; setResponse(null); // this.completion.signal(); } /** * Determines if a completed request encountered errors and will throw an {@link ExecutionException} wrapping * the original cause. Called by the {@link PendingRequest#get} methods. * @throws ExecutionException */ private final void checkStatus () throws ExecutionException { // check for runtime or provider exceptions if(excepted) { if(cre != null) { if(cre instanceof ProviderException) throw new ExecutionException ("Provider Exception", cre); else throw new ExecutionException ("Client Runtime Exception", cre); } else { throw new ExecutionException ("Bug -- Request processing encountered exceptions but CRE is null", new ProviderException("unknown cause")); } } // check for Redis Errors if(response.isError()) throw new ExecutionException("Redis Exception on ["+cmd.name()+"] " + response.getStatus().message(), new RedisException(cmd, response.getStatus().message())); } // ------------------------------------------------------------------------ // Interface: Future // ------------------------------------------------------------------------ /* (non-Javadoc) @see java.util.concurrent.Future#get() */ @Override public Response get () throws InterruptedException, ExecutionException { completion.await(); checkStatus(); return response; } /* (non-Javadoc) @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit) */ @Override public Response get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if(!completion.await(timeout, unit)) throw new TimeoutException(); checkStatus(); return response; } /** * Pipeline does not support canceling of requests -- will always return false. * @see java.util.concurrent.Future#cancel(boolean) */ @Override public boolean cancel (boolean mayInterruptIfRunning) { return false; } /** * Pipeline does not support canceling of requests -- will always return false. * @see PendingRequest#cancel(boolean) * @see java.util.concurrent.Future#isCancelled() */ @Override public boolean isCancelled () { return false; } /* (non-Javadoc) @see java.util.concurrent.Future#isDone() */ @Override public boolean isDone () { return completion.isSignalled(); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/PipelineConnectionBase.java ================================================ /* * Copyright 2009-2011 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.io.InputStream; import java.util.NoSuchElementException; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.NotConnectedException; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Request; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.protocol.ConcurrentSyncProtocol; import org.jredis.ri.alphazero.protocol.VirtualResponse; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.FastBufferedInputStream; import org.jredis.ri.alphazero.support.Log; /** * Abstract base for all Pipeline connections, providing basically all of the * required functionality for a pipeline with asynchronous semantics. * * Synchronous pipelines can simply call * the {@link PipelineConnectionBase#queueRequest(Command, byte[])} method * in their implementation of the synchronous {@link Connection#serviceRequest(Command, byte[])} * method and block on {@link Future#get()} to realize the blocking semantics * and results required. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Sep 7, 2009 * @since alpha.0 * */ public abstract class PipelineConnectionBase extends ConnectionBase { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** */ private ResponseHandler respHandler; /** */ private Thread respHandlerThread; /** */ BlockingQueue pendingResponseQueue; /** synchronization object used to serialize request queuing */ private Object serviceLock = new Object(); /** * flag (default false) indicates if a pending QUIT command is being processed. * If true, any calls to queueRequests will result in a raise runtime exception */ private boolean pendingQuit = false; /** used by the Pipeline to indicate its state. Set to true on connect and false on Quit/Close */ private AtomicBoolean isActive; /** counted down on notifyConnect */ private CountDownLatch connectionEstablished; // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ /** * @param spec * @throws ClientRuntimeException */ protected PipelineConnectionBase (ConnectionSpec spec) throws ClientRuntimeException { super(spec); } // ------------------------------------------------------------------------ // Extension // ------------------------------------------------------------------------ /** * */ @SuppressWarnings("boxing") @Override protected void initializeComponents () { spec.setConnectionFlag(Flag.PIPELINE, true); spec.setConnectionFlag(Flag.RELIABLE, true); spec.setConnectionFlag(Flag.SHARED, true); super.initializeComponents(); serviceLock = new Object(); isActive = new AtomicBoolean(false); connectionEstablished = new CountDownLatch(1); pendingResponseQueue = new LinkedBlockingQueue(); respHandler = new ResponseHandler(); respHandlerThread = new Thread(respHandler, "response-handler"); respHandlerThread.start(); isActive.set(false); } @Override protected void notifyConnected () { super.notifyConnected(); Log.log("Pipeline <%s> connected", this); isActive.set(true); connectionEstablished.countDown(); } @Override protected void notifyDisconnected () { super.notifyDisconnected(); Log.log("Pipeline <%s> disconnected", this); isActive.set(true); connectionEstablished.countDown(); } /** * Pipeline must use a concurrent protocol handler. * * @see org.jredis.ri.alphazero.connection.ConnectionBase#newProtocolHandler() */ @Override protected Protocol newProtocolHandler () { return new ConcurrentSyncProtocol(); // return new SynchProtocol(); } // TODO: write chunking + mod ProtocolBase.Stream...Request + Command.FLUSH_BUFFERS. // @Override // protected OutputStream newOutputStream(OutputStream socketOutputStream) { // return new BufferedOutputStream(socketOutputStream); // } /** * Just make sure its a {@link FastBufferedInputStream}. */ @SuppressWarnings("boxing") @Override protected final InputStream newInputStream (InputStream socketInputStream) throws IllegalArgumentException { InputStream in = super.newInputStream(socketInputStream); if(!(in instanceof FastBufferedInputStream)){ System.out.format("WARN: input was: %s\n", in.getClass().getCanonicalName()); in = new FastBufferedInputStream (in, spec.getSocketProperty(Connection.Socket.Property.SO_RCVBUF)); } return in; } // ------------------------------------------------------------------------ // Interface: Connection // ------------------------------------------------------------------------ /** * This is a pseudo asynchronous method. The actual write to server does * occur in this method, so when this method returns, your request has been * sent. This simply defers the response read to the response handler. *

    * Other item of note is that once a QUIT request has been queued, no further * requests are accepted and a ClientRuntimeException is thrown. * * @see org.jredis.ri.alphazero.connection.ConnectionBase#queueRequest(org.jredis.protocol.Command, byte[][]) */ @Override public final Future queueRequest (Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { if(!isConnected()) throw new NotConnectedException ("Not connected!"); if(pendingQuit) throw new ClientRuntimeException("Pipeline shutting down: Quit in progess; no further requests are accepted."); Protocol protocol = Assert.notNull(getProtocolHandler(), "thread protocol handler", ProviderException.class); Request request = Assert.notNull(protocol.createRequest (cmd, args), "request object from handler", ProviderException.class); PendingRequest pendingResponse = new PendingRequest(cmd); if(cmd == Command.CONN_FLUSH) { Log.log("%s not supported -- ignored", cmd.code); return pendingResponse; } synchronized (serviceLock) { if(cmd != Command.QUIT) { request.write(getOutputStream()); } else { pendingQuit = true; isActive.set(false); } pendingResponseQueue.add(pendingResponse); } return pendingResponse; } void onResponseHandlerError (ClientRuntimeException cre, PendingRequest request) { Log.error("Pipeline response handler encountered an error: " + cre.getMessage()); // signal fault onConnectionFault(cre.getMessage(), false); // set execution error for future object request.setCRE(cre); // BEST: // 1 - block the request phase // 2 - try reconnect // 3-ok: reconnected, resume processing // 2-not ok: close shop, and set all pending responses to error // for now .. flush the remaining pending responses from queue // with execution error // PendingRequest pending = null; while(true){ try { pending = pendingResponseQueue.remove(); pending.setCRE(cre); Log.error("set pending %s response to error with CRE", pending.cmd); } catch (NoSuchElementException empty){ break; } } } // ------------------------------------------------------------------------ // Inner Class // ------------------------------------------------------------------------ /** * Provides the response processing logic as a {@link Runnable}. *

    * TODD: Needs to have a more regulated operating cycle. Right now its just * infinite loop until something goes boom. Not good. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 18, 2009 * @since alpha.0 * */ public final class ResponseHandler implements Runnable, Connection.Listener { private final AtomicBoolean work_flag; private final AtomicBoolean alive_flag; private final AtomicReference thread; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * Adds self to the listeners of the enclosing {@link Connection} instance. */ public ResponseHandler () { PipelineConnectionBase.this.addListener(this); this.work_flag = new AtomicBoolean(true); this.alive_flag = new AtomicBoolean(false); this.thread = new AtomicReference(null); } // ------------------------------------------------------------------------ // INTERFACE /* ====================================================== Thread (Runnable) * */ // ------------------------------------------------------------------------ /** * Keeps processing the {@link PendingRequest}s in the pending {@link Queue} * until a QUIT is encountered in the pending queue. Thread will stop after * processing the QUIT response (which is expected to be a {@link VirtualResponse}. *

    * TODO: not entirely clear what is the best way to handle exceptions. *

    * TODO: socket Reconnect in the context of pipelining is non-trivial, and maybe * not even practically possible. (e.g. request n is sent but pipe breaks on * some m (m!=n) response. non trivial. Perhaps its best to assume broken connection * means faulted server, specially given the fact that a pipeline has a heartbeat * so the issue can not be timeout. */ @Override public void run () { thread.compareAndSet(null, Thread.currentThread()); alive_flag.compareAndSet(false, true); /** Response handler thread specific protocol handler -- optimize fencing */ Protocol protocol = Assert.notNull (newProtocolHandler(), "the delegate protocol handler", ClientRuntimeException.class); Log.log("Pipeline <%s> thread for <%s> started.", Thread.currentThread().getName(), PipelineConnectionBase.this.toString()); PendingRequest pending = null; while(work_flag.get()){ Response response = null; try { pending = pendingResponseQueue.take(); try { response = protocol.createResponse(pending.cmd); response.read(getInputStream()); pending.response = response; pending.completion.signal(); if(response.getStatus().isError()) { Log.error ("(Asynch) Error response for " + pending.cmd.code + " => " + response.getStatus().message()); } } // this exception handling as of now is basically broken and fairly useless // really, what we want is making a distinction between bugs and runtime problems // and in case of connection issues, signal the retry mechanism. // in the interim, all incoming requests must be rejected (e.g. PipelineReconnecting ...) // and all remaining pending responses must be set to error. // major TODO catch (ProviderException bug){ Log.bug ("ProviderException: " + bug.getMessage()); onResponseHandlerError(bug, pending); break; } catch (ClientRuntimeException cre) { Log.problem ("ClientRuntimeException: " + cre.getMessage()); onResponseHandlerError(cre, pending); break; } catch (RuntimeException e){ Log.problem ("Unexpected (and not handled) RuntimeException: " + e.getMessage()); onResponseHandlerError(new ClientRuntimeException("Unexpected (and not handled) RuntimeException", e), pending); break; } // redis (1.00) simply shutsdown connection even if pending responses // are expected, so quit is NOT sent. we simply close connection on this // end. if(pending.cmd == Command.QUIT) { PipelineConnectionBase.this.disconnect(); break; } } catch (InterruptedException e1) { Log.log("Pipeline thread interrupted."); break; //e1.printStackTrace(); } } Log.log("Pipeline <%s> thread for <%s> stopped.", Thread.currentThread().getName(), PipelineConnectionBase.this); alive_flag.compareAndSet(true, false); } final private void stopHandler() { Log.log("%s stopping handler thread", this); work_flag.set(false); thread.get().interrupt(); // PipelineConnectionBase.this.respHandlerThread.interrupt(); } final private void shutdownHandler() { /* * It is not expected that shutdown would get called before * stop, but if it has, this makes sure we first go through * the stop sequence. */ if(work_flag.get() != false || alive_flag.get() != false) stopHandler(); alive_flag.set(false); PipelineConnectionBase.this.removeListener(this); Log.log("%s response handler has shutdown", this); } // ------------------------------------------------------------------------ // INTERFACE /* =================================================== Connection.Listener * * hooks for integrating the response handler thread's state with the * wrapping connection's state through event callbacks. */ // ------------------------------------------------------------------------ /** * Needs to be hooked up. * TODO: zood tond foree saree! * * @see org.jredis.connector.Connection.Listener#onEvent(org.jredis.connector.Connection.Event) */ @Override public void onEvent (Event event) { if(event.getSource() != PipelineConnectionBase.this) { Log.bug("event source [%s] is not this pipeline [%s]", event.getSource(), PipelineConnectionBase.this); // BUG: what to do about it? } // (new Exception()).printStackTrace(); Log.log("Pipeline.ResponseHandler: onEvent %s source: %s", event.getType().name(), event.getSource()); switch (event.getType()){ case CONNECTED: // (re)start break; case DISCONNECTED: // should be stopped now // // break; case CONNECTING: // no op break; case FAULTED: case DISCONNECTING: stopHandler(); break; case SHUTDOWN: shutdownHandler(); break; } } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/SyncConnection.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionReset; import org.jredis.connector.ConnectionSpec; import org.jredis.connector.NotConnectedException; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Request; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 10, 2009 * @since alpha.0 * */ public class SyncConnection extends ConnectionBase implements Connection { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ final private Lock lock; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * This constructor will pass the connection spec to the super class constructor and * create and install the {@link Protocol} handler delegate instance for this {@link SyncConnection}. * If you definitely need to specify the redis server version, and the protocol implementation for that * version exists, you should use this constructor. Otherwise, it is recommended that the * {@link SyncConnection#SyncConnection(ConnectionSpec)}be used. *

    * This constructor will open the socket connection immediately. * * @param connectionSpec * @param redisversion * @throws ClientRuntimeException due to either dns (host connectivity) or any IO issues related to establishing * the socket connection to the specified server. * @throws ProviderException if the version specified is not supported. */ public SyncConnection ( ConnectionSpec connectionSpec ) throws ClientRuntimeException, ProviderException { super (connectionSpec.setModality(Modality.Synchronous)); // REVU: huge flaw. connection initialization occurs in constructor!!! stupid. // TODO: change it. if(spec.getConnectionFlag(Flag.RELIABLE)) lock = new ReentrantLock(false); else{ lock = null; spec.getConnectionFlag(Flag.RELIABLE); } } // ------------------------------------------------------------------------ // Interface // ======================================================= ProtocolHandler // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.connector.Connection#getModality() */ // TODO: restore this // @Override public final Modality getModality() { return Connection.Modality.Synchronous; } /* (non-Javadoc) * @see org.jredis.ri.alphazero.connection.ConnectionBase#serviceRequest(org.jredis.protocol.Command, byte[][]) */ @Override public Response serviceRequest (Command cmd, byte[]... args) throws RedisException { if(!isConnected()) throw new NotConnectedException ("Not connected!"); Request request = null; Response response = null; ResponseStatus status = null; Protocol protocol = Assert.notNull(getProtocolHandler(), "thread protocol handler", ProviderException.class); try { // 1 - Request // Log.log("RedisConnection - requesting ..." + cmd.code); request = Assert.notNull(protocol.createRequest (cmd, args), "request object from handler", ProviderException.class); request.write(super.getOutputStream()); // 2 - response // Log.log("RedisConnection - read response ..." + cmd.code); response = Assert.notNull(protocol.createResponse(cmd), "response object from handler", ProviderException.class); response.read(super.getInputStream()); // break; } catch (ProviderException bug){ Log.bug ("serviceRequest() -- ProviderException: " + bug.getLocalizedMessage()); Log.log ("serviceRequest() -- closing connection ..."); disconnect(); throw bug; } catch (ClientRuntimeException cre) { Log.problem ("serviceRequest() -- ClientRuntimeException => " + cre.getLocalizedMessage()); reconnect(); throw new ConnectionReset ("Connection re-established but last request not processed: " + cre.getLocalizedMessage()); } catch (RuntimeException e){ e.printStackTrace(); Log.bug ("serviceRequest() -- *unexpected* RuntimeException: " + e.getLocalizedMessage()); Log.log ("serviceRequest() -- closing connection ..."); disconnect(); throw new ClientRuntimeException("unexpected runtime exeption: " + e.getLocalizedMessage(), e); } // 3 - Status // status = Assert.notNull (response.getStatus(), "status from response object", ProviderException.class); if(status.isError()) { Log.error ("Error response for " + cmd.code + " => " + status.message()); throw new RedisException(cmd, status.message()); } else if(status.code() == ResponseStatus.Code.CIAO) { // normal for quit and shutdown commands. we disconnect too. disconnect(); } return response; } @SuppressWarnings("unused") private Lock acquireLock() { lock.lock(); return lock; } @SuppressWarnings("unused") private void releaseLock() { lock.unlock(); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/SyncPipelineConnection.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Log; /** * Synchronous {@link PipelineConnectionBase} extension. * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Sep 7, 2009 * @since alpha.0 * */ public class SyncPipelineConnection extends PipelineConnectionBase { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ /** * @param spec * @throws ClientRuntimeException */ public SyncPipelineConnection (ConnectionSpec spec) throws ClientRuntimeException { super(spec.setModality(Modality.Synchronous)); } // ------------------------------------------------------------------------ // Interface : Connection // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.ri.alphazero.connection.ConnectionBase#serviceRequest(org.jredis.protocol.Command, byte[][]) */ @Override public Response serviceRequest (Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { // queue the request // Future pendingResponse = queueRequest(cmd, args); // wait for response // Response response; try { // This will block. response = pendingResponse.get(); } catch (InterruptedException e) { e.printStackTrace(); throw new ClientRuntimeException("on pendingResponse.get()", e); } catch (ExecutionException e) { if(e.getCause() instanceof RedisException) { throw (RedisException) e.getCause(); } else { e.printStackTrace(); throw new ProviderException("on pendingResponse.get()", e); } } // check response status // ResponseStatus status = Assert.notNull (response.getStatus(), "status from response object", ProviderException.class); if(status.isError()) { Log.error ("Error response for " + cmd.code + " => " + status.message()); throw new RedisException(cmd, status.message()); } /* this is handled by the super class */ // else if(status.code() == ResponseStatus.Code.CIAO) { // // normal for quit and shutdown commands. we disconnect too. // disconnect(); // } return response; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/connection/UnexpectedEOFException.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.connection; import org.jredis.connector.ConnectionException; import org.jredis.ri.alphazero._specification; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ public class UnexpectedEOFException extends ConnectionException { /** * @param msg * @param e */ public UnexpectedEOFException(String msg) { super(msg); } /** */ private static final long serialVersionUID = _specification.Version.major; } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * This package and child packages contain reference implementation alphazero * of the {@link org.jredis.JRedis} and {@link org.jredis.connector.Connection} * of the optional Connector specifications. */ package org.jredis.ri.alphazero; ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/protocol/ConcurrentSyncProtocol.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.protocol; import org.jredis.protocol.Command; import org.jredis.protocol.Response; /** * This basically extends {@link SyncProtocol} so that the response buffers * are not shared, so that it can be used in multi-threaded environments. * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Apr 23, 2009 * @since alpha.0 * */ public class ConcurrentSyncProtocol extends SyncProtocol { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Super Extensions // ------------------------------------------------------------------------ // @Override // protected ByteArrayOutputStream createRequestBufffer(Command cmd) { // return new ByteArrayOutputStream (PREFERRED_REQUEST_BUFFER_SIZE); // } // // protected Request createRequest (ByteArrayOutputStream buffer) { //// sharedRequestObject.reset(buffer); // return new StreamBufferRequest (buffer); // } @Override protected Response createStatusResponse(Command cmd) { return new SyncLineResponse (new byte[PREFERRED_LINE_BUFFER_SIZE], cmd, ValueType.STATUS); } @Override protected Response createBooleanResponse(Command cmd) { return new SyncLineResponse (new byte[PREFERRED_LINE_BUFFER_SIZE], cmd, ValueType.BOOLEAN); } @Override protected Response createStringResponse(Command cmd) { return new SyncLineResponse (new byte[PREFERRED_LINE_BUFFER_SIZE], cmd, ValueType.STRING); } @Override protected Response createNumberResponse(Command cmd /*, boolean isBigNum*/) { ValueType flavor = ValueType.NUMBER64; return new SyncLineResponse (new byte[PREFERRED_LINE_BUFFER_SIZE], cmd, flavor); } @Override protected Response createBulkResponse(Command cmd) { return new SyncBulkResponse (new byte[PREFERRED_LINE_BUFFER_SIZE], cmd); } @Override protected Response createMultiBulkResponse(Command cmd) { return new SyncMultiBulkResponse (new byte[PREFERRED_LINE_BUFFER_SIZE], cmd); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/protocol/DefaultProtocolFactory.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.protocol; import org.jredis.NotSupportedException; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Protocol; import org.jredis.connector.Connection; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Sep 13, 2010 * */ public class DefaultProtocolFactory implements Protocol.Factory { /* (non-Javadoc) @see org.jredis.protocol.Protocol.Factory#newProtocol(org.jredis.connector.ConnectionSpec) */ public Protocol newProtocol (ConnectionSpec connSpec) throws NotSupportedException { /* * TODO: * check various Connection.Property/Spec keys. * */ return connSpec.getConnectionFlag(Connection.Flag.SHARED) ? new ConcurrentSyncProtocol() : new SyncProtocol(); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/protocol/ProtocolBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.protocol; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.SocketException; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.protocol.Command; import org.jredis.protocol.Protocol; import org.jredis.protocol.Request; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; import org.jredis.ri.alphazero.support.Assert; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Apr 10, 2009 * @since alpha.0 * */ public abstract class ProtocolBase implements Protocol { // ------------------------------------------------------------------------ // Protocol Revision specific // ------------------------------------------------------------------------ public static final byte[] CRLF = {(byte) 13, (byte)10}; public static final byte[] SPACE = {(byte) 32}; public static final int CRLF_LEN = CRLF.length; public static final int DELIMETER_LEN = SPACE.length; public static final byte ERR_BYTE = (byte) 45; // - public static final byte OK_BYTE = (byte) 43; // + public static final byte COUNT_BYTE = (byte) 42; // * public static final byte SIZE_BYTE = (byte) 36; // $ public static final byte NUM_BYTE = (byte) 58; // : public static final byte ASCII_ZERO = (byte) 48; // 0 // ------------------------------------------------------------------------ // Protocol Revision specific consts // ------------------------------------------------------------------------ protected ProtocolBase () {} // ------------------------------------------------------------------------ // Interface: Protocol // ------------------------------------------------------------------------ /* TODO: lets just forget about this multi-version nonsense. */ @Override public boolean isCompatibleWithVersion(String version) { return version.equals("0.09"); } public byte[] createRequestBuffer(Command cmd, byte[]...args) throws ProviderException, IllegalArgumentException { class Buffer { byte[] b = null; private int off = 0; Buffer(int size){ b = new byte[size]; } void write (byte[] d){ final int dlen = d.length; System.arraycopy(d, 0, b, off, dlen); off+=dlen; } void write (byte d){ b[off] = d; off++; } byte[] getBytes() { return b; } } Buffer buffer = null; byte[] cmdLenBytes = Convert.toBytes(cmd.bytes.length); byte[] lineCntBytes = Convert.toBytes(args.length+1); // calculate the buffer size int bsize = 1 + lineCntBytes.length + CRLF_LEN + 1 + cmdLenBytes.length + CRLF_LEN + cmd.bytes.length + CRLF_LEN; for(int i=0;i * Specifically, if an instance of this class is obtained and not immediately * used (by calling read) before the user has obtained another instance of the * same, data corruption is guaranteed. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public static class StreamBufferRequest implements Request { /** */ ByteArrayOutputStream buffer; /** * @param buffer */ public StreamBufferRequest (ByteArrayOutputStream buffer) { this.buffer = buffer; } public void reset (ByteArrayOutputStream buffer) { this.buffer = buffer; } /* (non-Javadoc) * @see com.alphazero.jredis.connector.Message#read(java.io.InputStream) */ @Override public void read(InputStream in) throws ClientRuntimeException, ProviderException { throw new ProviderException("Request.read is not supported by this class! [Apr 2, 2009]"); } /** * Writes the entire content of the message to the output stream and flushes it. * * @param out the stream to write the Request message to. */ @Override public void write(OutputStream out) throws ClientRuntimeException, ProviderException { try { // you would expect these to throw exceptions if the socket has been reset // but they don't. buffer.writeTo(out); out.flush(); } catch (SocketException e){ Log.error("StreamBufferRequest.write(): SocketException on write: " + e.getLocalizedMessage()); throw new ClientRuntimeException ("socket exception", e); } catch (IOException e) { Log.error("StreamBufferRequest.write(): IOException on write: " + e.getLocalizedMessage()); throw new ClientRuntimeException ("stream io exception", e); } } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/protocol/ResponseSupport.java ================================================ package org.jredis.ri.alphazero.protocol; import java.io.OutputStream; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; /** * Base for all responses. Responsible for reading and determining status. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public abstract class ResponseSupport implements Response { // ------------------------------------------------------------------------ // Properties and fields // ------------------------------------------------------------------------ protected Type type; protected ResponseStatus status; protected Command cmd; protected boolean didRead = false; protected boolean isError = false; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ public ResponseSupport (Command cmd, Type type) { this.type = type; this.cmd = cmd; } // ------------------------------------------------------------------------ // Internal ops // ------------------------------------------------------------------------ /** called by child classes to indicate if & when their read operation has completed */ protected final boolean didRead (boolean value) { return didRead = value;} /** a bit aggressive but to force out the little bugs .. */ protected final void assertResponseRead () { if(!didRead) throw new ProviderException ("Response has not been read yet! -- whose bad?"); } // ------------------------------------------------------------------------ // Interface // ------------------------------------------------------------------------ @Override public boolean didRead() { return didRead; } @Override public ResponseStatus getStatus() {return status; } @Override public Type getType() { return type;} @Override public boolean isError() { assertResponseRead(); return isError; } @Override public void write(OutputStream out) throws ClientRuntimeException, ProviderException { throw new RuntimeException ("Message.write not implemented! [Apr 10, 2009]"); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/protocol/SyncProtocol.java ================================================ /* * Copyright 2009 - 2011 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.protocol; //import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.connector.ConnectionReset; import org.jredis.protocol.BulkResponse; import org.jredis.protocol.Command; import org.jredis.protocol.MultiBulkResponse; import org.jredis.protocol.Protocol; import org.jredis.protocol.Request; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; import org.jredis.protocol.StatusResponse; import org.jredis.protocol.ValueResponse; import org.jredis.ri.alphazero.connection.UnexpectedEOFException; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.Log; /** * [TODO: document me!] * * @version alpha.0, Apr 10, 2009 * @author Joubin Houshyar (alphazero@sensesay.net) * @since alpha.0 * */ public class SyncProtocol extends ProtocolBase { // ------------------------------------------------------------------------ // Protocol Handler's Data Buffer (pseudo-Registers) specific attributes // ------------------------------------------------------------------------ /** Preferred size of request data buffer */ protected static final int PREFERRED_REQUEST_BUFFER_SIZE = 1024 * 48; /** Initial size of the shared line buffer */ protected static final int PREFERRED_LINE_BUFFER_SIZE = 128; /** */ protected static final int INPUT_STREAM_BUFFER_SIZE = 1024 * 128; // ------------------------------------------------------------------------ // SyncConnection's can use the same buffers again and again and ... // ------------------------------------------------------------------------ /** Shared by all {@link Request} instances of this non-thread-safe {@link Protocol} implementation. */ private final ByteArrayOutputStream sharedRequestBuffer; /** Shared {@link Request} instance of this non-thread-safe {@link Protocol} implementation. */ private final StreamBufferRequest sharedRequestObject; /** Shared by all {@link Response} instances of this non-thread-safe {@link Protocol} implementation. */ private final byte[] sharedResponseBuffer; // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ public SyncProtocol() { sharedRequestBuffer = new ByteArrayOutputStream (PREFERRED_REQUEST_BUFFER_SIZE); sharedRequestObject = new StreamBufferRequest (sharedRequestBuffer); sharedResponseBuffer = new byte [PREFERRED_LINE_BUFFER_SIZE]; } // ------------------------------------------------------------------------ // Super Extensions // ------------------------------------------------------------------------ /** * @param cmd {@link Command} for this request - potentially useful for * optimizing buffers. * * @return the shared instance of {@link ByteArrayOutputStream} that * is used by all requests created by this {@link Protocol} implementation. */ @Override protected ByteArrayOutputStream createRequestBufffer(Command cmd) { sharedRequestBuffer.reset(); return sharedRequestBuffer; } protected Request createRequest (ByteArrayOutputStream buffer) { // sharedRequestObject.reset(buffer); return sharedRequestObject; } SyncLineResponse cache_syncLineResponse = null; @Override protected Response createStatusResponse(Command cmd) { if(null == cache_syncLineResponse) cache_syncLineResponse = new SyncLineResponse(cmd, ValueType.STATUS); else { cache_syncLineResponse.reset(cmd); } return cache_syncLineResponse; } @Override protected Response createBooleanResponse(Command cmd) { if(null == cache_syncLineResponse) cache_syncLineResponse = new SyncLineResponse(cmd, ValueType.BOOLEAN); else { cache_syncLineResponse.reset(cmd, ValueType.BOOLEAN); } return cache_syncLineResponse; } @Override protected Response createStringResponse(Command cmd) { if(null == cache_syncLineResponse) cache_syncLineResponse = new SyncLineResponse(cmd, ValueType.STRING); else { cache_syncLineResponse.reset(cmd, ValueType.STRING); } return cache_syncLineResponse; } @Override protected Response createNumberResponse(Command cmd /*, boolean isBigNum*/) { ValueType flavor = ValueType.NUMBER64; if(null == cache_syncLineResponse) cache_syncLineResponse = new SyncLineResponse(cmd, flavor); else { cache_syncLineResponse.reset(cmd, flavor); } return cache_syncLineResponse; } SyncBulkResponse cache_syncBulkResponse = null; @Override protected Response createBulkResponse(Command cmd) { if(null == cache_syncBulkResponse) cache_syncBulkResponse = new SyncBulkResponse(cmd); else { cache_syncBulkResponse.reset(cmd); } return cache_syncBulkResponse; } SyncMultiBulkResponse cache_syncMultiBulkResponse = null; @Override protected Response createMultiBulkResponse(Command cmd) { if(null == cache_syncMultiBulkResponse) cache_syncMultiBulkResponse = new SyncMultiBulkResponse(cmd); else { cache_syncMultiBulkResponse.reset(cmd); } return cache_syncMultiBulkResponse; } // ------------------------------------------------------------------------ // Inner Type // ======================================================================== // ------------------------------------------------------------------------ protected enum ValueType { STATUS, BOOLEAN, NUMBER64, STRING } // ------------------------------------------------------------------------ // Inner Type // ============================================================ Response(s) // ------------------------------------------------------------------------ /** * Synchronous responses are guaranteed to be contiguous chunks (if the * client of this class is respecting its contract) -- meaning, it can go * ahead and read as much as it can in its first read without busy waiting * or reading one byte at a time. After that initial read, specialized * response types can go head and read/parse as they please. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public abstract class SyncResponseBase extends ResponseSupport { byte[] buffer; int offset; protected SyncResponseBase(byte[] buffer, Command cmd, Type type) { super(cmd, type); this.buffer = buffer; offset = 0; } protected void reset (Command cmd, Type type) { this.cmd = cmd; this.type = type; offset = 0; didRead = false; status = null; isError = false; } /** * Makes blocking calls to input stream until it gets crlf. Should not be * used for size/count lines. * @param in */ void readSingleLineResponse (InputStream in) { offset = 0; int c = -1; int available = buffer.length; try { while ((c = in.read(buffer, offset, 1)) != -1) { offset += c; available -= c; if(offset > 2 && buffer[offset-2]==(byte)13 && buffer[offset-1]==(byte)10){ break; // we're done } if(available == 0) { byte[] newbuff = new byte[buffer.length * 2]; System.arraycopy(buffer, 0, newbuff, 0, buffer.length); buffer = newbuff; available = buffer.length - offset; } } if(c == -1) { Log.error("-1 read count in readLine() while reading response line."); throw new UnexpectedEOFException ("Unexpected EOF (read -1) in readLine. Command: " + cmd.code); } if((this.isError = buffer[0] == ProtocolBase.ERR_BYTE) == true) status = new ResponseStatus(ResponseStatus.Code.ERROR, new String(buffer, 1, offset-3)); else status = ResponseStatus.STATUS_OK; } catch (SocketException e) { // on connection reset throw new ConnectionReset("SocketException in readLine. Command: " + cmd.code, e); } catch (IOException e) { e.printStackTrace(); throw new ClientRuntimeException ("IOException in readLine. Command: " + cmd.code, e); } } } // ------------------------------------------------------------------------ // Inner Type // ============================================================ Response(s) // ------------------------------------------------------------------------ /** * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public class SyncLineResponse extends SyncResponseBase implements StatusResponse, ValueResponse { private ValueType flavor; private String stringValue; private long longValue; private boolean booleanValue; private SyncLineResponse(Command cmd, ValueType flavor) { this (sharedResponseBuffer, cmd, flavor); } /** * @param bs * @param cmd * @param status */ public SyncLineResponse(byte[] buff, Command cmd, ValueType flavor) { super(buff, cmd, Type.Value); this.flavor = flavor; } protected void reset (Command cmd){ super.reset(cmd, Type.Status); this.flavor = ValueType.STATUS; } protected void reset (Command cmd, ValueType flavor){ super.reset(cmd, Type.Value); this.flavor = flavor; } @Override public boolean getBooleanValue() throws IllegalStateException { if(flavor != ValueType.BOOLEAN) throw new IllegalStateException ("Response value type is " + flavor.name() + " not " + ValueType.BOOLEAN.name()); return booleanValue; } @Override public long getLongValue() throws IllegalStateException { if(flavor != ValueType.NUMBER64) throw new IllegalStateException ("Response value type is " + flavor.name() + " not " + ValueType.NUMBER64.name()); return longValue; } @Override public String getStringValue() throws IllegalStateException { if(flavor != ValueType.STRING) throw new IllegalStateException ("Response value type is " + flavor.name() + " not " + ValueType.STRING.name()); return stringValue; } /** * Delegates the io handling to the base class and parses the value reponse * based on the data flavor. */ @Override public void read(InputStream in) throws ClientRuntimeException, ProviderException { if(didRead) return; // BufferedInputStream bin = new BufferedInputStream(in, 1024 * 48); // super.readSingleLineResponse (bin); super.readSingleLineResponse (in); didRead = true; // TODO: not quite happy with the access to raw buffer here -- a method call would // slow things done but this is fragile in light of possible future code re-factoring. if(!status.isError() && flavor != ValueType.STATUS){ switch (flavor){ case BOOLEAN: booleanValue = buffer[1]==49?true:false; break; case NUMBER64: longValue = Convert.toLong (buffer, 1, offset-3); break; case STATUS: break; case STRING: stringValue = new String (buffer, 1, offset-3); break; } } } } // ------------------------------------------------------------------------ // Inner Type // ============================================================ Response(s) // ------------------------------------------------------------------------ /** * Abstract base for all multiline responses (as of now, Bulk and MultBulk). * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Sep 2, 2009 * @since alpha.0 * TODO: Use overflow buffers to increase read efficiency. */ public abstract class SyncMultiLineResponseBase extends SyncResponseBase { protected SyncMultiLineResponseBase (byte[] buffer, Command cmd, Type type) { super(buffer, cmd, type); } /** * @param in */ void seekToCRLF (InputStream in){ offset = 0; int c = -1; int available = buffer.length; try { while ((c = in.read(buffer, offset, 1)) != -1) { offset += c; available -= c; if(offset > 2 && buffer[offset-2]==(byte)13 && buffer[offset-1]==(byte)10){ break; // we're done } if(available == 0) { byte[] newbuff = new byte[buffer.length * 2]; System.arraycopy(buffer, 0, newbuff, 0, buffer.length); buffer = newbuff; available = buffer.length - offset; } } } catch (IOException e) { e.printStackTrace(); throw new ClientRuntimeException ("IOEx while reading line for command " + cmd.code, e); } if(c==-1) throw new ClientRuntimeException ("in.read returned -1"); } /** * @param in * @param checkForError * @param ctlByte * @return */ int readControlLine (InputStream in, boolean checkForError, byte ctlByte){ seekToCRLF(in); if(checkForError && (this.isError = buffer[0] == ProtocolBase.ERR_BYTE) == true) { status = new ResponseStatus(ResponseStatus.Code.ERROR, new String(buffer, 1, offset-3)); didRead = true; // we're done - error's are only one line return -2; } if(buffer[0] != ctlByte) { throw new ProviderException ("Bug? Expecting status code for size/count"); } status = ResponseStatus.STATUS_OK; return Convert.toInt (buffer, 1, offset-3); } /** * Will read up expected bulkdata bytes from the input stream. Routine will * also read in the last two bytes and will check that they are indeed CRLF. * * @param in the stream to read from. * @param length expected bulk data length (NOT including the trailing CRLF). * @return a byte[] of length. * @throws IOException * @throws IllegalArgumentException if could not read the bulk data */ public final byte[] readBulkData (InputStream in, int length) throws IOException, RuntimeException { byte[] data = new byte[length]; // TODO: optimize me // byte[] term = new byte[CRLF.length]; // FIX: http://github.com/alphazero/jredis/issues#issue/5 -- N/A int readcnt = -1; int offset = 0; while(offset < length){ if((readcnt = in.read (data, offset, length-offset)) ==-1 ) throw new ClientRuntimeException("IO - read returned -1 -- problem"); offset += readcnt; } // FIX: http://github.com/alphazero/jredis/issues#issue/5 -- BEGIN for(int i=0; i= 0){ try { data = super.readBulkData(in, size); } catch (IllegalArgumentException bug){ throw new ProviderException ("Bug: in converting the bulk data length bytes", bug); } catch (IOException problem) { throw new ClientRuntimeException ("Problem: reading the bulk data bytes", problem); } catch (RuntimeException bug) { throw new ProviderException ("Bug: reading the bulk data bytes. expecting " + size + " bytes.", bug); } } didRead = true; return; } } // ------------------------------------------------------------------------ // Inner Type // ============================================================ Response(s) // ------------------------------------------------------------------------ public class SyncMultiBulkResponse extends SyncMultiLineResponseBase implements MultiBulkResponse { /** */ List datalist; /** * @param cmd */ private SyncMultiBulkResponse(Command cmd) { this (sharedResponseBuffer, cmd); } public SyncMultiBulkResponse(byte[] buff, Command cmd) { super (buff, cmd, Type.MultiBulk); } protected void reset (Command cmd){ super.reset(cmd, Type.Bulk); this.datalist = null; } @Override public List getMultiBulkData() throws ClientRuntimeException, ProviderException { assertResponseRead(); return datalist; } @Override public void read(InputStream in) throws ClientRuntimeException, ProviderException { if(didRead) return; // BufferedInputStream bin = new BufferedInputStream(in, 1024); int count = super.readControlLine (in, true, COUNT_BYTE); if(!status.isError() && count >= 0){ datalist = new ArrayList(count); try { int size = -1; for(int i=0;i= 0) datalist.add (super.readBulkData(in, size)); else datalist.add(null); } } catch (IllegalArgumentException bug){ throw new ProviderException ("Bug: in converting the bulk data length bytes", bug); } catch (IOException problem) { throw new ClientRuntimeException ("Problem: reading the bulk data bytes", problem); } catch (RuntimeException bug) { throw new ProviderException ("Bug: reading the multibulk data bytes.", bug); } } didRead = true; return; } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/protocol/VirtualResponse.java ================================================ /* * Copyright 2009 Joubin Mohammad Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.protocol; import java.io.InputStream; import java.io.OutputStream; import org.jredis.ClientRuntimeException; import org.jredis.ProviderException; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.protocol.ResponseStatus; /** * Certain requested commands do not have a corresponding response from * redis. Quit and Shutdown are two examples as of now. This response * provides a virtual response for this type of request. You can set * the virtual response's status in the constructor. The default constructor * provides for OK status. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public final class VirtualResponse implements Response { private final ResponseStatus status; public VirtualResponse () { this.status = ResponseStatus.STATUS_OK; } public VirtualResponse (ResponseStatus status) { this.status = status; } @Override public boolean didRead() {return true;} @Override public ResponseStatus getStatus() { return status;} @Override public Type getType() { return Type.Status;} @Override public boolean isError() { return false;} @Override /** * The purpose of this class is to provide responses that are not actually read from * the server. Typically this is for commands that closed the connection on the send, such * as QUIT. * @see Command#QUIT * @see Command#SHUTDOWN * @see org.jredis.connector.Message#read(java.io.InputStream) */ public void read(InputStream in) throws ClientRuntimeException, ProviderException { return;} @Override public void write(OutputStream out) throws ClientRuntimeException, ProviderException { throw new RuntimeException ("Streamable.write not implemented! [Apr 4, 2009]"); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/semantics/DefaultKeyCodec.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.semantics; import java.io.UnsupportedEncodingException; import java.lang.reflect.UndeclaredThrowableException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jredis.NotSupportedException; import org.jredis.ri.alphazero.JRedisSupport; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.semantics.KeyCodec; /** * Default {@link KeyCodec} provider for JRedis RI. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 25, 2009 * @since alpha.0 * */ public class DefaultKeyCodec implements KeyCodec { private static final KeyCodec instance; static { instance = new DefaultKeyCodec(); } /** */ static final private Map keyByteCache = new ConcurrentHashMap(); public static final KeyCodec provider() { return instance; } /** TODO: jvm arg me ... defaults to FALSE now */ public static final boolean CacheKeys = false; /* (non-Javadoc) * @see org.jredis.Codec#decode(byte[]) */ public K decode (byte[] bytes) { throw new NotSupportedException("key decode not supported."); } /* (non-Javadoc) * @see org.jredis.Codec#encode(java.lang.Object) */ public byte[] encode (K key) { if(null == key) throw new IllegalArgumentException("key is null"); if(key instanceof String) { return encodeString((String) key); } else if (key instanceof byte[]){ byte[] bkey = (byte[]) key ; if(bkey.length == 0) throw new IllegalArgumentException("key is zerolewn"); return bkey; } else { String msg = String.format("only String and byte[] keys are supported", key.getClass().getCanonicalName()); throw new IllegalArgumentException(msg); } } public static byte[] encodeString(String key) throws IllegalArgumentException { if(null == key) throw new IllegalArgumentException("key is null"); byte[] bytes = null; if(JRedisSupport.CacheKeys == true) bytes = keyByteCache.get(key); if(null == bytes) { // bytes = key.getBytes(DefaultCodec.SUPPORTED_CHARSET); // java 1.6 try { bytes = key.getBytes(DefaultCodec.SUPPORTED_CHARSET_NAME); } catch (UnsupportedEncodingException e) { throw new UndeclaredThrowableException(e); } for(byte b : bytes) { if (b == (byte)32 || b == (byte)10 || b == (byte)13) throw new IllegalArgumentException ("Key includes invalid byte value: " + (int)b); } if(JRedisSupport.CacheKeys == true) keyByteCache.put(key, bytes); } return bytes; } /* (non-Javadoc) * @see org.jredis.Codec#supports(java.lang.Class) */ static byte[] ba = new byte[0]; static Class BAClass = ba.getClass(); public boolean supports (Class type) { return type == String.class || type.equals(BAClass); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/semantics/DefaultStringCodec.java ================================================ package org.jredis.ri.alphazero.semantics; import java.nio.charset.Charset; import org.jredis.Codec; /** * Nothing to see here, folks. Your basic {@link String#getBytes(Charset)} and {@link String#String(byte[], Charset)} * wrapper, and of course you can get to set the {@link Charset} if the default UTF-8 {@link Charset} is * not to your liking. * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Aug 23, 2009 * @since alpha.0 * */ public class DefaultStringCodec implements Codec { /** Default supported character set is UTF-8 */ public final static Charset DEFAULT_CHARSET = Charset.forName ("UTF-8"); /** */ @SuppressWarnings("unused") private final Charset charSet; // java 1.6 /** * */ public DefaultStringCodec() { this(DEFAULT_CHARSET); } /** * @param charset */ public DefaultStringCodec(Charset charset){ this.charSet = charset; } /* (non-Javadoc) * @see org.jredis.Codec#decode(byte[]) */ @Override public String decode (byte[] bytes) { return new String(bytes); // return new String(bytes, charSet); // java 1.6 } /* (non-Javadoc) * @see org.jredis.Codec#encode(java.lang.Object) */ @Override public byte[] encode (String value) { return value.getBytes(); // return value.getBytes(charSet); } /* (non-Javadoc) * @see org.jredis.Codec#supports(java.lang.Class) */ @Override public boolean supports (Class type) { return type.equals(String.class) ? true : false; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/semantics/GZipCompressedStringCodec.java ================================================ package org.jredis.ri.alphazero.semantics; import static org.jredis.ri.alphazero.support.GZip.compress; import static org.jredis.ri.alphazero.support.GZip.decompress; /** * * @author Joubin (alphazero@sensesay.net) * @version alpha.0, Aug 23, 2009 * @since alpha.0 * */ public class GZipCompressedStringCodec extends DefaultStringCodec { /* (non-Javadoc) * @see org.jredis.Codec.DefaultStringCodec#decode(byte[]) */ @Override public String decode (byte[] bytes) { return super.decode(decompress(bytes)); } /* (non-Javadoc) * @see org.jredis.Codec.DefaultStringCodec#encode(java.lang.String) */ @Override public byte[] encode (String value) { return compress(super.encode(value)); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/Assert.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.lang.reflect.InvocationTargetException; import java.util.Formatter; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 02, 2009 * @since alpha.0 * */ public class Assert { /** * assert identical based on content. * @param * @param a * @param b * @param clazz */ public static final void isEquivalent (byte[] a, byte[] b) { isEquivalent(a, b, RuntimeException.class); } /** * assert identical based on content. * @param * @param a * @param b * @param clazz */ public static final void isEquivalent (byte[] a, byte[] b, Class clazz) { notNull(a, "arg a", clazz); notNull(b, "arg b", clazz); if(a.length != b.length) { throwIt("byte arrays being compared are of unequal length", clazz); } for(int i=0; iUsage:
    
    	 * // ... somewhere within the bowels of your code ..
    	 * //
    	 * Assert.notNull (aReference, YourRuntimeException.class);
    	 *  
    * @param * @param * @param obj * @param clazz * @return * @throws E */ public static final T notNull (T obj, Class clazz) throws E { if(null == obj){ throwIt("null reference", clazz);} return obj; } /** * Usage:
    
    	 * // ... somewhere within the bowels of your code ..
    	 * //
    	 * Assert.notNull (inputRecord, "inputRecord", YourRuntimeException.class);
    	 *  
    * @param * @param * @param obj * @param info * @param clazz * @return * @throws E */ public static final T notNull (T obj, String info, Class clazz) throws E { if(null == obj){ throwIt("null reference:{"+info+"}", clazz);} return obj; } @SuppressWarnings("boxing") public static final T notNull (T obj, int idx, Class clazz) throws E { if(null == obj){ throwIt(String.format("null reference: arg {%d}", idx), clazz);} return obj; } /** * Usage:
    
    	 * // ... somewhere within the bowels of your code ..
    	 * //
    	 * Assert.isTrue (conn.isReady(), "connection is ready", YourRuntimeException.class);
    	 *  
    * @param * @param fact * @param claim * @param clazz * @throws E */ public static final void isTrue (boolean fact, String claim, Class clazz) throws E { if(!fact) { throwIt("its not true that \"" + claim + "\"", clazz);} } /** * Usage:
    
    	 * // ... somewhere within the bowels of your code ..
    	 * //
    	 * Assert.isTrue (conn.isReady(), YourRuntimeException.class);
    	 *  
    * @param is what you wish thrown - must be related to {@link RuntimeException} * @param fact * @param clazz * @throws E */ public static final void isTrue (boolean fact, Class clazz) throws E { if(!fact) { throwIt("Factual error ", clazz);} } /** * @param * @param n * @param from * @param to * @param info * @param clazz * @return * @throws E */ public static final long inRange (long n, long from, long to, String info, Class clazz) throws E { if(n > to || n < from ){ throwIt("exceeds valid range :{"+info+"}", clazz);} return n; } /** * Note: asserts n is in the range from->to, inclusive. * @param * @param n * @param from * @param to * @param info * @param clazz * @return * @throws E */ public static final int inRange (int n, int from, int to, String info, Class clazz) throws E { if(n > to || n < from ){ throwIt("exceeds valid range :{"+info+"}", clazz);} return n; } // Object obj = Assert.isType(T obj, Class clazz, "", RuntimeException.class); /** * Tests to see if the object can be cast to the specified type. * Will not test to see arguments are not null. * @param the type * @param the thrown exception type * @param obj the object to be cast * @param clazz of the the target type * @param info the message for the exception thrown * @param throwable specified if not * @return the cast object if successful * @throws E */ public static final T cast (Object obj, Class clazz, String info, Class throwable) throws E { T t = null; try { t = clazz.cast(obj); } catch (ClassCastException e) { String actual = obj.getClass().getCanonicalName(); throwIt(info + " [object type: "+ actual +" target: " + clazz.getCanonicalName() +"]", throwable); } return t; } /** * Not really intended for external use since you could simply throw new E (msg). *

    * Usage (if you must):

    
    	 * // ... somewhere within the bowels of your code ..
    	 * //
    	 * Assert.throwIt ("why am i calling Assert.throwIt() when I could be throwing myself?", YourRuntimeException.class);
    	 *  
    * @param {@link RuntimeException} subclass to to throw * @param msg message to set in the exception * @param clazz the Class of E * @throws E your requested exception (unless reflection instantiate failed, * in which case it will be a plain old {@link RuntimeException}) */ private static final void throwIt (String msg, Class clazz) throws E { // TODO: isolate parts of this for our logger // TODO: hey - a simple logger ... // 1 - get the current stack trace and filter it // int fidx = 0; StackTraceElement ste[] =Thread.currentThread().getStackTrace(); int j=0; for(StackTraceElement e : ste){ j++; if(e.getClassName().equals(Assert.class.getName())) fidx = j; } StackTraceElement[] filtered = new StackTraceElement[ste.length-fidx]; for(int i=fidx; i 0 ? src.getClassName().indexOf(simpleClassName)-1 : 0; String packageName = src.getClassName().substring(0, scni); @SuppressWarnings("boxing") String info = new Formatter().format("%s in method %s.%s() [file: %s line:%d - package: %s]", msg, simpleClassName, src.getMethodName(), src.getFileName(), src.getLineNumber(), packageName).toString(); // 3- throw the exception // RuntimeException rte = new IllegalArgumentException(info); // in case we can't instantiate the E class try { rte = clazz.getDeclaredConstructor(String.class, Throwable.class).newInstance(info, rte); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 4- use the filtered stacktrace from (1) rte.setStackTrace(filtered); // 5- its coming your way ... catch! throw rte; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/Convert.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; /** * Perhaps a silly hack, but proven to speed things up. * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 02, 2009 * @since alpha.0 * */ public class Convert { // ------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------ public static final int INT_P_65535 = 65535; public static final int INT_N_65535 = 0 - INT_P_65535; private static final byte[][] i2b_65535 = new byte[INT_P_65535+1][]; private static final byte BYTE_MINUS = (byte) '-'; private static final byte BYTE_PLUS = (byte) '+'; private static final byte BYTE_ZERO = (byte) '0'; private static final byte BYTE_NINE = (byte) '9'; private static final int MAX_POSITIVE_32_BIT_DIGITS = 10; private static final int MAX_POSITIVE_64_BIT_DIGITS = 19; // ------------------------------------------------------------------------ // public Interface // ------------------------------------------------------------------------ /** * return the bytes of the string representation of the integer. Perhaps * should be called getNumberBytes, or perhaps getHumanReadableBytes() * Ex: 444 => "444".getBytes() => new byte[3]={52, 52, 52} * * If representation is not in our cache or too high (larger than Convert#INT_P_65535 ) * then it will return whatever we would get from {@link Convert#toBytes(int)} which is * using JDK libs for the same. * * The whole point here is to be faster since we need to convert numbers to the byte array * of their string representation a lot in JRedis for the protocol and to cut out the * unnecessary step of creating a new string simply because there apparently isn't any other * way in JDK to go from a number to the bytes of its textual representation. (!) * * @param i * @param signed * @return */ public static final byte[] toBytes(int i){ if(i < INT_N_65535 || i > INT_P_65535) { return Integer.toString(i).getBytes(); } final int absi = Math.abs(i); final byte[] cachedData = i2b_65535[absi]; final byte[] data; if(cachedData == null) { data = Integer.toString(absi).getBytes(); i2b_65535[absi] = data; } else { data = cachedData; } return i >= 0 ? data : getNegativeNumberBytes(data); } /** * Will delegate to {@link Convert#getBytes(int)} if the 'long' number is actually * within the range of our int cache, otherwise it will return the bytes using std * JDK mechanisms. * @param lnum * @return */ public static final byte[] toBytes(long lnum){ if(lnum >= INT_N_65535 && lnum <= INT_P_65535) return toBytes((int)lnum); return Long.toString(lnum).getBytes(); } public static final byte[] toBytes(double dnum){ return Double.toString(dnum).getBytes(); } /** * Converts the byte[]s of the ASCII representation of a decimal number to an int. * *

    Expects a byte array of no more than {@link Convert#MAX_POSITIVE_32_BIT_DIGITS} bytes in length * after accounting for potential leading byte indicating the sign of the number representation. * 9 bytes (for a positive integer). * * @param potentiallySignedAsciiBytes, for example {49, 49, 52} ("114") * @return * @throw IllegalArgumentException if buffer contains anything other than values 48 to 57 */ public static final int toInt(final byte[] potentiallySignedAsciiBytes, final int offsetin, final int len) throws IllegalArgumentException { int offset = offsetin; final byte[] buff = potentiallySignedAsciiBytes; // lets use a sensible name ;) if(null == buff) throw new IllegalArgumentException ("Null input"); if(len > buff.length) throw new IllegalArgumentException ("buffer length of " + buff.length + " less than the spec'd len " + len); boolean negative = false; int digitCnt = len; final byte bs = buff[offset]; if(bs ==BYTE_MINUS || bs == BYTE_PLUS){ if(bs == BYTE_MINUS) negative = true; offset++; digitCnt--; } if(digitCnt > MAX_POSITIVE_32_BIT_DIGITS) throw new IllegalArgumentException ("This \"int\" has more digits than a 32 bit signed number:" + digitCnt); // lets do it int value = 0; for(int p = 0; p < digitCnt; p++){ final byte b = buff[offset+p]; if(b < BYTE_ZERO || b > BYTE_NINE) throw new IllegalArgumentException("That's not a number! byte value: " + b); value = value*10 + b - BYTE_ZERO; } if(negative) value = 0 - value; return value; } /** * Its just like (really! :) {@link Convert#toInt(byte[], int, int)} but for {@link Long} values. Max number of digits * is now {@link Convert#MAX_POSITIVE_64_BIT_DIGITS}. * * @param potentiallySignedAsciiBytes * @param offset * @param len * @return * @throws IllegalArgumentException */ public static final long toLong(byte[] potentiallySignedAsciiBytes, final int offsetin, int len) throws IllegalArgumentException { int offset = offsetin; final byte[] buff = potentiallySignedAsciiBytes; // lets use a sensible name ;) if(null == buff) throw new IllegalArgumentException ("Null input"); if(len > buff.length) throw new IllegalArgumentException ("buffer length of " + buff.length + " less than the spec'd len " + len); boolean negative = false; int digitCnt = len; final byte bs = buff[offset]; if(bs ==BYTE_MINUS || bs == BYTE_PLUS){ if(buff[offset]==BYTE_MINUS) negative = true; offset++; digitCnt--; } if(digitCnt > MAX_POSITIVE_64_BIT_DIGITS) throw new IllegalArgumentException ("This \"int\" has more digits than a 32 bit signed number:" + digitCnt); // lets do it long value = 0; for(int p = 0; p < digitCnt; p++){ final byte b = buff[offset+p]; if(b < BYTE_ZERO || b > BYTE_NINE) throw new IllegalArgumentException("That's not a number! byte value: " + b); value = value*10 + b - BYTE_ZERO; } if(negative) value = 0 - value; return value; } /** * @param potentiallySignedBytes * @return * @throws IllegalArgumentException */ public static final int toInt(byte[] potentiallySignedBytes) throws IllegalArgumentException { if(null == potentiallySignedBytes) throw new IllegalArgumentException ("null input"); return toInt(potentiallySignedBytes, 0, potentiallySignedBytes.length); } /** * @param potentiallySignedBytes * @return * @throws IllegalArgumentException */ public static final long toLong(byte[] potentiallySignedBytes) throws IllegalArgumentException { if(null == potentiallySignedBytes) throw new IllegalArgumentException ("null input"); return toLong(potentiallySignedBytes, 0, potentiallySignedBytes.length); } /** * TODO: optimize. * @param potentiallySignedBytes * @return * @throws IllegalArgumentException */ public static final double toDouble (byte[] stringRepOfDoublePrecisionBytes) throws IllegalArgumentException { double dnum = 0; if(null == stringRepOfDoublePrecisionBytes) throw new IllegalArgumentException ("null input"); try { dnum = Double.parseDouble(new String(stringRepOfDoublePrecisionBytes)); } catch (Exception e){ throw new IllegalArgumentException("", e); } return dnum; } // ------------------------------------------------------------------------ // Inner ops // ------------------------------------------------------------------------ /** * @param unsigned * @return */ private static final byte[] getNegativeNumberBytes(byte[] unsigned){ int unsigned_length = unsigned.length; byte[] data = new byte[unsigned_length+1]; data [0] = BYTE_MINUS; System.arraycopy(unsigned, 0, data, 1, unsigned_length); return data; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/DefaultCodec.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jredis.JRedis; import org.jredis.semantics.KeyCodec; /** * Note that this is the one element of this package that is most likely to change * drastically. It is provided, for now, as a helper for using {@link JRedis}. * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, 04/02/09 * @since alpha.0 * */ public class DefaultCodec { public static class Keys implements KeyCodec{ public enum SupportedType { STRING (), BYTES (); } /* (non-Javadoc) @see org.jredis.Codec#decode(byte[]) */ @Override public K decode(byte[] bytes) { throw new RuntimeException("TODO Auto-generated Codec#decode stub -- NOT IMPLEMENTED"); } /* (non-Javadoc) @see org.jredis.Codec#encode(java.lang.Object) */ @Override public byte[] encode(K object) { throw new RuntimeException("TODO Auto-generated Codec#encode stub -- NOT IMPLEMENTED"); } /* (non-Javadoc) @see org.jredis.Codec#supports(java.lang.Class) */ @Override public boolean supports(Class type) { throw new RuntimeException("TODO Auto-generated Codec#supports stub -- NOT IMPLEMENTED"); } } public final static String SUPPORTED_CHARSET_NAME = "UTF-8"; // this is for jdk 1.5 compliance public final static Charset SUPPORTED_CHARSET = Charset.forName ("UTF-8"); /** * This helper method is mainly intended for use with a list of * keys returned from Redis, given that it will use the UTF-8 * {@link Charset} in decoding the byte array. Typical use would * be to convert from the List output of {@link JRedis#keys()} * * @param bytearray * @return */ public static final List toStr (List bytearray) { if(null == bytearray) return null; List list = new ArrayList(bytearray.size()); for(byte[] b : bytearray) if(null!= b) list.add(toStr(b)); else list.add(null); return list; } public static final Map toDataDictionary (Map binaryMap) { if(null == binaryMap) return null; Map dict = new HashMap(binaryMap.size()); for(byte[] bkey : binaryMap.keySet()) if(null!= bkey) dict.put(toStr(bkey), binaryMap.get(bkey)); return dict; } /** * @param bytes * @return new {@link String#String(byte[])} or null if bytes is null. */ public static final String toStr (byte[] bytes) { String str = null; if(null != bytes) { try { str = new String(bytes, SUPPORTED_CHARSET_NAME); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return str; // return new String(bytes, SUPPORTED_CHARSET); // Java 1.6 only } public static final byte[] encode(String value) { byte[] bytes = null; try { bytes = value.getBytes(SUPPORTED_CHARSET_NAME); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return bytes; // return value.getBytes(SUPPORTED_CHARSET); } // /** // * @param bytes // * @return // */ // @Deprecated // public static final Integer toInt (byte[] bytes) { // return new Integer(toStr (bytes)); // } /** * NOTE: Isn't this already in {@link Convert#toLong(byte[])}? * TODO: get rid of this method * This helper method will convert the byte[] to a {@link Long}. * @param bytes * @return */ // @Deprecated @SuppressWarnings("boxing") public static final Long toLong (byte[] bytes) { // return new Long (toStr (bytes)); return Convert.toLong(bytes); } @SuppressWarnings("boxing") public static final List toLong(List bytearray){ if(null == bytearray) return null; List list = new ArrayList(bytearray.size()); for(byte[] b : bytearray) list.add(Convert.toLong(b)); return list; } /** * @param bs bytes of the ascii string representation of a double number. E.g. "2.002".getBytes() * @return */ public static double toDouble (byte[] bs) { return Convert.toDouble(bs); } @SuppressWarnings("boxing") public static final List toDouble(List bytearray){ if(null == bytearray) return null; List list = new ArrayList(bytearray.size()); for(byte[] b : bytearray) list.add(Convert.toDouble(b)); return list; } /** * This helper method will assume the List being presented is the list returned * from a {@link JRedis} method such as {@link JRedis#smembers(String)}, and that this * list contains the {@link DefaultCodec#encode(Serializable)}ed bytes of the parametric type T. *

    * Specifically, this method will instantiate an {@link ArrayList} for type T, of equal * size to the size of bytelist {@link List}. Then it will iterate over the byte list * and for each byte[] list item call {@link DefaultCodec#decode(byte[])}. *

    * Usage example: *

    
    	 * List  memberBytes = redis.smembers("my-object-set");
    	 * List  members = decode (memberBytes);
    	 * 
    * @param * @param byteList * @return */ @SuppressWarnings("unchecked") public static final List decode (List byteList) { if(null == byteList) return null; List objectList = new ArrayList(byteList.size()); for (byte[] bytes : byteList) { if(null != bytes){ T object = (T) decode(bytes); objectList.add (object); } else{ objectList.add(null); } } return objectList; } /** * This helper method will assume that the byte[] provided are the serialized * bytes obtainable for an instance of type T obtained from {@link ObjectOutputStream} * and subsequently stored as a value for a Redis key (regardless of key type). *

    Specifically, this method will simply do: *

    
    	 * ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytes));
    	 * t = (T) oin.readObject();
    	 * 
    * and returning the reference t, and throwing any exceptions encountered along * the way. *

    * This method is the decoding peer of {@link DefaultCodec#encode(Serializable)}, and it is * assumed (and certainly recommended) that you use these two methods in tandem. *

    * Naturally, all caveats, rules, and considerations that generally apply to {@link Serializable} * and the Object Serialization specification apply. * @param * @param bytes * @return the instance for T */ @SuppressWarnings("unchecked") public static final T decode(byte [] bytes) { T t = null; Exception thrown = null; try { ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytes)); t = (T) oin.readObject(); } catch (IOException e) { e.printStackTrace(); thrown = e; } catch (ClassNotFoundException e) { e.printStackTrace(); thrown = e; } catch (ClassCastException e) { e.printStackTrace(); thrown = e; } finally { if(null != thrown) throw new RuntimeException( "Error decoding byte[] data to instantiate java object - " + "data at key may not have been of this type or even an object", thrown ); } return t; } /** * This helper method will serialize the given serializable object of type T * to a byte[], suitable for use as a value for a redis key, regardless of the key * type. * * @param * @param obj * @return */ public static final byte[] encode(T obj) { byte[] bytes = null; try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); out.writeObject(obj); bytes = bout.toByteArray(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("Error serializing object"+obj+" => " + e); } return bytes; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/FastBufferedInputStream.java ================================================ package org.jredis.ri.alphazero.support; import java.io.IOException; import java.io.InputStream; import org.jredis.ProviderException; /** * Extension of {@link java.io.InputStream} that uses the enclosing instance's * {@link InputStream} its data source. This is not supposed to be a general purpose * implementation. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Sep 5, 2009 * @since alpha.0 * */ public final class FastBufferedInputStream extends java.io.InputStream { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** data buffer (cache) */ private byte[] buffer; /** current (read) offset of {@link FastBufferedInputStream#buffer} */ private int offset = 0; /** * underlying input stream read buffer -- the size of this buffer determines the * maximum bytes read */ final private byte[] iobuffer; /** underying input stream */ final private InputStream in; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * @param in the input source * @param bufferSize size of the {@link FastBufferedInputStream#iobuffer} * */ public FastBufferedInputStream (InputStream in, int bufferSize) { this.in = in; buffer = new byte [0]; iobuffer = new byte[bufferSize]; } // ------------------------------------------------------------------------ // Inner Ops // ------------------------------------------------------------------------ /** * Get more bytes from the underling {@link InputStream}. * Only reads from input source if len exceeds available data in * {@link FastBufferedInputStream#buffer}. *

    * This call will block until (minimally) len bytes have been read. * * @param len * @throws IOException if a read on the underlying stream returns 0 length bytes. * This (obviously) shouldn't happen but if it does, it would be treated as an exception. */ @SuppressWarnings("boxing") private final int getMoreBytes (int len) throws IOException { // hit the date source until we have enough bytes // compact (reset offset to 0) on first copy int rlen = 0; while (len > buffer.length - offset) { int c = in.read(iobuffer, 0, iobuffer.length); if(c==-1) { return -1; } else if(c > 0){ int bufflen = buffer.length - offset; rlen+=c; byte[] newbuffer = new byte[bufflen + c]; System.arraycopy(buffer, offset, newbuffer, 0, bufflen); System.arraycopy(iobuffer, 0, newbuffer, bufflen, c); offset = 0; buffer = newbuffer; } else {// should never happen per contract of inputstream ... Log.bug (String.format("ZERO! <= %d\n", c)); throw new IOException ("input stream read return 0 bytes!"); } } return rlen; } // ------------------------------------------------------------------------ // Interface: InputStream // ------------------------------------------------------------------------ /** * Note:
    * Breaks the contract of the {@link InputStream#read(byte[], int, int)} * to the extent that call will block on the underlying {@link InputStream} * until it gets all the len bytes specified. *

    * Also, this call will return -1 if and only if it (a) needs to get more * data by calling {@link FastBufferedInputStream#getMoreBytes(int)}, * and (b) that call returns -1. Note that it is possible, in a general * context, that -1 is returned but there is previously accumulated data in * {@link FastBufferedInputStream#buffer} and thus * {@link FastBufferedInputStream#available()} returns a non zero positive * integer which is less than specified len. But that is not expected * in the specific context of Redis protocol. * * @see java.io.InputStream#read(byte[], int, int) */ int maxRead = 0; @Override public int read (byte[] b, int off, int len) throws IOException { if (off < 0 || off >= b.length || len < 0 || off + len > b.length) { throw new ArrayIndexOutOfBoundsException(); } int available = buffer.length - offset; if(len > available) { int c = getMoreBytes (len); // this is a potentially blocking call if(c==-1) return -1; else if(c < len - available) throw new ProviderException ("Bug: getMoreBytes() returned less bytes than requested. c="+c+" len=" + len + "available=" + available); } if(len == 1){ b[off] = buffer[offset]; } else { System.arraycopy(buffer, offset, b, off, len); } offset += len; return len; } /** * @return the length of data available without making call * to the underlying stream. * @see java.io.InputStream#available() */ @Override public int available () throws IOException { return buffer.length - offset; } /** * {@link InputStream#mark(int)} is not supported. * @see java.io.InputStream#markSupported() */ @Override public boolean markSupported () { return false; } /* (non-Javadoc) * @see java.io.InputStream#read(byte[]) */ @Override public int read (byte[] b) throws IOException { return read (b, 0, b.length); } /* (non-Javadoc) * @see java.io.InputStream#read() */ @Override public int read () throws IOException { byte[] b = new byte[1]; int c = read(b, 0, 1); if(c == -1) return -1; return b[0]; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/GZip.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; /** * GZip (de)compression utility methods. * @author Houshyar (alphazero@sensesay.net) * @version alpha.0, Aug 24, 2009 * @since alpha.0 * */ public class GZip { /** * @param data * @return */ public static final byte[] compress(byte[] data){ ByteArrayOutputStream out = new ByteArrayOutputStream(); try { GZIPOutputStream gzipOutputtStream = new GZIPOutputStream(out); gzipOutputtStream.write(data); gzipOutputtStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to GZip compress data", e); } return out.toByteArray(); } /** * @param data * @return */ public static final byte[] decompress(byte[] data){ ByteArrayOutputStream buffer = null; GZIPInputStream gizpInputStream= null; try { buffer = new ByteArrayOutputStream(); gizpInputStream = new GZIPInputStream(new ByteArrayInputStream(data)); int n=-1; @SuppressWarnings("unused") int tot = 0; byte[] _buffer = new byte[1024 * 12]; while(-1 != (n = gizpInputStream.read(_buffer))){ buffer.write(_buffer, 0, n); tot += n; } gizpInputStream.close(); buffer.close(); } catch (IOException e) { throw new RuntimeException("Failed to GZip decompress data", e); } return buffer.toByteArray(); } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/Log.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.util.Date; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 02, 2009-2011 * @since alpha.0 * */ public class Log { public final static Logger logger = Logger.getLogger("JRedis"); static { logger.setUseParentHandlers(false); final Handler handler = new Log.Handler(); final Formatter formatter = new Log.Formatter(); handler.trySetFormatter(formatter); logger.addHandler(handler); } public enum Category { INFO, DEBUG, ERROR, PROBLEM, BUG } // the various 'just FYI's ... public static final void log (String msg) { log (msg, (Object[])null); } public static final void log (String format, Object...args) { logger.info(String.format(format, args)); } public static final void debug (String msg) { debug(msg, (Object[])null); } public static final void debug (String format, Object...args) { logger.log(Level.FINE, String.format(format, args)); } // the various 'error! run for covers', ... public static final void error (String msg) { _error (Category.ERROR, msg); } public static final void error (String msg, Throwable t) { logger.log(Level.SEVERE, msg, t); } public static final void error (String msg, Object...args) { _error (Category.ERROR, msg, args); } public static final void problem (String msg) { _error (Category.PROBLEM, msg); } public static final void problem (String msg, Object...args) { _error (Category.PROBLEM, msg, args); } public static final void bug (String msg) { _error (Category.BUG, msg); } public static final void bug (String msg, Object...args) { _error (Category.BUG, msg, args); } private static final void _error (final Category cat, final String msg, final Object...args) { final String _msg = String.format(msg, args); if(cat.equals(Category.ERROR)) logger.severe(String.format("%s", _msg)); else logger.log(Level.WARNING, String.format("%s: %s", cat, _msg)); } // ------------------------------------------------------------------------ // Inner Classes | formatter and handler // ------------------------------------------------------------------------ /** * Default handler for JRedis.Log * @author alphazero */ public static class Handler extends java.util.logging.Handler { /** * Try and set the formatter -- may not be possible if * run in containers, etc. due to security checks. * @param fmt */ private java.util.logging.Formatter formatter; final void trySetFormatter(Formatter fmt){ try { super.setFormatter(fmt); } catch (Exception e) { e.printStackTrace(); } finally { formatter = getFormatter(); } } @Override final public void publish(LogRecord record) { System.err.print(formatter.format(record)); flush(); } @Override final public void flush() { System.err.flush(); } @Override final public void close() throws SecurityException { flush(); } } /** * simple formatter for a single line log out. * @author alphazero */ public static class Formatter extends java.util.logging.Formatter { final String LINESEP = System.getProperty("line.separator"); @SuppressWarnings("boxing") @Override final public String format(LogRecord record) { // TODO: clean up the mess above and fix this. final Level level = record.getLevel(); final String logger = record.getLoggerName(); final String msg = record.getMessage(); final Object[] msgparams = record.getParameters(); final int tid = record.getThreadID(); final long time = record.getMillis(); String _msg = null; if(msgparams != null && msgparams.length > 0){ _msg = String.format(msg, msgparams); } else { _msg = msg; } final Date d = new Date(time); return String.format("%014d %s %s[tid:%d] <%s>: %s%s", time, d, logger, tid, level.getLocalizedName(), _msg, LINESEP); } } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/Signal.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Non-reusable (disposable) signal class. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 15, 2009 * @since alpha.0 * */ public class Signal { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** underlying lock used for implementation */ final Lock lock = new ReentrantLock(); /** condition based on lock */ final Condition signalled = lock.newCondition(); /** Signal state */ boolean isSignalled = false; // ------------------------------------------------------------------------ // Public interface // ------------------------------------------------------------------------ /** * Blocking call awaits the signal for the specified duration. * Multiple threads may call this method to wait signal. Ordering is not maintained. * @param time duration in time units * @param unit time unit * @return true if timedout. * @throws InterruptedException * @see Signal#signal() */ public boolean await (long time, TimeUnit unit) throws InterruptedException { boolean timedout = false; long nanosTimeout = unit.toNanos(time); long timecheck = System.nanoTime(); lock.lock(); try { while(!isSignalled && nanosTimeout > 0L) { timedout = signalled.await(time, unit); long now = System.nanoTime(); nanosTimeout -= now - timecheck; timecheck = now; } } finally { lock.unlock(); } return timedout | isSignalled; } /** * Blocking call awaits the signal. Multiple threads may call this method to wait signal. * Ordering is not maintained. * @throws InterruptedException if interrupted while waiting * @see Signal#signal() */ public void await () throws InterruptedException { lock.lock(); try { while(!isSignalled) signalled.await(); } finally { lock.unlock(); } } /** * Signals. All waiters are signaled. */ public void signal () { lock.lock(); try { isSignalled = true; signalled.signalAll(); } finally { lock.unlock(); } } /** * Non-blocking call immediately returns with the current status of the signal. * @return true if signaled. */ public boolean isSignalled() { boolean state = false; lock.lock(); try { state = isSignalled; } finally { lock.unlock(); } return state; } } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/SortSupport.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.RedisException; import org.jredis.Sort; import org.jredis.protocol.Command; import org.jredis.ri.alphazero.semantics.DefaultKeyCodec; public abstract class SortSupport implements Sort { protected volatile boolean stores = false; // protected final String key; protected final byte[] keyBytes; // protected SortSupport (String key, byte[] validatedKeyBytes){ protected SortSupport (byte[] validatedKeyBytes){ // Assert.notNull(key, "key for sort", ClientRuntimeException.class); // this.key = key; this.keyBytes = validatedKeyBytes; } public static final byte[] CRLF = {(byte) 13, (byte)10}; public static final byte[] SPACE = {(byte) 32}; public static final int CRLF_LEN = CRLF.length; public static final int DELIMETER_LEN = SPACE.length; public static final byte ERR_BYTE = (byte) 45; // - public static final byte OK_BYTE = (byte) 43; // + public static final byte COUNT_BYTE = (byte) 42; // * public static final byte SIZE_BYTE = (byte) 36; // $ public static final byte NUM_BYTE = (byte) 58; // : public static final byte ASCII_ZERO = (byte) 48; // 0 private List alphaSpec = new ArrayList(); private List descSpec = new ArrayList(); private List getSpec = new ArrayList(); private List bySpec = new ArrayList(); private List limitSpec = new ArrayList(); private List storeSpec = new ArrayList(); public Sort ALPHA() { // String alphaSpecName = Command.Option.ALPHA.name(); alphaSpec.add(Command.Option.ALPHA.bytes); return this; } public Sort DESC() { // String sortSpecName = Command.Option.DESC.name(); descSpec.add(Command.Option.DESC.bytes); return this; } public Sort BY(K pattern) { // String bySpecName = Command.Option.BY.name(); bySpec.add(Command.Option.BY.bytes); bySpec.add(DefaultKeyCodec.provider().encode(pattern)); return this; } public Sort GET(K pattern) { // String getSpecName = Command.Option.GET.name(); getSpec.add(Command.Option.GET.bytes); getSpec.add(DefaultKeyCodec.provider().encode(pattern)); return this; } public Sort LIMIT(long from, long count) { if(from < 0) { throw new ClientRuntimeException("from in LIMIT clause: " + from); } if(count <= 0) { throw new ClientRuntimeException("count in LIMIT clause: " + from); } // String limitSpecName = Command.Option.LIMIT.name(); String fromString = new Long(from).toString(); String countString = new Long(count).toString(); limitSpec.add(Command.Option.LIMIT.bytes); limitSpec.add(fromString.getBytes()); limitSpec.add(countString.getBytes()); return this; } /** Store the sort results in another key */ public Sort STORE (K destKey) { Assert.notNull(destKey, "deskKey is null", ClientRuntimeException.class); // String storeSpecName = Command.Option.STORE.name(); storeSpec.add(Command.Option.STORE.bytes); storeSpec.add(DefaultKeyCodec.provider().encode(destKey)); stores = true; return this; } private final byte[][] buildSortCmd() { ArrayList sortSpecs = new ArrayList(); sortSpecs.addAll(bySpec); sortSpecs.addAll(limitSpec); sortSpecs.addAll(getSpec); sortSpecs.addAll(descSpec); sortSpecs.addAll(alphaSpec); sortSpecs.addAll(storeSpec); byte[][] sortCmd = new byte[sortSpecs.size() + 1][]; sortCmd[0] = keyBytes; for (int i = 0; i < sortSpecs.size(); i++) { sortCmd[i+1] = sortSpecs.get(i);//.getBytes(); } return sortCmd; } public List exec() throws IllegalStateException, RedisException { // System.out.format("sort spec: [%S %S %S %S %S %S]\n", bySpec, limitSpec, getSpec, descSpec, alphaSpec, storeSpec); List res = null; if(!stores) res = execSort(buildSortCmd()); else res = execSortStore(buildSortCmd()); return res; } public Future> execAsync() { // System.out.format("sort spec: [%S %S %S %S %S %S]\n", bySpec, limitSpec, getSpec, descSpec, alphaSpec, storeSpec); Future> res = null; if(!stores) res = execAsyncSort (buildSortCmd()); else res = execAsyncSortStore(buildSortCmd()); return res; } protected abstract List execSort (byte[]... fullSortCmd) throws IllegalStateException, RedisException; protected abstract List execSortStore (byte[]... fullSortCmd) throws IllegalStateException, RedisException; protected abstract Future> execAsyncSort (byte[]... fullSortCmd); protected abstract Future> execAsyncSortStore (byte[]... fullSortCmd); } ================================================ FILE: core/ri/src/main/java/org/jredis/ri/alphazero/support/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * Utility classes to support this implementation. */ package org.jredis.ri.alphazero.support; ================================================ FILE: core/ri/src/main/java/org/jredis/ri/package-info.java ================================================ /* * Copyright 2009 Joubin Houshyar * * 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. */ /** * This the top level package for the reference implementations of JRedis. * This package may in the future itself contain support classes for the * {@link org.jredis.JRedis} interface and other types in the root package * as barebones clients to redis. *

    * Child packages contain reference implementations of optional packages * of JRedis. */ package org.jredis.ri; ================================================ FILE: core/ri/src/test/java/org/jredis/ri/JRedisTestSuiteBase.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri; import static org.testng.Assert.fail; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Parameters; /** * The grand daddy of all TestNG test classes for the RI test suites and classes, * this class will get loaded with all the general parameters we use for * testing, namely host, port, password, and the DBs we will use to test (which * will be flushed!) *

    * Defaults for values are defined in this class so no testng.xml is required. Change * values in master pom as required. *

    * [Note: as of now, these are defined in the master pom.] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 17, 2009 * @since alpha.0 * */ //TODO: get rid of NG in class name public abstract class JRedisTestSuiteBase extends ProviderTestBase{ // ------------------------------------------------------------------------ // General RI Test Suite Parameters with default values // ------------------------------------------------------------------------ protected String password = "jredis"; protected String host = "localhost"; protected int port = 6379; protected int db1 = 13; protected int db2 = 10; // ------------------------------------------------------------------------ // General RI Test Suite Parameters with default values to avoid XML // ------------------------------------------------------------------------ protected int SMALL_DATA = 128; protected int MEDIUM_DATA = 1024 * 2; protected int LARGE_DATA = 1024 * 512; protected int SMALL_CNT = 10; protected int MEDIUM_CNT = 1000; protected int LARGE_CNT = 100000; protected int expire_secs = 1; protected long expire_wait_millisecs = 1; protected final Random random = new Random(System.currentTimeMillis()); // we'll use these for values protected final byte[] emptyBytes = new byte[0]; protected final String emptyString = ""; protected final List dataList = new ArrayList(); protected final List sparseList = new ArrayList(); protected final List objectList = new ArrayList(); protected final List stringList = new ArrayList(); protected final List patternList = new ArrayList(); protected final List intList = new ArrayList(); protected final List longList= new ArrayList(); protected final List doubleList= new ArrayList(); protected final Set uniqueSet = new HashSet (); protected final Set commonSet = new HashSet (); protected final Set set1 = new HashSet (); protected final Set set2 = new HashSet (); protected final String patternA = "_AAA_"; protected final byte[] smallData = getRandomBytes(SMALL_DATA); protected final byte[] mediumData = getRandomBytes(MEDIUM_DATA); protected final byte[] largeData = getRandomBytes(LARGE_DATA); protected final List keys = new ArrayList(); protected int cnt; protected String key = null; @SuppressWarnings("unused") private byte bytevalue; @SuppressWarnings("unused") private String stringvalue; @SuppressWarnings("unused") private int intValue; @SuppressWarnings("unused") private long longValue; @SuppressWarnings("unused") private TestBean objectvalue; // ------------------------------------------------------------------------ // General RI Test Suite Parameters init // ------------------------------------------------------------------------ /** * This method sets up all the general test parameters for all * classes that inherit from it. * @param password password we'll use to authenticate * @param host host name * @param port port number * @param db1 db index for testing - will be flushed * @param db2 db index for testing - will be flushed */ @Parameters({ "jredis.test.password", "jredis.test.host", "jredis.test.port", "jredis.test.db.1", "jredis.test.db.2", "jredis.test.datasize.small", "jredis.test.datasize.medium", "jredis.test.datasize.large", "jredis.test.cnt.small", "jredis.test.cnt.medium", "jredis.test.cnt.large", "jredis.test.expire.secs", "jredis.test.expire.wait.millisecs" }) @BeforeSuite public void suiteParametersInit( String password, String host, int port, int db1, int db2, int small_data, int medium_data, int large_data, int small_cnt, int medium_cnt, int large_cnt, int expire_secs, int expire_wait_millisecs ) { this.password = password; this.host = host; this.port = port; this.db1 = db1; this.db2 = db2; this.SMALL_DATA = small_data; this.MEDIUM_DATA = medium_data; this.LARGE_DATA = large_data; this.SMALL_CNT = small_cnt; this.MEDIUM_CNT = medium_cnt; this.LARGE_CNT = large_cnt; this.expire_secs = expire_secs; this.expire_wait_millisecs = expire_wait_millisecs; Log.log("Suite parameters initialized "); setupTestSuiteData(); } // ------------------------------------------------------------------------ // Test data setup methods // ------------------------------------------------------------------------ /** * All providers to be tested with the same degree of test data. * We're using random data and can't guarantee exact teset data. * TODO: flip switch to use random or deterministic data. */ @SuppressWarnings("boxing") private final void setupTestSuiteData () { /** setup data */ cnt = MEDIUM_CNT; byte[] zerobytes = new byte[0]; for(int i=0; i " + e.getLocalizedMessage()); // fail("AUTH with password: " + password, e); // } // try { // jredis.select(db1).flushdb().select(db2).flushdb().select(db1); // Log.log("TEST-PREP: %s:%d Redis server DB %d & %d flushed", host, port, db1, db2); // } // catch (RedisException e) { // Log.error("SELECT/FLUSHDB for test prep" + password); // fail("SELECT/FLUSHDB for test prep", e); // } } // ------------------------------------------------------------------------ // Helper methods // ------------------------------------------------------------------------ /** * Creates a random ascii string * @param length * @return */ protected String getRandomAsciiString (int length) { StringBuilder builder = new StringBuilder(length); for(int i = 0; i void assertDidRaiseRuntimeError (Runnable test, Class errtype){ boolean didRaiseError = false; try { test.run(); } catch (RuntimeException t){ if(errtype.isAssignableFrom(t.getClass())) didRaiseError = true; } catch (Exception e){ fail("Unexpected exception", e); } finally { if(!didRaiseError) { fail("Failed to raise expected RuntimeError " + errtype.getCanonicalName()); } } } // ------------------------------------------------------------------------ // INNER TYPES USED FOR TESTING // ============================================================== TestBean // ------------------------------------------------------------------------ /** * This is a simple {@link Serializable} class that we use to test object * values. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 18, 2009 * @since alpha.0 * */ public static class TestBean implements Serializable { /** */ private static final long serialVersionUID = 4457509786469904810L; public final long getCreated_on() {return named_on;} public final void setCreated_on(long created_on) {this.named_on = created_on;} public final String getName() {return name;} public final void setName(String name) {this.name = name;} public final byte[] getData() { return data;} public final void setData(byte[] data) { this.data = data;} private long named_on; private String name; private byte[] data; public TestBean() { // named_on = System.currentTimeMillis(); } public TestBean(String string) { this(); name = string; named_on = System.currentTimeMillis(); } @Override public String toString() { return "[" + getClass().getSimpleName() + " | name: " + getName() + " created on: " + getCreated_on() + "]"; } @Override public boolean equals (Object o) { if(o instanceof TestBean) { TestBean isItMe = (TestBean) o; return isItMe.getName().equals(name) && isItMe.getCreated_on()==this.named_on; } return false; } @Override public int hashCode() { return name.hashCode() ^ (int)named_on; } } // ------------------------------------------------------------------------ // Test support - mildly enhanced TESTNG Assert semantics // ------------------------------------------------------------------------ // TODO: check latest version of testng // // notNull // public static final void assertNotNull(Object object, String msgfmt, Object...optionalFmtArgs){ // String message = String.format(msgfmt, optionalFmtArgs); // Assert.assertNotNull (object, message); // } // // null // public static final void assertNull(Object object, String msgfmt, Object...optionalFmtArgs){ // String message = String.format(msgfmt, optionalFmtArgs); // Assert.assertNull (object, message); // << has bug. reports a boolean comp result -- TODO: fix and patch. // } // // // equals // public static final void assertEquals(Object actual, Object expected, String msgfmt, Object...optionalFmtArgs){ // String message = String.format(msgfmt, optionalFmtArgs); // Assert.assertEquals (actual, expected, message); // } // public static final void assertEquals(byte[] actual, byte[] expected, String msgfmt, Object...optionalFmtArgs){ // String message = String.format(msgfmt, optionalFmtArgs); // Assert.assertEquals (actual, expected, message); // } // // // true/false // public static final void assertTrue(boolean condition, String msgfmt, Object...optionalFmtArgs){ // String message = String.format(msgfmt, optionalFmtArgs); // Assert.assertTrue (condition, message); // } // public static final void assertFalse(boolean condition, String msgfmt, Object...optionalFmtArgs){ // String message = String.format(msgfmt, optionalFmtArgs); // Assert.assertFalse (condition, message); // } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/ProviderTestBase.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri; import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.BeforeTest; /** * Support for tests of interface T implementation providers. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 10, 2009 * @since alpha.0 * */ public abstract class ProviderTestBase { // ======================================================================== // Test Properties // ======================================================================== /** the JRedis implementation being tested */ protected T provider = null; // ------------------------------------------------------------------------ // JRedisFuture Provider initialize methods // ------------------------------------------------------------------------ /** * Sets the {@link JRedis} implementation provider for the test suite */ @BeforeTest public void setProvider () { try { T provider = newProviderInstance(); setProviderInstance (provider); Log.log("%s.setProvider - done", this.getClass().getSimpleName()); } catch (ClientRuntimeException e) { Log.error(e.getLocalizedMessage()); } } /** * Extension point: Tests for specific implementations of T * implement this method to create the provider instance. * @return T implementation instance */ protected abstract T newProviderInstance () ; /** * Must be called by a BeforeTest method to set the jredis parameter. * @param provider that is being tested. */ private final void setProviderInstance (T provider) { this.provider = provider; Log.log("\n\nTEST: " + "\n\t-----------------------------------------------\n" + "\tProvider Class: %s" + "\n\t-----------------------------------------------\n", provider.getClass().getCanonicalName()); } /** * @return the T instance used for the provider tests */ protected final T getProviderInstance() { return provider; } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/adhoc/AdHocTestChunkPipeline.java ================================================ package org.jredis.ri.adhoc; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisChunkedPipeline; import org.jredis.ri.alphazero.JRedisPipeline; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; @SuppressWarnings("unused") public class AdHocTestChunkPipeline { static final int MAX_ITER = Integer.MAX_VALUE; public static void main(String[] args) throws Throwable { try { AdHocTestChunkPipeline tester = new AdHocTestChunkPipeline(); int iter = 0; while(iter < MAX_ITER){ tester.run(); iter++; } tester.stop(); } catch (Exception e) { e.printStackTrace(); // Log.error("fault", e); // TODO: } } final ConnectionSpec spec; final JRedisFuture jredis; public AdHocTestChunkPipeline() { spec = DefaultConnectionSpec.newSpec().setCredentials("jredis").setDatabase(11); jredis = new JRedisChunkedPipeline(spec); // jredis = new JRedisPipeline(spec); } static final int wcnt = 2; static final int reqnums = 150000; private void stop() { jredis.quit(); } private void run() { final Thread[] workers = new Thread[wcnt]; for(int i=0;i< workers.length; i++){ String tname = String.format("w-%02d", i); workers[i] = worker(jredis, tname); } final long start = System.currentTimeMillis(); for(Thread t : workers){ t.start(); } for(Thread t : workers){ try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } Log.log("%d x %d @ %d msecs", wcnt, reqnums, System.currentTimeMillis() - start); } private Thread worker (final JRedisFuture jredis, String tname) { final Runnable task = task(jredis); return new Thread(task, tname); } // @SuppressWarnings("unused") private Runnable task (final JRedisFuture jredis) { return new Runnable() { final JRedisFuture conn = jredis; @Override public void run() { final String tname = Thread.currentThread().getName(); final byte[] key = tname.getBytes(); final byte[] cntr = (tname + "#").getBytes(); Future fCntr = null; for(int i=0; i infomap = jredis.info(); for (String k : infomap.keySet()) { System.out.format("%s\n", k); } } catch (Throwable e) { e.printStackTrace(); Log.error("synch run error", e); throw new RuntimeException(e); } finally { jredis.quit(); } } public void runasync() { try { Map infomap = jredis_async.info().get(); for(String k : infomap.keySet()){ System.out.format("%s => %s\n", k, infomap.get(k)); } System.out.format("\n\n### InfoMap needs to be updated to include ###\n"); for (RedisInfo info : RedisInfo.values()){ if(infomap.get(info.name()) == null) Log.log("%s IS MISSING", info.name()); } for(String k : infomap.keySet()){ try { RedisInfo.valueOf(k); } catch (IllegalArgumentException e){ System.out.format("%s,\n", k); } } System.out.format("### END ###\n"); } catch (Throwable t) { t.printStackTrace(); } finally { jredis_async.quit(); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/adhoc/AdHocTestNoConnection.java ================================================ package org.jredis.ri.adhoc; import java.util.concurrent.Future; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisPipeline; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; public class AdHocTestNoConnection { private static final int NOT_A_USUAL_REDIS_PORT = 9999; public static void main(String[] args) throws Throwable { try { new AdHocTestNoConnection().run(); } catch (Exception e) { Log.error(e.getMessage()); } } final ConnectionSpec spec; JRedisFuture jredis = null; public AdHocTestNoConnection() throws Throwable{ spec = DefaultConnectionSpec.newSpec("localhost", NOT_A_USUAL_REDIS_PORT, 11, "jredis".getBytes()); jredis = new JRedisPipeline(spec); } /** this is not supposed to get called unless you actually run redis on port 9999 :P */ public void run() { final byte[] key = "foo".getBytes(); for(;;){ try { @SuppressWarnings("unused") Future fcntr = jredis.incr(key); } catch (Throwable t) { t.printStackTrace(); } } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/ConcurrentJRedisProviderTestsBase.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.testng.Assert.*; import static org.jredis.ri.alphazero.support.DefaultCodec.*; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.protocol.Command; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; /** * Tests {@link JRedis} implementations that support concurrent access. Will * not run the full suite of tests (per {@link JRedisProviderTestsBase} concurrently. * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 7, 2009 * @since alpha.0 * */ public abstract class ConcurrentJRedisProviderTestsBase extends JRedisProviderTestsBase { /* switch (command.responseType){ case BULK: case MULTI_BULK: case STATUS: case STRING: case VIRTUAL: break; */ @Test(invocationCount=20, threadPoolSize=5, singleThreaded=false) public void testConcurrentBulkCommands() { String cmd = Command.GET.responseType.name(); String threadName = Thread.currentThread().getName(); String key = threadName + "::" + keys.get(0); Log.log("CONCURRENT TEST: %s resp type command | key: %s", cmd, key); try { provider.del(key); provider.set (key, threadName); assertTrue (provider.exists(key), "key should exist"); String value = toStr (provider.get(key)); assertTrue(value.endsWith(threadName), "value of key should be the thread name"); provider.del(key); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * */ @Test(invocationCount=20, threadPoolSize=5, singleThreaded=false) public void testConcurrentBooleanCommands() { String cmd = Command.EXISTS.responseType.name(); String threadName = Thread.currentThread().getName(); String key = threadName + "::" + keys.get(0); Log.log("CONCURRENT TEST: %s resp type command | key: %s", cmd, key); try { provider.set (key, threadName); // assertTrue (provider.del (key), "del response should be OK"); assertEquals (provider.del(key), 1, "del response should be 1 for one key deleted"); assertFalse (provider.exists(key), "deleted key response should not exist"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * */ @Test(invocationCount=20, threadPoolSize=5, singleThreaded=false) public void testConcurrentNumberCommands() { String cmd = Command.INCR.responseType.name(); String cntr_key = Thread.currentThread().getName() + "::" + keys.get(0); Log.log("CONCURRENT TEST: %s resp type command | key: %s", cmd, cntr_key); try { provider.del(cntr_key); for(int i = 1; i<50; i++) assertEquals(i, provider.incr(cntr_key)); provider.del(cntr_key); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisAsyncClientTest.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.testng.Assert.fail; import org.jredis.ClientRuntimeException; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.AfterTest; import org.testng.annotations.Test; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 6, 2009 * @since alpha.0 * */ @Test(sequential = true, suiteName="JRedisAsyncClient-tests") public class JRedisAsyncClientTest extends JRedisFutureProviderTestsBase { // ------------------------------------------------------------------------ // TEST SETUP // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.ri.ProviderTestBase#newProviderInstance() */ @Override protected JRedisFuture newProviderInstance () { JRedisFuture provider = null; try { ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(this.host, this.port, this.db2, this.password.getBytes()); provider = new JRedisAsyncClient(connectionSpec); } catch (ClientRuntimeException e) { Log.error(e.getLocalizedMessage()); } return provider; } // ------------------------------------------------------------------------ // The Tests // ========================================================= JRedisClient /** * We define and run any additional, provider specific tests here. The * basic generally applicable JRedis interface method test are defined * in the super class. * * Here we test Quit in a post test method to insure all tests have been * completed. */ // ------------------------------------------------------------------------ /** * Pipeline quit. * We first ping and await the response to insure pipeline has processed * all pending responses, and then issue the quit command. */ @AfterTest public void testQuit() { try { JRedisFuture pipeline = getProviderInstance(); pipeline.ping().get(); pipeline.quit().get(); } catch (Exception e) { fail("QUIT" + e); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisChunkedPipelineClientTest.java ================================================ /* * Copyright 2009-2012 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.testng.Assert.fail; import org.jredis.ClientRuntimeException; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.AfterTest; import org.testng.annotations.Test; /** * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Jan 18, 2012 * */ //@Test(invocationCount=20, threadPoolSize=5, sequential=false) @Test(singleThreaded = false, suiteName="JRedisChunkedPipeline-tests", invocationCount=20, threadPoolSize=5) public class JRedisChunkedPipelineClientTest extends JRedisFutureProviderTestsBase { // ------------------------------------------------------------------------ // TEST SETUP // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.ri.ProviderTestBase#newProviderInstance() */ @Override protected JRedisFuture newProviderInstance () { JRedisFuture provider = null; try { ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(this.host, this.port, this.db2, this.password.getBytes()); provider = new JRedisChunkedPipeline(connectionSpec); } catch (ClientRuntimeException e) { Log.error(e.getLocalizedMessage()); } return provider; } // ------------------------------------------------------------------------ // The Tests // ========================================================= JRedisClient /** * We define and run any additional, provider specific tests here. The * basic generally applicable JRedis interface method test are defined * in the super class. * * Here we test Quit in a post test method to insure all tests have been * completed. */ // ------------------------------------------------------------------------ /** * Pipeline quit. * We first ping and await the response to insure pipeline has processed * all pending responses, and then issue the quit command. */ @AfterTest() public void testQuit() { try { JRedisFuture pipeline = getProviderInstance(); pipeline.quit(); } catch (Exception e) { fail("QUIT" + e); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisClientTest.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.testng.Assert.fail; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.AfterTest; import org.testng.annotations.Test; /** * For testing the JRedis test suite for {@link JRedisClient} implementation. * TODO: should also do a minimal test using {@link ConnectionSpec}. * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 17, 2009 * @since alpha.0 * */ //TODO: get rid of NG in class name @Test(singleThreaded = true, suiteName="JRedisClient-tests") public class JRedisClientTest extends JRedisProviderTestsBase { // ------------------------------------------------------------------------ // TEST SETUP // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.ri.alphazero.JRedisProviderTestNGBase#newJRedisProviderInstance() */ protected JRedis newProviderInstance () { JRedis provider = null; try { provider = new JRedisClient (this.host, this.port, this.password, this.db1); } catch (ClientRuntimeException e) { Log.error(e.getLocalizedMessage()); } return provider; } // ------------------------------------------------------------------------ // The Tests // ========================================================= JRedisClient /** * We define and run any additional, provider specific tests here. The * basic generally applicable JRedis interface method test are defined * in the super class. * * Here we test Quit in a post test method to insure all tests have been * completed. */ // ------------------------------------------------------------------------ /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#auth(java.lang.String)}. */ @AfterTest public void testQuit() { Log.log("TEST: QUIT command"); try { JRedis provider = getProviderInstance(); provider.quit (); } catch (Exception e) { fail("QUIT" + e); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisFutureProviderTestsBase.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.jredis.ri.alphazero.support.DefaultCodec.toStr; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.JRedisFuture; import org.jredis.ObjectInfo; import org.jredis.Query; import org.jredis.RedisException; import org.jredis.RedisType; import org.jredis.ZSetEntry; import org.jredis.protocol.Command; import org.jredis.protocol.ResponseStatus; import org.jredis.ri.JRedisTestSuiteBase; import org.jredis.ri.alphazero.support.Convert; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; /** * [TODO: cleanup the unit test comments] * * Provides the comprehensive set of tests of all {@link JRedisFuture} methods. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 10, 2009 * @since alpha.0 * */ public abstract class JRedisFutureProviderTestsBase extends JRedisTestSuiteBase { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** JRedis Command being tested -- for log info */ private String cmd; // ------------------------------------------------------------------------ // The Tests // ======================================================= JRedisFuture === /* * We define and run provider agnostic tests here. This means we run a set * of JRedisFuture interface method tests that every connected JRedisFuture * implementation should be able to support. * * The following commands are omitted: * 1 - QUIT: since we may be testing a multi-connection provier * 2 - SHUTDOWN: for the same reason as QUIT * 3 - MOVE and SELECT */ // ------------------------------------------------------------------------ // @Test // public void testTemplate() throws InterruptedException { // cmd = Command.PING.code + " | " + Command.SETNX.code + " byte[] | " + Command.GET; // Log.log("TEST: %s command", cmd); // // try { // provider.flushdb(); // try { // } // catch(ExecutionException e){ // Throwable cause = e.getCause(); // fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); // } // } // catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } // } @Test public void testSrandmember () throws InterruptedException { cmd = Command.SRANDMEMBER.code + " String | " + Command.SMEMBERS; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> saddResponses = new ArrayList>(); for(int i=0;i> smembersResponse = provider.smembers(setkey); Future srandmemberResponse = provider.srandmember(setkey); try { for(Future resp : saddResponses) assertTrue (resp.get().booleanValue(), "sadd of random element should have been true"); List members = toStr(smembersResponse.get()); assertEquals(members.size(), SMALL_CNT, "set members count should be SMALL_CNT"); String randmember = toStr(srandmemberResponse.get()); boolean found = false; for(String m : members) { if(m.equals(randmember)) { found = true; break; } } assertTrue(found, "random member should have been part of the members list"); // edge cases // empty set String emptyset = "empty-set"; provider.sadd(emptyset, "delete-me"); provider.srem(emptyset, "delete-me"); assertEquals(provider.smembers(emptyset).get(), Collections.EMPTY_LIST, "smembers should return an empty list"); assertEquals(provider.srandmember(emptyset).get(), null, "random member of empty set should be null"); // non existent key String nonsuch = "no-such-key"; assertEquals(provider.smembers(nonsuch).get(), Collections.EMPTY_LIST, "members of non existent key set should be empty"); assertEquals (provider.srandmember(nonsuch).get(), null, "random member of non existent key set should be null"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSmoveStringByteArray() throws InterruptedException{ cmd = Command.SMOVE.code + " byte[]"; Log.log("TEST: %s command", cmd); provider.flushdb(); String srckey = keys.get(0); String destkey = keys.get(1); List> saddResponses = new ArrayList>(); for(int i=0;i> smoveResponses = new ArrayList>(); for(int i=0;i resp : saddResponses) assertTrue (resp.get().booleanValue(), "sadd of random element should have been true"); for(Future resp : smoveResponses) assertTrue (resp.get().booleanValue(), "smove of element should have been true"); for(int i=0;i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testScard() throws InterruptedException { cmd = Command.SCARD.code + " Java Object"; Log.log("TEST: %s command", cmd); provider.flushdb(); String setkey = keys.get(0); List> saddResponses = new ArrayList>(); for(int i=0;i scardResp = provider.scard(setkey); try { provider.flushdb(); try { for(Future resp : saddResponses) assertTrue (resp.get().booleanValue(), "sadd of random object should have been true"); assertEquals (scardResp.get().longValue(), MEDIUM_CNT, "scard value should have been MEDIUM_CNT"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZcard() throws InterruptedException { cmd = Command.ZCARD.code + " Java Object"; Log.log("TEST: %s command", cmd); provider.flushdb(); String setkey = keys.get(0); List> zaddResponses = new ArrayList>(); for(int i=0;i zcardResp = provider.zcard(setkey); try { provider.flushdb(); try { for(Future resp : zaddResponses) assertTrue (resp.get().booleanValue(), "zadd of random object should have been true"); assertEquals (zcardResp.get().longValue(), MEDIUM_CNT, "zcard value should have been MEDIUM_CNT"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSismemberStringByteArray() throws InterruptedException { cmd = Command.SISMEMBER.code + " byte[]"; Log.log("TEST: %s command", cmd); String setkey = keys.get(0); provider.flushdb(); List> saddResponses = new ArrayList>(); for(int i=0;i> sismemberResponses = new ArrayList>(); for(int i=0;i resp : saddResponses) assertTrue (resp.get().booleanValue(), "sadd of random element should have been true"); for(Future resp : sismemberResponses) assertTrue (resp.get().booleanValue(), "set membership test should have been true"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSmembers() throws InterruptedException{ cmd = Command.SMEMBERS.code + " byte[] | " + Command.SADD + "| " + Command.SCARD; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> saddResponses = new ArrayList>(); for(int i=0;i scardResp = provider.scard(setkey); Future> smembersResp = provider.smembers(setkey); String setkey2 = keys.get(2); provider.sadd(setkey2, dataList.get(0)); provider.srem(setkey2, dataList.get(0)); Future scardResp2 = provider.scard(setkey2); Future> smembersResp2 = provider.smembers(setkey2); try { for(Future resp : saddResponses) assertTrue (resp.get().booleanValue(), "sadd of random element should have been true"); List members = smembersResp.get(); assertTrue(members.size() == SMALL_CNT, "smembers should have returned a list of SMALL_CNT size"); assertTrue(members.size() == scardResp.get().longValue(), "smembers should have returned a list of scard size"); List members2 = smembersResp2.get(); assertEquals(scardResp2.get().longValue(), 0, "setkey2 should be zero"); assertEquals(members2, Collections.EMPTY_LIST, "smembers should have returned empty"); // assertTrue(members2.size() == scardResp2.get().longValue(), "smembers should have returned a list of scard size"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZremStringByteArray() throws InterruptedException{ cmd = Command.ZADD.code + " byte[] | " + Command.ZREM.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> expectedOKRemResponses = new ArrayList>(); for(int i=0;i resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); for(Future resp : expectedOKRemResponses) assertTrue (resp.get().booleanValue(), "zadd of existing element should have been false"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZaddStringByteArray() throws InterruptedException{ cmd = Command.ZADD.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> expectedErrorResponses = new ArrayList>(); for(int i=0;i resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); for(Future resp : expectedErrorResponses) assertFalse (resp.get().booleanValue(), "zadd of existing element should have been false"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZscoreAndZincrbyStringByteArray() throws InterruptedException{ cmd = Command.ZSCORE.code + " byte[] | " + Command.ZINCRBY.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> scores = new ArrayList>(); for(int i=0;i> incrementedScores = new ArrayList>(); for(int i=0;i noneSuchKeyScore = provider.zscore(setkey, "no such member"); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); int i=0; for(Future score : scores){ assertEquals (score.get(), doubleList.get(i), "zscore of element should have been " + doubleList.get(i)); i++; } i=0; for(Future score : incrementedScores){ assertEquals (score.get(), doubleList.get(i) + increment, "zincr of element should be " + doubleList.get(i) + increment); i++; } assertNull(noneSuchKeyScore.get(), "zscore of none existent member of sorted set should be null"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrangebyscoreStringByteArray() throws InterruptedException{ cmd = Command.ZRANGEBYSCORE.code + " byte[] | " + Command.ZSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> frRange = provider.zrangebyscore(setkey, 0, SMALL_CNT); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); List range = frRange.get(); assertTrue(range.size() > 0, "should have non empty results for range by score here"); for(int i=0;i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZremrangebyscoreStringByteArray() throws InterruptedException{ cmd = Command.ZREMRANGEBYSCORE.code + " byte[] | " + Command.ZSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i frRemCnt = provider.zremrangebyscore(setkey, 0, SMALL_CNT); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); long remCnt = frRemCnt.get().longValue(); assertTrue(remCnt > 0, "should have non-zero number of rem cnt for zremrangebyscore"); assertEquals(remCnt, SMALL_CNT+1, "should have specific number of rem cnt for zremrangebyscore"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZcountStringByteArray() throws InterruptedException{ cmd = Command.ZCOUNT.code + " byte[] | " + Command.ZADD.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i frCount = provider.zcount(setkey, 0, SMALL_CNT); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); long remCnt = frCount.get().longValue(); assertTrue(remCnt > 0, "should have non-zero number of rem cnt for zremrangebyscore"); assertEquals(remCnt, SMALL_CNT+1, "should have specific number for zcount"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZremrangebyrankStringByteArray() throws InterruptedException{ cmd = Command.ZREMRANGEBYRANK.code + " byte[] | " + Command.ZSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i frRemCnt = provider.zremrangebyrank(setkey, 0, SMALL_CNT); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); long remCnt = frRemCnt.get().longValue(); assertTrue(remCnt > 0, "should have non-zero number of rem cnt for zremrangebyrank"); assertEquals(remCnt, SMALL_CNT+1, "should have specific number of rem cnt for zremrangebyrank"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrankStringByteArray() throws InterruptedException{ cmd = Command.ZRANK.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> rankingResps = new ArrayList>(); for(int i=0;i frRankForMissingElement = provider.zrank(setkey, dataList.get(SMALL_CNT+1)); Future frRankForNoSuchSet = provider.zrank("no-such-set", dataList.get(0)); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); int i=0; for(Future resp : rankingResps) assertEquals (resp.get().longValue(), i++, "zrank of element"); assertEquals (frRankForMissingElement.get().longValue(), -1, "zrank against non-existent member should be -1"); assertEquals (frRankForNoSuchSet.get().longValue(), -1, "zrank against non-existent key should be -1"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrevrankStringByteArray() throws InterruptedException{ cmd = Command.ZREVRANK.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i<=SMALL_CNT; i++) expectedOKResponses.add (provider.zadd(setkey, i, dataList.get(i))); List> rankingResps = new ArrayList>(); for(int i=0;i<=SMALL_CNT; i++) rankingResps.add(provider.zrevrank(setkey, dataList.get(i))); Future frRankForMissingElement = provider.zrevrank(setkey, dataList.get(SMALL_CNT+1)); Future frRankForNoSuchSet = provider.zrevrank("no-such-set", dataList.get(0)); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); int i=0; for(Future resp : rankingResps) assertEquals (resp.get().longValue(), SMALL_CNT - i++, "zrevrank of element"); assertEquals (frRankForMissingElement.get().longValue(), -1, "zrevrank against non-existent member should be -1"); assertEquals (frRankForNoSuchSet.get().longValue(), -1, "zrevrank against non-existent key should be -1"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrangeWithscoresStringByteArray() throws InterruptedException{ cmd = Command.ZRANGE$OPTS.code + " byte[] | " + Command.ZSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> frZValues = provider.zrange(setkey, 0, SMALL_CNT); Future> frSubset = provider.zrangeSubset(setkey, 0, SMALL_CNT); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); List zvalues = frZValues.get(); List zsubset = frSubset.get(); for(int i=0;i0) assertTrue(zsubset.get(i).getScore() >= zsubset.get(i-1).getScore(), "range member score should be bigger or equal to previous range member. idx: " + i); } } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrevrangeWithscoresStringByteArray() throws InterruptedException{ cmd = Command.ZREVRANGE$OPTS.code + " byte[] | " + Command.ZSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> frZValues = provider.zrevrange(setkey, 0, SMALL_CNT); Future> frSubset = provider.zrevrangeSubset(setkey, 0, SMALL_CNT); try { for(Future resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "zadd of random element should have been true"); List zvalues = frZValues.get(); List zsubset = frSubset.get(); for(int i=0;i= zsubset.get(i+1).getScore(), "range member score should be smaller or equal to previous range member. idx: " + i); if(i>0) assertTrue(zsubset.get(i).getScore() <= zsubset.get(i-1).getScore(), "range member score should be bigger or equal to previous range member. idx: " + i); } } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSaddStringByteArray() throws InterruptedException{ cmd = Command.SADD.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); List> expectedOKResponses = new ArrayList>(); for(int i=0;i> expectedErrorResponses = new ArrayList>(); for(int i=0;i resp : expectedOKResponses) assertTrue (resp.get().booleanValue(), "sadd of random element should have been true"); for(Future resp : expectedErrorResponses) assertFalse (resp.get().booleanValue(), "sadd of existing element should have been false"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSort() throws InterruptedException{ cmd = Command.SORT.code; Log.log("TEST: %s command", cmd); final String setkey = "set-key"; final String listkey = "list-key"; try { provider.flushdb(); for(int i=0; i> sortListResp1 = provider.sort(listkey).ALPHA().LIMIT(0, cnt1).DESC().execAsync(); Future> sortListResp2 = provider.sort(listkey).ALPHA().LIMIT(10, cnt2).DESC().execAsync(); Future> sortListResp3 = provider.sort(listkey).ALPHA().LIMIT(MEDIUM_CNT-1, cnt3).DESC().execAsync(); Future> sortSetResp = provider.sort(setkey).ALPHA().LIMIT(0, 555).DESC().execAsync(); try { assertEquals(sortListResp1.get().size(), cnt1, "expecting sort results of size MEDIUM_CNT"); assertEquals(sortListResp2.get().size(), cnt2, "expecting sort results of size 9"); assertEquals(sortListResp3.get().size(), cnt3, "expecting sort results of size 1"); Log.log("TEST: SORTED LIST "); for(String s : toStr(sortListResp1.get())) System.out.format("[t.1] %s\n", s); Log.log("TEST: SORTED LIST "); for(String s : toStr(sortListResp2.get())) System.out.format("[t.1] %s\n", s); Log.log("TEST: SORTED LIST "); for(String s : toStr(sortListResp3.get())) System.out.format("[t.1] %s\n", s); Log.log("TEST: SORTED SET "); for(String s : toStr(sortSetResp.get())) System.out.format("%s\n", s); String destKey = String.format("%s_store", listkey); List ssres = provider.sort(listkey).ALPHA().LIMIT(0, MEDIUM_CNT).DESC().STORE(destKey).execAsync().get(); assertNotNull(ssres, "result of srot with STORE should be non-null"); assertEquals(ssres.size(), 1, "result of sort with STORE should be a list of single entry (the stored list's size)"); long sortedListSize = Query.Support.unpackValue(ssres); assertEquals(sortedListSize, MEDIUM_CNT); RedisType type = provider.type(destKey).get(); assertEquals(type, RedisType.list, "dest key of SORT .. STORE should be a LIST"); long sslistSize = provider.llen(destKey).get(); assertEquals(sslistSize, sortedListSize, "result of SORT ... STORE and LLEN of destkey list should be same"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } // force errors // count can't be zero Runnable invalidLimitSpec = new Runnable() { public void run() { try { provider.sort(listkey).ALPHA().LIMIT(0, 0).DESC().exec(); } catch (Throwable t) { throw new RuntimeException ("", t); } } }; assertDidRaiseRuntimeError(invalidLimitSpec, RuntimeException.class); // LIMIT from must be positive, {0...n} Runnable invalidLimitSpec2 = new Runnable() { public void run() { try { provider.sort(listkey).ALPHA().LIMIT(-1, 1).DESC().exec(); } catch (Throwable t) { throw new RuntimeException ("", t); } } }; assertDidRaiseRuntimeError(invalidLimitSpec2, RuntimeException.class); } @Test public void testLsetStringIntByteArray() throws InterruptedException { cmd = Command.LSET.code + " byte[] | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a MEDIUM list String listkey = keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); // now we'll change their values for(int i=0; i> lrangeResp1 = provider.lrange(listkey, 0, LARGE_CNT); // now we'll change their values using the negative index mode int lim = SMALL_CNT*-1; for(int i=-1; i>lim; i--) provider.lset(listkey, i, dataList.get(i*-1)); Future> lrangeResp2 = provider.lrange(listkey, 0, LARGE_CNT); // test edge conditions // out of range Future expectedErrorResp = provider.lset(listkey, SMALL_CNT, dataList.get(0)); try { assertTrue (llenResp.get().longValue() == SMALL_CNT, "list length should be SMALL_CNT"); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLremStringByteArrayInt() throws InterruptedException { cmd = Command.LREM.code + " byte[] | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a MEDIUM list String listkey = keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); // everysingle one of these should work and remove exactly 1 item Future lremResp1 = provider.lrem(listkey, dataList.get(0), 0); Future lremResp2 = provider.lrem(listkey, dataList.get(1), -1); Future lremResp3 = provider.lrem(listkey, dataList.get(2), 1); Future lremResp4 = provider.lrem(listkey, dataList.get(3), 2); Future lremResp5 = provider.lrem(listkey, dataList.get(4), -2); // everysingle one of these should work and remove NOTHING Future lremResp6 = provider.lrem(listkey, dataList.get(0), 0); Future lremResp7 = provider.lrem(listkey, dataList.get(1), -1); Future lremResp8 = provider.lrem(listkey, dataList.get(2), 1); Future lremResp9 = provider.lrem(listkey, dataList.get(3), 2); Future lremResp10 = provider.lrem(listkey, dataList.get(4), -2); try { long listcnt = llenResp.get(); assertTrue (listcnt == MEDIUM_CNT, "list length should be MEDIUM_CNT"); assertEquals(1, lremResp1.get().longValue()); assertEquals(1, lremResp2.get().longValue()); assertEquals(1, lremResp3.get().longValue()); assertEquals(1, lremResp4.get().longValue()); assertEquals(1, lremResp5.get().longValue()); assertEquals(0, lremResp6.get().longValue()); assertEquals(0, lremResp7.get().longValue()); assertEquals(0, lremResp8.get().longValue()); assertEquals(0, lremResp9.get().longValue()); assertEquals(0, lremResp10.get().longValue()); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLrange() throws InterruptedException { cmd = Command.LRANGE.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a MEDIUM list String listkey = keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); Future> lrangeResp = provider.lrange(listkey, 0, MEDIUM_CNT-1); try { // provider.ping().get(); // sanity check long listcnt = llenResp.get(); assertTrue (listcnt == MEDIUM_CNT, "list length should be MEDIUM_CNT"); List items = lrangeResp.get(); assertEquals (items.size(), MEDIUM_CNT, "list range 0->MEDIUM_CNT length should be MEDIUM_CNT"); for(int i=0; iCNT should be the same as nth dataitem, where n is " + i); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSubstr() throws InterruptedException { cmd = Command.SUBSTR.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String key = keys.get(0); byte[] value = dataList.get(0); provider.set(key, value); List> frBytesRespList = new ArrayList>(); for(int i=0; i frFullValue1 = provider.substr(key, 0, -1); Future frFullValue2 = provider.substr(key, 0, value.length); Future frExpectedNull = provider.substr(key, -1, 0); try { assertEquals(frFullValue1.get(), value, "full range substr should be equal to value"); assertEquals(frFullValue2.get(), value, "full range substr should be equal to value"); assertEquals(frExpectedNull.get(), new byte[0], "substr with -1 from idx should be zero-length array"); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testRpop() throws InterruptedException { cmd = Command.RPOP.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); List> rpops = new ArrayList>(); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLpop() throws InterruptedException { cmd = Command.LPOP.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); List> lpops = new ArrayList>(); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLindex() throws InterruptedException { cmd = Command.LINDEX.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLtrim() throws InterruptedException { cmd = Command.LTRIM.code + " | " + Command.LLEN.code + " | " + Command.LRANGE.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); try { long listcnt = llenResp.get(); // sanity check assertTrue (listcnt == SMALL_CNT, "list length should be SMALL_CNT"); provider.ltrim(listkey, 0,listcnt-1); // trim nothing assertTrue (provider.llen(listkey).get() == listcnt, "trim from end to end - no delta expected"); provider.ltrim(listkey, 1, listcnt-1); // remove the head assertTrue (provider.llen(listkey).get() == listcnt-1, "trim head - len should be --1 expected"); listcnt = provider.llen(listkey).get(); assertEquals(listcnt, SMALL_CNT - 1, "list length should be SMALL_CNT - 1"); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLpushStringByteArray() throws InterruptedException { cmd = Command.LPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); boolean expected = false; try { byte[] nil = null; provider.lpush("foo", nil); } catch(IllegalArgumentException e) { expected = true; } assertTrue(expected, "expecting exception for null value to RPUSH"); String listkey = this.keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); Future> lrangeResp = provider.lrange(listkey, 0, SMALL_CNT); try { // use LLEN: size should be small count assertTrue(llenResp.get()==SMALL_CNT, "LLEN after LPUSH is wrong"); // use LRANGE 0 cnt: equal size and data should be same in order List range = lrangeResp.get(); assertTrue(range.size()==SMALL_CNT, "range size after LPUSH is wrong"); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testLpoppushStringByteArray() throws InterruptedException { cmd = Command.RPOPLPUSH.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i llenRespBefore = provider.llen(listkey); List> poppushResponses = new ArrayList>(); for(int i=0; i llenRespAfter = provider.llen(listkey); try { // use LLEN: size should be small count assertTrue(llenRespBefore.get()==SMALL_CNT, "LLEN after LPUSH is wrong"); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testRpushStringByteArray() throws InterruptedException { cmd = Command.RPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); boolean expected = false; try { byte[] nil = null; provider.rpush("foo", nil); } catch(IllegalArgumentException e) { expected = true; } assertTrue(expected, "expecting exception for null value to RPUSH"); String listkey = this.keys.get(0); for(int i=0; i llenResp = provider.llen(listkey); Future> lrangeResp = provider.lrange(listkey, 0, SMALL_CNT); try { // use LLEN: size should be small count assertTrue(llenResp.get()==SMALL_CNT, "LLEN after RPUSH is wrong"); // use LRANGE 0 cnt: equal size and data should be same in order List range = lrangeResp.get(); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testMget() throws InterruptedException { cmd = Command.MGET.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); for(int i=0; i> mgetResp1 = provider.mget(keys.get(0)); Future> mgetResp2 = provider.mget(keys.get(0), keys.get(1)); Future> mgetResp3 = provider.mget(keys.get(0), keys.get(1), keys.get(2)); Future> mgetResp4 = provider.mget("foo", "bar", "paz"); try { List values = null; values = mgetResp1.get(); assertEquals(values.size(), 1, "one value expected"); for(int i=0; i<1; i++) assertEquals(values.get(i), dataList.get(i)); values = mgetResp2.get(); assertEquals(values.size(), 2, "2 values expected"); for(int i=0; i<2; i++) assertEquals(values.get(i), dataList.get(i)); values = mgetResp3.get(); assertEquals(values.size(), 3, "3 values expected"); for(int i=0; i<3; i++) assertEquals(values.get(i), dataList.get(i)); values = mgetResp4.get(); assertEquals(values.size(), 3, "3 values expected"); for(int i=0; i<3; i++) assertEquals(values.get(i), null, "nonexistent key value in list should be null"); // edge cases // all should through exceptions boolean didRaiseEx; didRaiseEx = false; try { String[] keys = null; provider.mget(keys).get(); } catch (IllegalArgumentException e) {didRaiseEx = true;} catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} if(!didRaiseEx){ fail ("Expected exception not raised."); } didRaiseEx = false; try { String[] keys = new String[0]; provider.mget(keys).get(); } catch (IllegalArgumentException e) {didRaiseEx = true;} catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} if(!didRaiseEx){ fail ("Expected exception not raised."); } didRaiseEx = false; try { String[] keys = new String[3]; keys[0] = stringList.get(0); keys[1] = null; keys[2] = stringList.get(2); provider.mget(keys).get(); } catch (IllegalArgumentException e) {didRaiseEx = true;} catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} if(!didRaiseEx){ fail ("Expected exception not raised."); } } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testDel() throws InterruptedException { cmd = Command.DEL.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String key = this.keys.get(0); provider.set (key, dataList.get(0)); Future existsResp1 = provider.exists(key); Future delResp = provider.del(key); Future existsResp2 = provider.exists(key); try { assertEquals(delResp.get().longValue(), 1, "one key should been deleted"); assertTrue (existsResp1.get(), "After set key should exist"); assertFalse (existsResp2.get(), "After del key should not exist"); // delete many keys provider.flushdb(); for(int i=0; i delCnt1 = provider.del(keysToDel); for(int i=0; i delCnt2 = provider.del(keysToDel); for(int i=0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testIncrAndDecr() throws InterruptedException { cmd = Command.INCR.code + " | " + Command.DECR.code; Log.log("TEST: %s command", cmd); try { String cntr_key = keys.get(0); provider.flushdb(); Future incrResp = null; for(int i = 0; i decrResp = null; for(int i = 0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testIncrbyAndDecrby() throws InterruptedException { cmd = Command.INCRBY.code + " |" + Command.DECRBY.code; Log.log("TEST: %s command", cmd); try { String cntr_key = keys.get(0); provider.flushdb(); Future incrbyResp = null; for(int i = 0; i decrbyResp = null; for(int i = 0; i " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testGetSetStringByteArray() throws InterruptedException { cmd = Command.SET.code + " | " + Command.GETSET.code + " byte[] "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(0), dataList.get(0)); Future getResp = provider.get(keys.get(0)); Future getsetResp1 = provider.getset(keys.get(0), dataList.get(1)); Future getsetResp2 = provider.getset(keys.get(1), dataList.get(2)); try { assertEquals(getResp.get(), dataList.get(0), "get results doesn't match the expected data"); assertEquals(getsetResp1.get(), dataList.get(0), "getset results doesn't match the expected data"); assertEquals(getsetResp2.get(), null, "getset result for new key should have been null"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testHsetHget() throws InterruptedException { cmd = Command.HSET.code + " | " + Command.HGET + " | " + Command.HEXISTS; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Future hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0)); Future hexistsResp1 = provider.hexists(keys.get(0), keys.get(1)); Future hexistsResp2 = provider.hexists(keys.get(0), keys.get(2)); Future hsetResp1_1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0)); Future hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0)); Future hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222); objectList.get(0).setName("Hash Stash"); Future hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0)); Future hgetResp1 = provider.hget(keys.get(0), keys.get(1)); Future hgetResp2 = provider.hget(keys.get(0), keys.get(2)); Future hgetResp3 = provider.hget(keys.get(0), keys.get(3)); Future hgetResp4 = provider.hget(keys.get(0), keys.get(4)); Future hlenResp1 = provider.hlen(keys.get(0)); Future hdelResp1 = provider.hdel(keys.get(0), keys.get(1)); Future hdelResp2 = provider.hdel(keys.get(0), keys.get(1)); Future hlenResp2 = provider.hlen(keys.get(0)); Future hlenResp3 = provider.hlen("some-random-key"); try { assertTrue (hsetResp1.get(), "hset using byte[] value"); assertTrue (hexistsResp1.get(), "hexists of field should be true"); assertTrue (!hexistsResp2.get(), "hexists of non existent field should be false"); assertTrue (!hsetResp1_1.get(), "second hset using byte[] value should return false"); assertTrue (hsetResp2.get(), "hset using String value"); assertTrue (hsetResp3.get(), "hset using Number value"); assertTrue (hsetResp4.get(), "hset using Object value"); assertEquals (hgetResp1.get(), dataList.get(0), "hget of field with byte[] value"); assertEquals (DefaultCodec.toStr(hgetResp2.get()), stringList.get(0), "hget of field with String value"); assertEquals (DefaultCodec.toLong(hgetResp3.get()).longValue(), 222, "hget of field with Number value"); TestBean objval = DefaultCodec.decode(hgetResp4.get()); assertEquals (objval.getName(), objectList.get(0).getName(), "hget of field with Object value"); assertTrue (hdelResp1.get(), "hdel of field should be true"); assertTrue (!hdelResp2.get(), "hdel of non-existent field should be false"); assertEquals (hlenResp1.get().longValue(), 4, "hlen of hash should be 4"); assertEquals (hlenResp2.get().longValue(), 3, "hlen of hash should be 3"); assertEquals (hlenResp3.get().longValue(), 0, "hlen of non-existant hash should be 0"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testHIncrBy() throws InterruptedException { cmd = Command.HSET.code + " | " + Command.HGET + " | " + Command.HINCRBY; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Future hsetResp1 = provider.hset(keys.get(0), keys.get(1), 41); Future hincrBy1 = provider.hincrby(keys.get(0), keys.get(1), 1); Future hget = provider.hget(keys.get(0), keys.get(1)); Future hincrBy2 = provider.hincrby(keys.get(0), keys.get(2), 4); try { assertTrue (hsetResp1.get(), "hset using byte[] value"); assertEquals (hincrBy1.get(), (Long)42l, "hexists of field should be true"); assertEquals(hget.get(), Convert.toBytes(42l)); assertEquals(hincrBy2.get(), (Long)4l); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testHkeys() throws InterruptedException { cmd = Command.HSET.code + " | " + Command.HKEYS; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Future hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0)); Future hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0)); Future hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222); objectList.get(0).setName("Hash Stash"); Future hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0)); // get keys Future> hkeysResp1 = provider.hkeys(keys.get(0)); // alright - empty the hash for(int i=1; i<5; i++) try { assertTrue(provider.hdel(keys.get(0), keys.get(i).getBytes()).get()); } catch (ExecutionException e) { Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } // get keys again Future> hkeysResp2 = provider.hkeys(keys.get(0)); // nil case Future> hkeysResp3 = provider.hkeys("no-such-hash"); try { assertTrue (hsetResp1.get(), "hset using byte[] value"); assertTrue (hsetResp2.get(), "hset using String value"); assertTrue (hsetResp3.get(), "hset using Number value"); assertTrue (hsetResp4.get(), "hset using Object value"); List hkeys = hkeysResp1.get(); assertEquals (hkeys.size(), 4, "keys list size should be 4"); assertEquals(hkeysResp2.get(), Collections.EMPTY_LIST, "result should be empty"); assertEquals(hkeysResp3.get(), Collections.EMPTY_LIST, "list of keys of non-existent hash should be empty"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testSetBitGetBit() throws InterruptedException { cmd = Command.SETBIT.code + " | " + Command.GETBIT; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Future setbit1 = provider.setbit(keys.get(0), 1, true); Future setbit32 = provider.setbit(keys.get(0), 32, true); Future getbit1 = provider.getbit(keys.get(0), 1); Future getbit2 = provider.getbit(keys.get(0), 2); Future getbit32 = provider.getbit(keys.get(0), 32); try { assertFalse (setbit1.get(), "original bit at 1"); assertFalse (setbit32.get(), "original bit at 32"); assertTrue (getbit1.get(), "getbit at 1"); assertFalse (getbit2.get(), "getbit at 2"); assertTrue (getbit32.get(), "getbit at 32"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testHvals() throws InterruptedException { cmd = Command.HSET.code + " | " + Command.HVALS; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Future hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0)); Future hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0)); Future hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222); objectList.get(0).setName("Hash Stash"); Future hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0)); // get values Future> hvalsResp1 = provider.hvals(keys.get(0)); // alright - empty the hash for(int i=1; i<5; i++) try { assertTrue(provider.hdel(keys.get(0), keys.get(i)).get()); } catch (ExecutionException e) { Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } // get values again // get values Future> hvalsResp2 = provider.hvals(keys.get(0)); // nil case Future> hvalsResp3 = provider.hvals("no-such-hash"); try { assertTrue (hsetResp1.get(), "hset using byte[] value"); assertTrue (hsetResp2.get(), "hset using String value"); assertTrue (hsetResp3.get(), "hset using Number value"); assertTrue (hsetResp4.get(), "hset using Object value"); List hvals = hvalsResp1.get(); assertEquals(hvals.size(), 4, "values list size should be 4"); assertEquals(hvalsResp2.get(), Collections.EMPTY_LIST, "values list size should be empty"); assertEquals(hvalsResp3.get(), Collections.EMPTY_LIST, "list of values of non-existent hash should be empty"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testHgetall() throws InterruptedException { cmd = Command.HSET.code + " | " + Command.HGETALL; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Future hsetResp1 = provider.hset(keys.get(0), keys.get(1), dataList.get(0)); Future hsetResp2 = provider.hset(keys.get(0), keys.get(2), stringList.get(0)); Future hsetResp3 = provider.hset(keys.get(0), keys.get(3), 222); objectList.get(0).setName("Hash Stash"); Future hsetResp4 = provider.hset(keys.get(0), keys.get(4), objectList.get(0)); // get keys Future> frHkeys = provider.hkeys(keys.get(0)); // get all Future> frHmap1 = provider.hgetall(keys.get(0)); // delete all keys for(int i =1; i<5; i++) { try { provider.hdel(keys.get(0), keys.get(i)).get(); } catch (ExecutionException e) { Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } // get all again Future> frHmap2 = provider.hgetall(keys.get(0)); // get all for non-existent hash Future> frHmap3 = provider.hgetall("no-such-hash"); try { assertTrue (hsetResp1.get(), "hset using byte[] value"); assertTrue (hsetResp2.get(), "hset using String value"); assertTrue (hsetResp3.get(), "hset using Number value"); assertTrue (hsetResp4.get(), "hset using Object value"); assertEquals( frHmap1.get().size(), 4, "hash map length"); assertEquals( frHkeys.get().size(), 4, "keys list length"); Map hmap = DefaultCodec.toDataDictionary(frHmap1.get()); assertEquals(hmap.get(keys.get(1)), dataList.get(0), "byte[] value mapping should correspond to prior HSET"); assertEquals(DefaultCodec.toStr(hmap.get(keys.get(2))), stringList.get(0), "String value mapping should correspond to prior HSET"); assertEquals(DefaultCodec.toLong(hmap.get(keys.get(3))).longValue(), 222, "Number value mapping should correspond to prior HSET"); assertEquals(DefaultCodec.decode(hmap.get(keys.get(4))), objectList.get(0), "Object value mapping should correspond to prior HSET"); Map hmap2 = DefaultCodec.toDataDictionary(frHmap2.get()); assertEquals(hmap2, Collections.EMPTY_MAP, "result should be empty"); Map hmap3 = DefaultCodec.toDataDictionary(frHmap3.get()); assertEquals(hmap3, Collections.EMPTY_MAP, "hgetall for non existent hash should be empty"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, byte[])}. * @throws InterruptedException */ @Test public void testSetStringByteArray() throws InterruptedException { cmd = Command.SET.code + " | " + Command.SETNX.code + " byte[] | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(keys.size()-1), emptyBytes); Future getResp = provider.get(keys.get(keys.size()-1)); Future setnxResp1 = provider.setnx(keys.get(1), dataList.get(1)); Future setnxResp2 = provider.setnx(keys.get(1), dataList.get(2)); try { assertEquals(getResp.get(), emptyBytes, "set and get results for empty byte[]"); assertTrue(setnxResp1.get()); assertFalse(setnxResp2.get()); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link JRedisFuture#append(String, String)} * @throws InterruptedException */ @Test public void testAppendStringString() throws InterruptedException { cmd = Command.APPEND.code + " | " + Command.GET.code + " String"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // append to a non-existent key // as of Redis 1.3.7 it behaves just like set but returns value len instead of status String key1 = keys.get(0); Future frLen0 = provider.append(key1, emptyString); Future frGet0 = provider.get(key1); Future frLen1 = provider.append(key1, stringList.get(0)); Future frGet1 = provider.get(key1); Future frLen2 = provider.append(key1, stringList.get(1)); Future frGet2 = provider.get(key1); provider.sadd(keys.get(1), stringList.get(0)); Future frExpectedError = provider.append(keys.get(1), stringList.get(0)); try { assertEquals(frLen0.get().longValue(), 0, "append of emtpy string to new key should be zero"); assertEquals(DefaultCodec.toStr(frGet0.get()), emptyString, "get results after append to new key for empty string"); assertEquals(frLen1.get().longValue(), stringList.get(0).length(), "append of emtpy string to new key should be zero"); assertEquals(DefaultCodec.toStr(frGet1.get()), stringList.get(0), "get results after append to new key for empty string"); assertEquals(frLen2.get().longValue(), stringList.get(0).length() + stringList.get(1).length(), "append of emtpy string to new key should be zero"); StringBuffer appendedString = new StringBuffer(); appendedString.append(stringList.get(0)); appendedString.append(stringList.get(1)); assertEquals(DefaultCodec.toStr(frGet2.get()), appendedString.toString(), "get results after append to new key for empty string"); } catch(ExecutionException e){ Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } // check see if we got the expected RedisException boolean expected = false; try { frExpectedError.get(); } catch(ExecutionException e){ Throwable cause = e.getCause(); if(cause instanceof RedisException) expected = true; else fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } assertTrue(expected, "expecting RedisException for append to a non-string key"); } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rename(java.lang.String, java.lang.String)}. * @throws InterruptedException */ @Test public void testRename() throws InterruptedException { cmd = Command.RENAME.code; Log.log("TEST: %s command", cmd); try { String newkey = null; byte[] value = dataList.get(0); key = getRandomAsciiString (random.nextInt(24)+2); newkey = getRandomAsciiString (random.nextInt(24)+2); Future flushResp = provider.flushdb(); Future setResp = provider.set (key, value); Future getOldResp = provider.get(key); Future reanmeResp = provider.rename (key, newkey); Future getNewResp = provider.get(newkey); try { flushResp.get(); setResp.get(); assertEquals(value, getOldResp.get()); reanmeResp.get(); assertEquals(value, getNewResp.get()); } catch(ExecutionException e){ // errors in response Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rename(java.lang.String, java.lang.String)}. * @throws InterruptedException */ @Test public void testRenamenx() throws InterruptedException { cmd = Command.RENAMENX.code; Log.log("TEST: %s command", cmd); try { // flush db and set a key provider.flushdb(); provider.set (keys.get(0), dataList.get(0)); // this should return true in future Future reanmenxResp1 = provider.renamenx (keys.get(0), keys.get(2)); // flush db again and set 2 keys provider.flushdb(); provider.set (keys.get(1), dataList.get(1)); provider.set (keys.get(2), dataList.get(2)); // this should return false in future Future reanmenxResp2 = provider.renamenx (keys.get(1), keys.get(2)); try { // note that we don't have to 'get' the future results for // commands we are not interested in. assertTrue (reanmenxResp1.get(), "1st renamenx should have been true"); assertFalse (reanmenxResp2.get(), "2nd renamenx should have been false"); } catch(ExecutionException e){ // errors in response Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Test settting a key in a flushed db, and then checking * exists, flushing again, and finally keys(). * Will invoke commands asynchronously, and after * @throws InterruptedException */ @Test public void testSetAndFlushdbAndExistsAndKeys() throws InterruptedException { cmd = Command.FLUSHDB.code + " | " + Command.SET.code + " | " + Command.EXISTS.code + " | " + Command.FLUSHDB.code + " | " + Command.KEYS.code; Log.log("TEST: %s commands", cmd); try { key = "woof"; Future flushResp = provider.flushdb(); Future setResp = provider.set(key, "meow"); Future existsResp = provider.exists(key); Future flush2Resp = provider.flushdb(); Future> keysResp = provider.keys(); try { flushResp.get(); // no need to check status; if error exception is raised setResp.get(); assertTrue(existsResp.get(), "key should exists at this point"); flush2Resp.get(); assertTrue(keysResp.get().size() == 0, "keys should have returned list of 0 items"); } catch(ExecutionException e){ // errors in response Throwable cause = e.getCause(); fail(cmd + " ERROR => " + cause.getLocalizedMessage(), e); } } catch (ClientRuntimeException e) { // errors in request time fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Tests to force RedisExceptions against various response types. * First we queue the requests and then we get the responses for all. * This tests both the primary goal and the correct behaviour of asynch * provider to queue the responses. * @throws InterruptedException */ @Test public void testElicitErrors() throws InterruptedException { Log.log("TEST: Elicit errors"); try { provider.flushdb(); String key = keys.get(0); try { provider.set(key, smallData).get(); } catch (ExecutionException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } // queue a few commands -- all are expected to result in ExecutionExcetion on Future.get() // Future fSaddResp = provider.sadd(key, dataList.get(0)); Future fScardResp = provider.scard(key); Future fLpopResp = provider.lpop(key); Future> fSmembersResp = provider.smembers(key); boolean expectedError; expectedError = false; try { Log.log("1 - Expecting an operation against key holding the wrong kind of value ERROR for SADD.."); fSaddResp.get(); } catch (ExecutionException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); expectedError = false; try { Log.log("2 - Expecting an operation against key holding the wrong kind of value ERROR for SCARD.."); fScardResp.get(); } catch (ExecutionException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); expectedError = false; try { Log.log("3 - Expecting an operation against key holding the wrong kind of value ERROR for LPOP .."); fLpopResp.get(); } catch (ExecutionException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); expectedError = false; try { Log.log("4 - Expecting an operation against key holding the wrong kind of value ERROR for SMEMBERS .."); fSmembersResp.get(); } catch (ExecutionException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); } catch (ClientRuntimeException e) { fail(cmd + " Runtime ERROR => " + e.getLocalizedMessage(), e); } } /** * Asynchronous responses must provide reference to {@link RedisException} * through the {@link ExecutionException#getCause()} per {@link Future} semantics. * @throws InterruptedException */ @Test public void testExecutionExceptionCauseType() throws InterruptedException { boolean expectedError; String key = keys.get(0); try { expectedError = false; try { Log.log("Expecting an operation against key holding the wrong kind of value ERROR.."); provider.set(key, smallData); // don't wait for response .. Future fBool = provider.sadd(key, dataList.get(0)); @SuppressWarnings("unused") boolean response = fBool.get(); // wait for response } catch (ExecutionException e) { expectedError = true; Throwable cause = e.getCause(); if(cause instanceof RedisException) Log.log("%s (as excepted)", cause); else fail("FAULT: the cause of ExecutionException was expected to be a RedisException"); } assertTrue(expectedError, "should have raised an exception but did not"); } finally { } } /** * Test {@link JRedisFuture#ping()} * @throws InterruptedException */ @Test public void testPing () throws InterruptedException { Future frStatus = null; cmd = Command.PING.code; Log.log("TEST: %s command", cmd); try { frStatus = provider.ping(); ResponseStatus status = frStatus.get(); assertTrue(!status.isError(), "ping return status"); } catch (ExecutionException e) { e.printStackTrace(); fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e); } } /** * Test {@link JRedisFuture#flushdb()} * @throws InterruptedException */ @Test public void testFlushDb () throws InterruptedException { Future frStatus = null; cmd = Command.FLUSHDB.code; Log.log("TEST: %s command", cmd); try { frStatus = provider.flushdb(); ResponseStatus status = frStatus.get(); assertTrue(!status.isError(), "flushdb return status"); } catch (ExecutionException e) { e.printStackTrace(); fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e); } } @Test public void testEcho() throws InterruptedException { Future echoResp = null; cmd = Command.ECHO.code; Log.log("TEST: %s command", cmd); try { echoResp = provider.echo(dataList.get(0)); assertEquals(dataList.get(0), echoResp.get(), "data and echo results"); byte[] zerolenData = new byte[0]; assertEquals(zerolenData, provider.echo(zerolenData).get(), "zero len byte[] and echo results"); boolean expected = false; try { provider.echo((byte[])null); } catch(IllegalArgumentException e) { expected = true; } assertTrue(expected, "expecting exception for null value to ECHO"); } catch (ExecutionException e) { e.printStackTrace(); fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e); } } @Test public void testBgrewriteaof() throws InterruptedException { Future cmdRespMsg = null; cmd = Command.BGREWRITEAOF.code; Log.log("TEST: %s command", cmd); try { cmdRespMsg = provider.bgrewriteaof(); assertTrue(cmdRespMsg.get() != null, "cmd response message should not be null"); } catch (ExecutionException e) { e.printStackTrace(); fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e); } } /** * Test {@link JRedisFuture#debug()} * @throws InterruptedException */ @Test public void testDebug () throws InterruptedException { Future frInfo = null; cmd = Command.DEBUG.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set("foo", "bar"); frInfo = provider.debug("foo"); ObjectInfo info = frInfo.get(); assertNotNull(info); Log.log("DEBUG of key => %s", info); } catch (ExecutionException e) { e.printStackTrace(); fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e); } } @Test public void testExpireat() throws InterruptedException { cmd = Command.EXPIREAT.code; Log.log("TEST: %s command", cmd); try { provider.flushdb().get(); String keyToExpire = "expire-me"; provider.set(keyToExpire, dataList.get(0)).get(); Log.log("TEST: %s with expire time 1000 msecs in future", Command.EXPIREAT); assertTrue(provider.expireat(keyToExpire, System.currentTimeMillis() + 2000).get(), "expireat for existing key should be true"); assertTrue (provider.exists(keyToExpire).get()); assertTrue(!provider.expireat("no-such-key", System.currentTimeMillis() + 500).get(), "expireat for non-existant key should be false"); // NOTE: IT SIMPLY WON'T WORK WITHOUT GIVING REDIS A CHANCE // could be network latency, or whatever, but the expire command is NOT // that precise, so we need to wait a bit longer Thread.sleep(5000); assertTrue (!provider.exists(keyToExpire).get(), "key should have expired by now"); } catch (ExecutionException e) { e.printStackTrace(); fail(cmd + " FAULT: " + e.getCause().getLocalizedMessage(), e); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisPipelineServiceTest.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.ri.alphazero; // //import static org.testng.Assert.fail; //import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; //import org.jredis.connector.ConnectionSpec; //import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; //import org.jredis.ri.alphazero.support.Log; //import org.testng.annotations.AfterTest; //import org.testng.annotations.Test; // ///** // * As of now, this class simply runs the same set of {@link JRedis} contract // * compliance using {@link JRedisService} as the provider. // * // * TODO: figure out a good way to meaningfully test service (e.g. concurrent // * and random method usage ..) // * // * @author Joubin Houshyar (alphazero@sensesay.net) // * @version alpha.0, Nov 6, 2009 // * @since alpha.0 // * // */ ////@Test(sequential = true, suiteName="JRedisPipelineService-tests") ////public class JRedisPipelineServiceTest extends JRedisProviderTestsBase { //public class JRedisPipelineServiceTest extends ConcurrentJRedisProviderTestsBase { // // // ------------------------------------------------------------------------ // // TEST SETUP // // ------------------------------------------------------------------------ // /* (non-Javadoc) // * @see org.jredis.ri.ProviderTestBase#newProviderInstance() // */ // @Override // protected JRedis newProviderInstance () { // JRedis provider = null; // try { // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(this.host, this.port, this.db2, this.password.getBytes()); // provider = new JRedisPipelineService(connectionSpec); // } // catch (ClientRuntimeException e) { // Log.error(e.getLocalizedMessage()); // } // return provider; // } // // ------------------------------------------------------------------------ // // The Tests // // ========================================================= JRedisClient // /** // * We define and run any additional, provider specific tests here. The // * basic generally applicable JRedis interface method test are defined // * in the super class. // * // * Here we test Quit in a post test method to insure all tests have been // * completed. // */ // // ------------------------------------------------------------------------ // // /** // * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#auth(java.lang.String)}. // */ // @AfterTest // public void testQuit() { // Log.log("TEST: QUIT command "); // try { // JRedis service = getProviderInstance(); // service.quit (); // } // catch (Exception e) { // fail("QUIT" + e); // } // } //} ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisPipelineTest.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.testng.Assert.fail; import org.jredis.ClientRuntimeException; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.AfterTest; import org.testng.annotations.Test; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 10, 2009 * @since alpha.0 * */ @Test(sequential = true, suiteName="JRedisPipeline-tests") public class JRedisPipelineTest extends JRedisFutureProviderTestsBase { // ------------------------------------------------------------------------ // JRedisPipelineTest specific Test Suite Parameters // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // TEST SETUP // ------------------------------------------------------------------------ /* (non-Javadoc) * @see org.jredis.ri.ProviderTestBase#newProviderInstance() */ @Override protected JRedisFuture newProviderInstance () { JRedisFuture provider = null; try { ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(this.host, this.port, this.db2, this.password.getBytes()); provider = new JRedisPipeline(connectionSpec); } catch (ClientRuntimeException e) { Log.error(e.getLocalizedMessage()); } return provider; } // ------------------------------------------------------------------------ // The Tests // ========================================================= JRedisClient /** * We define and run any additional, provider specific tests here. The * basic generally applicable JRedis interface method test are defined * in the super class. * * Here we test Quit in a post test method to insure all tests have been * completed. */ // ------------------------------------------------------------------------ /** * Pipeline quit. * We first ping and await the response to insure pipeline has processed * all pending responses, and then issue the quit command. */ @AfterTest public void testQuit() { try { JRedisFuture pipeline = getProviderInstance(); pipeline.ping().get(); pipeline.quit().get(); } catch (Exception e) { fail("QUIT" + e); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisProviderTestsBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero; import static org.jredis.ri.alphazero.support.DefaultCodec.decode; import static org.jredis.ri.alphazero.support.DefaultCodec.toLong; import static org.jredis.ri.alphazero.support.DefaultCodec.toStr; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.jredis.JRedis; import org.jredis.ObjectInfo; import org.jredis.Query; import org.jredis.RedisException; import org.jredis.RedisInfo; import org.jredis.RedisType; import org.jredis.ZSetEntry; import org.jredis.protocol.Command; import org.jredis.ri.JRedisTestSuiteBase; import org.jredis.ri.RI.Version; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; /** * This class is abstract and it is to remain abstract. * It provides the comprehensive set of tests of all {@link JRedis} methods. */ @Version(major=2, minor=0) public abstract class JRedisProviderTestsBase extends JRedisTestSuiteBase{ // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** JRedis Command being tested -- for log info */ private String cmd; // ------------------------------------------------------------------------ // The Tests // ========================================================= JRedis ======= /** * We define and run provider agnostic tests here. This means we run a set * of JRedis interface method tests that every connected JRedis implementation * should be able to support. * * The following commands are omitted: * 1 - QUIT: since we may be testing a multi-connection provider * 2 - SHUTDOWN: for the same reason as QUIT * 3 - MOVE and SELECT */ // ------------------------------------------------------------------------ /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#auth(java.lang.String)}. */ @Test public void testElicitErrors() { Log.log("TEST: Elicit errors"); try { provider.flushdb(); String key = keys.get(0); provider.set(key, smallData); boolean expectedError; // -- commands returning status response expectedError = false; try { Log.log("Expecting an operation against key holding the wrong kind of value ERROR.."); provider.sadd(key, dataList.get(0)); } catch (RedisException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); // -- commands returning value response expectedError = false; try { Log.log("Expecting an operation against key holding the wrong kind of value ERROR.."); provider.scard(key); } catch (RedisException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); // -- commands returning bulk response expectedError = false; try { Log.log("Expecting an operation against key holding the wrong kind of value ERROR.."); provider.lpop(key); } catch (RedisException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); // -- commands returning multi-bulk response expectedError = false; try { Log.log("Expecting an operation against key holding the wrong kind of value ERROR.."); provider.smembers(key); } catch (RedisException e) { expectedError = true; } assertTrue(expectedError, "should have raised an exception but did not"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } // /** // * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#auth(java.lang.String)}. // */ // @Test // public void testAuth() { // test = Command.AUTH.code; // Log.log("TEST: %s command", test); // try { // jredis.auth(password); // } // catch (RedisException e) { // fail(test + " with password: " + password, e); // } // } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#ping()}. */ @Test public void testPing() { cmd = Command.PING.code; Log.log("TEST: %s command", cmd); try { provider.ping(); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Tests: *

  • Test method for {@link org.jredis.ri.alphazero.JRedisSupport#exists(java.lang.String)}. *
  • Test method for {@link org.jredis.ri.alphazero.JRedisSupport#expire(java.lang.String, int)}. *
  • Test method for {@link org.jredis.ri.alphazero.JRedisSupport#ttl (java.lang.String)}. */ @Test public void testExists_Expire_TTL() { cmd = Command.EXISTS.code + " | " + Command.EXPIRE.code + " | " + Command.TTL.code; Log.log("TEST: %s command(s)", cmd); try { provider.flushdb(); assertTrue(provider.dbsize() == 0); String keyToExpire = "expire-me"; String keyToKeep = "keep-me"; provider.set(keyToKeep, "master"); provider.set(keyToExpire, System.currentTimeMillis()); assertTrue (provider.exists(keyToExpire)); Log.log("TEST: %s with expire time of %d", Command.EXPIRE, expire_secs); provider.expire(keyToExpire, expire_secs); assertTrue (provider.exists(keyToExpire)); assertTrue (provider.ttl(keyToExpire) > 0, "key to expire ttl is less than zero"); // NOTE: IT SIMPLY WON'T WORK WITHOUT GIVING REDIS A CHANCE // could be network latency, or whatever, but the expire command is NOT // that precise Thread.sleep(500); assertTrue (provider.exists(keyToExpire)); Thread.sleep(this.expire_wait_millisecs); assertFalse (provider.exists(keyToExpire)); // REVU-01202015: above exists for the expired key passes test // but the ttl on same (below) consistently fails. commenting out for now // so users do not skip tests in order to build the jars. // assertTrue (provider.ttl(keyToExpire) == -1, "expired key ttl is not -1"); assertTrue (provider.ttl(keyToKeep) == -1, "key to keep ttl is not -1"); } catch (RedisException e) { fail(cmd + " with password: " + password, e); } catch (InterruptedException e) { fail (cmd + "thread was interrupted and test did not conclude" + e.getLocalizedMessage()); } } @Test public void testExpireat() { cmd = Command.EXPIREAT.code; Log.log("TEST: %s command(s)", cmd); try { provider.flushdb(); assertTrue(provider.dbsize() == 0); String keyToExpire = "expire-me"; provider.set(keyToExpire, dataList.get(0)); assertTrue (provider.exists(keyToExpire)); Log.log("TEST: %s with expire time 1000 msecs in future", Command.EXPIREAT); assertTrue(provider.expireat(keyToExpire, System.currentTimeMillis() + 2000), "expireat for existing key should be true"); assertTrue (provider.exists(keyToExpire)); assertTrue(!provider.expireat("no-such-key", System.currentTimeMillis() + 500), "expireat for non-existant key should be false"); // NOTE: IT SIMPLY WON'T WORK WITHOUT GIVING REDIS A CHANCE // could be network latency, or whatever, but the expire command is NOT // that precise, so we need to wait a bit longer Thread.sleep(5000); assertTrue (!provider.exists(keyToExpire), "key should have expired by now"); } catch (RedisException e) { fail(cmd + " with password: " + password, e); } catch (InterruptedException e) { fail (cmd + "thread was interrupted and test did not conclude" + e.getLocalizedMessage()); } } // CANT test this without risking hosing the user's DBs // TODO: use a flag // /** // * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#flushall()}. // */ // @Test // public void testFlushall() { // fail("Not yet implemented"); // } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#flushdb()}. */ @Test public void testSetAndFlushdbAndExistsAndKeys() { cmd = Command.FLUSHDB.code + " | " + Command.SET.code + " | " + Command.EXISTS.code + " | " + Command.FLUSHDB.code + " | " + Command.KEYS.code; Log.log("TEST: %s commands", cmd); try { key = "woof"; provider.flushdb(); provider.set(key, "meow"); assertTrue (provider.exists(key)); provider.flushdb(); assertTrue(provider.keys().size()==0); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rename(java.lang.String, java.lang.String)}. */ @Test public void testRename() { cmd = Command.RENAME.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String newkey = null; byte[] value = dataList.get(0); key = getRandomAsciiString (random.nextInt(24)+2); newkey = getRandomAsciiString (random.nextInt(24)+2); provider.set (key, value); assertEquals(value, provider.get(key)); provider.rename (key, newkey); assertEquals(value, provider.get(newkey)); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testBitCommands() { cmd = Command.SETBIT.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); key = getRandomAsciiString (random.nextInt(24)+2); provider.del(key); provider.setbit(key, 0, true); provider.setbit(key, 32, true); assertEquals(true, provider.getbit(key,0)); assertEquals(true, provider.getbit(key,32)); assertEquals(false, provider.getbit(key,64)); assertEquals(false, provider.getbit(key,1)); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#renamenx(java.lang.String, java.lang.String)}. */ @Test public void testRenamenx() { cmd = Command.RENAMENX.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set (keys.get(0), dataList.get(0)); assertEquals(dataList.get(0), provider.get(keys.get(0))); // should work assertTrue(provider.renamenx (keys.get(0), keys.get(2))); assertEquals(dataList.get(0), provider.get(keys.get(2))); provider.flushdb(); // set key1 provider.set (keys.get(1), dataList.get(1)); assertEquals(dataList.get(1), provider.get(keys.get(1))); // set key2 provider.set (keys.get(2), dataList.get(2)); assertEquals(dataList.get(2), provider.get(keys.get(2))); // rename key1 to key 2 // should not assertFalse(provider.renamenx (keys.get(1), keys.get(2))); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#save()}. */ @Test public void testSaveAndLastSave() { cmd = Command.SAVE.code + " | " + Command.LASTSAVE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.save(); long when = provider.lastsave(); Thread.sleep (this.expire_wait_millisecs); provider.save(); long when2 = provider.lastsave(); assertTrue(when != when2); } catch (RedisException e) { if(e.getLocalizedMessage().indexOf("background save in progress") != -1){ Log.problem ("** NOTE ** Redis background save in progress prevented effective test of SAVE and LASTSAVE."); } else fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } catch (InterruptedException e) { fail ("thread was interrupted and test did not conclude" + e.getLocalizedMessage()); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#bgsave()}. */ @Test public void testBgsave() { cmd = Command.BGSAVE.code; Log.log("TEST: %s command", cmd); // final long start = System.nanoTime(); // final long until = start + TimeUnit.SECONDS.toNanos(10); // while(System.nanoTime() < until){ try { // if(!didflush) { provider.flushdb(); // didflush = true; // } // TODO: what's a meaningful test for this besides asserting command works? provider.bgsave(); } catch (RedisException e) { /* can fail due if server is in middle of AOF */ if(e.getMessage().contains("ERR Can't BGSAVE")){ try { Log.log("NOTE: Ignoring error <<%s>> finish with AOF during test for %s", e.getMessage(), cmd); Thread.sleep(1000L); Log.log(".. let's try again"); }catch (InterruptedException ie) { Log.log("sleep interrupted while waiting for Redis server to finish with AOF during test for %s", cmd); } } else { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } // } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#bgrewriteaof()}. */ @Test public void testBgrewriteaofe() { cmd = Command.BGREWRITEAOF.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // TODO: what's a meaningful test for this besides asserting command works? String msg = provider.bgrewriteaof(); assertTrue(msg != null, "expecting a non null response message - msg details may change so will not be checked here"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, byte[])}. */ @Test public void testSetStringByteArray() { cmd = Command.SET.code + " | " + Command.SETNX.code + " byte[] | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(keys.size()-1), emptyBytes); assertEquals(provider.get(keys.get(keys.size()-1)), emptyBytes, "set and get results for empty byte[]"); provider.set(keys.get(0), dataList.get(0)); assertEquals(dataList.get(0), provider.get(keys.get(0)), "data and get results"); assertTrue(provider.setnx(keys.get(1), dataList.get(1)), "set key"); assertNotNull(provider.get(keys.get(1))); assertFalse(provider.setnx(keys.get(1), dataList.get(2)), "key was already set"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#append(java.lang.String, byte[])}. */ @Test public void testAppendStringByteArray() { cmd = Command.SET.code + " | " + Command.APPEND.code + " byte[] | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // append to a non-existent key // as of Redis 1.3.7 it behaves just like set but returns value len instead of status String key1 = keys.get(0); long key1len = 0; key1len = provider.append(key1, emptyBytes); assertEquals(key1len, 0, "append of emtpy bytes to new key should be zero"); assertEquals(provider.get(key1), emptyBytes, "get results after append to new key for empty byte[]"); long len = 0; len = provider.append(key1, dataList.get(0)); assertEquals(len+key1len, dataList.get(0).length, "append results"); assertEquals(provider.get(key1), dataList.get(0), "get results after append"); key1len += len; len = provider.append(key1, dataList.get(1)); assertEquals(len, dataList.get(0).length + dataList.get(1).length, "append results"); byte[] appendedBytes = new byte[dataList.get(0).length + dataList.get(1).length]; System.arraycopy(dataList.get(0), 0, appendedBytes, 0, dataList.get(0).length); System.arraycopy(dataList.get(1), 0, appendedBytes, dataList.get(0).length, dataList.get(1).length); assertEquals(provider.get(key1), appendedBytes, "get results after 2nd append"); // raise errors boolean expected = false; try { String nonStringKey = keys.get(1); provider.sadd(nonStringKey, dataList.get(0)); provider.append(nonStringKey, dataList.get(3)); } catch(RedisException e) { expected = true; } assertTrue(expected, "expecting RedisException for append to a non-string key"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#append(java.lang.String, java.lang.String)}. */ @Test public void testAppendStringString() { cmd = Command.SET.code + " | " + Command.APPEND.code + " String | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // append to a non-existent key // as of Redis 1.3.7 it behaves just like set but returns value len instead of status String key1 = keys.get(0); long key1len = 0; long len = 0; // append empty string to non-existent key key1len = provider.append(key1, emptyString); assertEquals(key1len, 0, "append of emtpy string to new key should be zero"); assertEquals(DefaultCodec.toStr(provider.get(key1)), emptyString, "get results after append to new key for empty string"); // append a string len = provider.append(key1, stringList.get(0)); assertEquals(len+key1len, stringList.get(0).length(), "append results"); assertEquals(DefaultCodec.toStr(provider.get(key1)), stringList.get(0), "get results after append"); key1len += len; // append a string again len = provider.append(key1, stringList.get(1)); assertEquals(len, stringList.get(0).length() + stringList.get(1).length(), "append results"); StringBuffer appendedString = new StringBuffer(); appendedString.append(stringList.get(0)); appendedString.append(stringList.get(1)); assertEquals(DefaultCodec.toStr(provider.get(key1)), appendedString.toString(), "get results after 2nd append"); // raise RedisException boolean expected = false; try { String nonStringKey = keys.get(1); provider.sadd(nonStringKey, stringList.get(0)); provider.append(nonStringKey, stringList.get(3)); } catch(RedisException e) { expected = true; } assertTrue(expected, "expecting RedisException for append to a non-string key"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, java.lang.String)}. */ @Test public void testSetStringString() { cmd = Command.SET.code + " | " + Command.SETNX.code + " String | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(keys.size()-1), emptyString); assertEquals(toStr(provider.get(keys.get(keys.size()-1))), emptyString, "set and get results for empty String"); provider.set(keys.get(0), stringList.get(0)); assertEquals(stringList.get(0), toStr(provider.get(keys.get(0))), "string and get results"); assertTrue(provider.setnx(keys.get(1), stringList.get(1)), "set key"); assertNotNull(provider.get(keys.get(1))); assertFalse(provider.setnx(keys.get(1), stringList.get(2)), "key was already set"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, java.lang.Number)}. */ @Test public void testSetStringNumber() { cmd = Command.SET.code + " | " + Command.SETNX.code + " Long | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(0), longList.get(0)); assertTrue(longList.get(0).equals(toLong(provider.get(keys.get(0)))), "long and get results"); assertTrue(provider.setnx(keys.get(1), longList.get(1)), "set key"); assertNotNull(provider.get(keys.get(1))); assertFalse(provider.setnx(keys.get(1), longList.get(2)), "key was already set"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, java.io.Serializable)}. */ @Test public void testSetStringT() { cmd = Command.SET.code + " | " + Command.SETNX.code + " Java Object | " + Command.GET; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(0), objectList.get(0)); assertTrue(objectList.get(0).equals(decode(provider.get(keys.get(0)))), "object and get results"); assertTrue(provider.setnx(keys.get(1), objectList.get(1)), "set key"); assertNotNull(provider.get(keys.get(1))); assertFalse(provider.setnx(keys.get(1), objectList.get(2)), "key was already set"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testHsetHgetHexists() { cmd = Command.HSET.code + " | " + Command.HGET + " | " + Command.HEXISTS; Log.log("TEST: %s command", cmd); try { provider.flushdb(); assertTrue( provider.hset(keys.get(0), keys.get(1), dataList.get(0)), "hset using byte[] value"); assertTrue( provider.hexists(keys.get(0), keys.get(1)), "field should exist"); assertTrue( !provider.hexists(keys.get(0), keys.get(2)), "field should NOT exist"); assertTrue( !provider.hset(keys.get(0), keys.get(1), dataList.get(0)), "repeated hset using byte[] value should return false"); assertTrue( provider.hset(keys.get(0), keys.get(2), stringList.get(0)), "hset using String value"); assertTrue( provider.hset(keys.get(0), keys.get(3), 222), "hset using Number value"); objectList.get(0).setName("Hash Stash"); assertTrue( provider.hset(keys.get(0), keys.get(4), objectList.get(0)), "hset using Object value"); assertEquals( provider.hlen(keys.get(0)), 4, "hlen value"); assertEquals( provider.hlen("some-random-key"), 0, "hlen of non-existent hash should be zero"); assertEquals (provider.hget(keys.get(0), keys.get(1)), dataList.get(0), "hget of field with byte[] value"); assertEquals (DefaultCodec.toStr(provider.hget(keys.get(0), keys.get(2))), stringList.get(0), "hget of field with String value"); assertEquals (DefaultCodec.toLong(provider.hget(keys.get(0), keys.get(3))).longValue(), 222, "hget of field with Number value"); TestBean objval = DefaultCodec.decode(provider.hget(keys.get(0), keys.get(4))); assertEquals (objval.getName(), objectList.get(0).getName(), "hget of field with Object value"); assertTrue( provider.hdel(keys.get(0), keys.get(1)), "hdel of existing field should be true"); assertTrue( !provider.hdel(keys.get(0), keys.get(1)), "hdel of non-existing field should be false"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#hkeys(java.lang.String, java.io.Serializable)}. */ @Test public void testHkeys() { cmd = Command.HKEYS.code + " | " + Command.HSET + " | " + Command.HDEL; Log.log("TEST: %s command", cmd); try { provider.flushdb(); assertTrue( provider.hset(keys.get(0), keys.get(1), dataList.get(0)), "hset using byte[] value"); assertTrue( provider.hset(keys.get(0), keys.get(2), stringList.get(0)), "hset using String value"); assertTrue( provider.hset(keys.get(0), keys.get(3), 222), "hset using Number value"); objectList.get(0).setName("Hash Stash"); assertTrue( provider.hset(keys.get(0), keys.get(4), objectList.get(0)), "hset using Object value"); List hkeys = provider.hkeys(keys.get(0)); assertEquals( hkeys.size(), 4, "keys list length"); for(byte[] key : hkeys){ assertTrue(provider.hdel(keys.get(0), key), "deleting existing field should be true"); } assertEquals(provider.hlen(keys.get(0)), 0, "hash should empty"); List hkeys2 = provider.hkeys(keys.get(0)); assertEquals(hkeys2, Collections.EMPTY_LIST, "keys list should be empty"); List hkeys3 = provider.hkeys("no-such-hash"); assertEquals(hkeys3, Collections.EMPTY_LIST, "keys list of non-existent hash should be empty."); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#hvals(java.lang.String, java.io.Serializable)}. */ @Test public void testHvals() { cmd = Command.HVALS.code + " | " + Command.HSET + " | " + Command.HDEL; Log.log("TEST: %s command", cmd); try { provider.flushdb(); assertTrue( provider.hset(keys.get(0), keys.get(1), dataList.get(0)), "hset using byte[] value"); assertTrue( provider.hset(keys.get(0), keys.get(2), stringList.get(0)), "hset using String value"); assertTrue( provider.hset(keys.get(0), keys.get(3), 222), "hset using Number value"); objectList.get(0).setName("Hash Stash"); assertTrue( provider.hset(keys.get(0), keys.get(4), objectList.get(0)), "hset using Object value"); List hvals = provider.hvals(keys.get(0)); assertEquals( hvals.size(), 4, "value list length"); List hkeys = provider.hkeys(keys.get(0)); assertEquals( hkeys.size(), 4, "keys list length"); for(byte[] key : hkeys){ assertTrue(provider.hdel(keys.get(0), key), "deleting existing field should be true"); } assertEquals(provider.hlen(keys.get(0)), 0, "hash should empty"); List hvals2 = provider.hvals(keys.get(0)); assertEquals(hvals2, Collections.EMPTY_LIST, "keys list should be empty"); List hvals3 = provider.hvals("no-such-hash"); assertEquals(hvals3, Collections.EMPTY_LIST, "values list of non-existent hash should be empty."); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#hvals(java.lang.String, java.io.Serializable)}. */ @Test public void testHgetall() { cmd = Command.HGETALL.code + " | " + Command.HSET + " | " + Command.HDEL; Log.log("TEST: %s command", cmd); try { provider.flushdb(); assertTrue( provider.hset(keys.get(0), keys.get(1), dataList.get(0)), "hset using byte[] value"); assertTrue( provider.hset(keys.get(0), keys.get(2), stringList.get(0)), "hset using String value"); assertTrue( provider.hset(keys.get(0), keys.get(3), 222), "hset using Number value"); objectList.get(0).setName("Hash Stash"); assertTrue( provider.hset(keys.get(0), keys.get(4), objectList.get(0)), "hset using Object value"); Map hbinmap = provider.hgetall(keys.get(0)); assertEquals( hbinmap.size(), 4, "hash map length"); List hbinkeys = provider.hkeys(keys.get(0)); assertEquals( hbinkeys.size(), 4, "keys list length"); Map hmap = DefaultCodec.toDataDictionary(hbinmap); int i = 0; for(String key : DefaultCodec.toStr(hbinkeys)) { assertTrue(hmap.get(key) != null, String.format("key %d should exists in map and have a corresponding non null value", i++)); } assertEquals(hmap.get(keys.get(1)), dataList.get(0), "byte[] value mapping should correspond to prior HSET"); assertEquals(DefaultCodec.toStr(hmap.get(keys.get(2))), stringList.get(0), "String value mapping should correspond to prior HSET"); assertEquals(DefaultCodec.toLong(hmap.get(keys.get(3))).longValue(), 222, "Number value mapping should correspond to prior HSET"); assertEquals(DefaultCodec.decode(hmap.get(keys.get(4))), objectList.get(0), "Object value mapping should correspond to prior HSET"); for(byte[] key : hbinkeys) assertTrue(provider.hdel(keys.get(0), key), "deletion of existing key in hash should be true"); Map hmap2 = provider.hgetall(keys.get(0)); assertEquals(hmap2, Collections.EMPTY_MAP, "hash map should be empty"); Map hmap3 = provider.hgetall("no-such-hash"); assertEquals(hmap3, Collections.EMPTY_MAP, "hgetall for non existent hash should be empty"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, byte[])}. */ @Test public void testGetSetStringByteArray() { cmd = Command.SET.code + " | " + Command.GETSET.code + " byte[] "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(0), dataList.get(0)); assertEquals(dataList.get(0), provider.get(keys.get(0)), "data and get results"); assertEquals (provider.getset(keys.get(0), dataList.get(1)), dataList.get(0), "getset key"); assertEquals (provider.get(keys.get(1)), null, "non existent key should be null"); assertEquals (provider.getset(keys.get(1), dataList.get(1)), null, "getset on null key should be null"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, java.lang.String)}. */ // @Test // public void testGetSetStringString() { // test = Command.SET.code + " | " + Command.GETSET.code + " String "; // Log.log("TEST: %s command", test); // try { // jredis.flushdb(); // // jredis.set(keys.get(0), stringList.get(0)); // assertEquals(stringList.get(0), toStr(jredis.get(keys.get(0))), "string and get results"); // // assertTrue(jredis.setnx(keys.get(1), stringList.get(1)), "set key"); // assertNotNull(jredis.get(keys.get(1))); // assertFalse(jredis.setnx(keys.get(1), stringList.get(2)), "key was already set"); // } // catch (RedisException e) { fail(test + " ERROR => " + e.getLocalizedMessage(), e); } // } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, java.lang.Number)}. */ // @Test // public void testGetSetStringNumber() { // test = Command.SET.code + " | " + Command.GETSET.code + " Number "; // Log.log("TEST: %s command", test); // try { // jredis.flushdb(); // // jredis.set(keys.get(0), longList.get(0)); // assertTrue(longList.get(0).equals(toLong(jredis.get(keys.get(0)))), "long and get results"); // // assertTrue(jredis.setnx(keys.get(1), longList.get(1)), "set key"); // assertNotNull(jredis.get(keys.get(1))); // assertFalse(jredis.setnx(keys.get(1), longList.get(2)), "key was already set"); // } // catch (RedisException e) { fail(test + " ERROR => " + e.getLocalizedMessage(), e); } // } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#set(java.lang.String, java.io.Serializable)}. */ // @Test // public void testGetSetStringT() { // test = Command.SET.code + " | " + Command.GETSET.code + " Java Object "; // Log.log("TEST: %s command", test); // try { // jredis.flushdb(); // // jredis.set(keys.get(0), objectList.get(0)); // assertTrue(objectList.get(0).equals(decode(jredis.get(keys.get(0)))), "object and get results"); // // assertTrue(jredis.setnx(keys.get(1), objectList.get(1)), "set key"); // assertNotNull(jredis.get(keys.get(1))); // assertFalse(jredis.setnx(keys.get(1), objectList.get(2)), "key was already set"); // } // catch (RedisException e) { fail(test + " ERROR => " + e.getLocalizedMessage(), e); } // } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#incr(java.lang.String)}. */ @Test public void testIncrAndDecr() { cmd = Command.INCR.code + " | " + Command.DECR.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); long cntr = 0; String cntr_key = keys.get(0); for(int i = 1; i=0; i--){ cntr = provider.decr(cntr_key); assertEquals(i, cntr); } } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#incrby(java.lang.String, int)}. */ @Test public void testIncrbyAndDecrby() { cmd = Command.INCRBY.code + " |" + Command.DECRBY.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); long cntr = 0; String cntr_key = keys.get(0); for(long i = 1; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#del(java.lang.String)}. */ @Test public void testDel() { cmd = Command.DEL.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String key = this.keys.get(0); provider.set (key, dataList.get(0)); assertTrue (provider.exists(key)); long delCnt; delCnt = provider.del(key); assertFalse (provider.exists(key)); assertEquals(delCnt, 1, "one key was deleted"); // delete many keys provider.flushdb(); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#mget(java.lang.String, java.lang.String[])}. */ @Test public void testMget() { cmd = Command.MGET.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); for(int i=0; i values = null; values = provider.mget(keys.get(0)); assertEquals(values.size(), 1, "one value expected"); for(int i=0; i<1; i++) assertEquals(values.get(i), dataList.get(i)); values = provider.mget(keys.get(0), keys.get(1)); assertEquals(values.size(), 2, "2 values expected"); for(int i=0; i<2; i++) assertEquals(values.get(i), dataList.get(i)); values = provider.mget(keys.get(0), keys.get(1), keys.get(2)); assertEquals(values.size(), 3, "3 values expected"); for(int i=0; i<3; i++) assertEquals(values.get(i), dataList.get(i)); values = provider.mget("foo", "bar", "paz"); assertEquals(values.size(), 3, "3 values expected"); for(int i=0; i<3; i++) assertEquals(values.get(i), null, "nonexistent key value in list should be null"); // edge cases // all should through exceptions boolean didRaiseEx; didRaiseEx = false; try { String[] keys = null; provider.mget(keys); } catch (IllegalArgumentException e) {didRaiseEx = true;} catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} if(!didRaiseEx){ fail ("Expected exception not raised."); } didRaiseEx = false; try { String[] keys = new String[0]; provider.mget(keys); } catch (IllegalArgumentException e) {didRaiseEx = true;} catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} if(!didRaiseEx){ fail ("Expected exception not raised."); } didRaiseEx = false; try { String[] keys = new String[3]; keys[0] = stringList.get(0); keys[1] = null; keys[2] = stringList.get(2); provider.mget(keys); } catch (IllegalArgumentException e) {didRaiseEx = true;} catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} if(!didRaiseEx){ fail ("Expected exception not raised."); } } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /**************** LIST COMMANDS ******************************/ @Test public void testListPushWithSparseList() { cmd = Command.RPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } @Test public void testRpushStringByteArray() { cmd = Command.RPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); boolean expected = false; try { byte[] nil = null; provider.rpush("foo", nil); } catch(IllegalArgumentException e) { expected = true; } assertTrue(expected, "expecting exception for null value to RPUSH"); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lpush(java.lang.String, byte[])}. */ @Test public void testLpushStringByteArray() { cmd = Command.LPUSH.code + " byte[] | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); boolean expected = false; try { byte[] nil = null; provider.rpush("foo", nil); } catch(IllegalArgumentException e) { expected = true; } assertTrue(expected, "expecting exception for null value to RPUSH"); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after LPUSH is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } @Test public void testLpoppushStringString() { cmd = Command.RPOPLPUSH.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rpush(java.lang.String, java.lang.String)}. */ @Test public void testRpushStringString() { cmd = Command.RPUSH.code + " String | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i strRange = toStr(range); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lpush(java.lang.String, java.lang.String)}. */ @Test public void testLpushStringString() { cmd = Command.LPUSH.code + " String | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i strRange = toStr(range); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rpush(java.lang.String, java.lang.Number)}. */ @Test public void testRpushStringNumber() { cmd = Command.RPUSH.code + " Number | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i longRange = toLong(range); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lpush(java.lang.String, java.lang.Number)}. */ @Test public void testLpushStringNumber() { cmd = Command.LPUSH.code + " Number | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i longRange = toLong(range); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rpush(java.lang.String, java.io.Serializable)}. */ @Test public void testRpushStringT() { cmd = Command.RPUSH.code + " Java Object | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i objRange = decode(range); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lpush(java.lang.String, java.io.Serializable)}. */ @Test public void testLpushStringT() { cmd = Command.LPUSH.code + " Java Object | " + Command.LLEN + " | " + Command.LRANGE; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); for(int i=0; i range = provider.lrange(listkey, 0, SMALL_CNT); assertTrue(range.size()==SMALL_CNT, "range size after RPUSH is wrong"); for(int i=0; i objRange = decode(range); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#ltrim(java.lang.String, long, long)}. */ @Test public void testLtrim() { cmd = Command.LTRIM.code + " | " + Command.LLEN.code + " | " + Command.LRANGE.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lindex(java.lang.String, long)}. */ @Test public void testLindex() { cmd = Command.LINDEX.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lpop(java.lang.String)}. */ @Test public void testLpop() { cmd = Command.LPOP.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#rpop(java.lang.String)}. */ @Test public void testRpop() { cmd = Command.RPOP.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a small list String listkey = keys.get(0); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lrange(java.lang.String, int, int)}. */ @Test public void testLrange() { cmd = Command.LRANGE.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // prep a MEDIUM list String listkey = keys.get(0); for(int i=0; i items = provider.lrange(listkey, 0, SMALL_CNT-1); assertEquals (items.size(), SMALL_CNT, "list range 0->SMALL_CNT length should be SMALL_CNT"); for(int i=0; iCNT should be the same as nth dataitem, where n is " + i); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#substr(java.lang.String, int, int)}. */ @Test public void testSubstr() { cmd = Command.SUBSTR.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String key = keys.get(0); byte[] value = dataList.get(0); provider.set(key, value); byte[] substr = null; substr = provider.substr(key, 0, value.length); assertEquals(substr, value, "full range substr should be equal to value"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lrem(java.lang.String, byte[], int)}. */ @Test public void testLremStringByteArrayInt() { cmd = Command.LREM.code + " byte[] | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lrem(java.lang.String, java.lang.String, int)}. */ @Test public void testLremStringStringInt() { cmd = Command.LREM.code + " String | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lrem(java.lang.String, java.lang.Number, int)}. */ @Test public void testLremStringNumberInt() { cmd = Command.LREM.code + " Number | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lrem(java.lang.String, java.io.Serializable, int)}. */ @Test public void testLremStringTInt() { cmd = Command.LREM.code + " Java Object | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lset(java.lang.String, int, byte[])}. */ @Test public void testLsetStringIntByteArray() { cmd = Command.LSET.code + " byte[] | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i range = null; range = provider.lrange(listkey, 0, LARGE_CNT); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; ilim; i--) provider.lset(listkey, i, dataList.get(i*-1)); range = provider.lrange(listkey, 0, LARGE_CNT); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lset(java.lang.String, int, java.lang.String)}. */ @Test public void testLsetStringIntString() { cmd = Command.LSET.code + " String | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i range = null; range = toStr (provider.lrange(listkey, 0, LARGE_CNT)); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; ilim; i--) provider.lset(listkey, i, stringList.get(i*-1)); range = toStr (provider.lrange(listkey, 0, LARGE_CNT)); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lset(java.lang.String, int, java.lang.Number)}. */ @Test public void testLsetStringIntNumber() { cmd = Command.LSET.code + " Number | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i range = null; range = toLong (provider.lrange(listkey, 0, LARGE_CNT)); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; ilim; i--) provider.lset(listkey, i, longList.get(i*-1)); range = toLong (provider.lrange(listkey, 0, LARGE_CNT)); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#lset(java.lang.String, int, java.io.Serializable)}. */ @Test public void testLsetStringIntT() { cmd = Command.LSET.code + " Java Object | " + Command.LLEN; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String listkey = this.keys.get(0); // we'll make a list of unique items first for(int i=0; i range = null; range = decode (provider.lrange(listkey, 0, LARGE_CNT)); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; ilim; i--) provider.lset(listkey, i, objectList.get(i*-1)); range = decode (provider.lrange(listkey, 0, LARGE_CNT)); assertEquals (SMALL_CNT, range.size(), "range length is wrong"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /**************** QUERY COMMANDS ******************************/ /** * This command is still half-baked on the Redis side, so we just test to see if * it blows up or not. (cooking: if you sort on a set/list of size N and your * constrains (GET) limit the actual results to nothing, Redis (0.091) returns a * list of size N full of nulls. That's not tasty ..) * * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sort(java.lang.String)}. */ @Test public void testSort() { cmd = Command.SORT.code; Log.log("TEST: %s command", cmd); final String setkey = "set-key"; final String listkey = "list-key"; try { provider.flushdb(); for(int i=0; i sorted = null; Log.log("TEST: SORTED LIST [t.1]"); sorted = toStr(provider.sort(listkey).ALPHA().LIMIT(0, MEDIUM_CNT).DESC().exec()); assertEquals(sorted.size(), MEDIUM_CNT, "expecting sort results of size MEDIUM_CNT"); for(String s : sorted) System.out.format("[t.1]: %s\n", s); String destKey = String.format("%s_store", listkey); List ssres = provider.sort(listkey).ALPHA().LIMIT(0, MEDIUM_CNT).DESC().STORE(destKey).exec(); assertNotNull(ssres, "result of srot with STORE should be non-null"); assertEquals(ssres.size(), 1, "result of sort with STORE should be a list of single entry (the stored list's size)"); long sortedListSize = Query.Support.unpackValue(ssres); assertEquals(sortedListSize, MEDIUM_CNT); RedisType type = provider.type(destKey); assertEquals(type, RedisType.list, "dest key of SORT .. STORE should be a LIST"); long sslistSize = provider.llen(destKey); assertEquals(sslistSize, sortedListSize, "result of SORT ... STORE and LLEN of destkey list should be same"); Log.log("TEST: SORTED LIST [t.2]"); sorted = toStr(provider.sort(listkey).ALPHA().LIMIT(10, 9).DESC().exec()); assertEquals(sorted.size(), 9, "expecting sort results of size 9"); for(String s : sorted) System.out.format("[t.2]: %s\n", s); Log.log("TEST: SORTED LIST [t.3]"); sorted = toStr(provider.sort(listkey).ALPHA().LIMIT(MEDIUM_CNT-1, 1).DESC().exec()); assertEquals(sorted.size(), 1, "expecting sort results of size 1"); for(String s : sorted) System.out.format("[t.3]: %s\n", s); Log.log("TEST: SORTED SET "); // sorted = toStr(jredis.sort(setkey).ALPHA().LIMIT(0, 100).BY("*BB*").exec()); sorted = toStr(provider.sort(setkey).ALPHA().LIMIT(0, 555).DESC().exec()); for(String s : sorted) System.out.format("%s\n", s); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } // force errors // count can't be zero Runnable invalidLimitSpec = new Runnable() { public void run() { try { provider.sort(listkey).ALPHA().LIMIT(0, 0).DESC().exec(); } catch (Throwable t) { throw new RuntimeException ("", t); } } }; assertDidRaiseRuntimeError(invalidLimitSpec, RuntimeException.class); // LIMIT from must be positive, {0...n} Runnable invalidLimitSpec2 = new Runnable() { public void run() { try { provider.sort(listkey).ALPHA().LIMIT(-1, 1).DESC().exec(); } catch (Throwable t) { throw new RuntimeException ("", t); } } }; assertDidRaiseRuntimeError(invalidLimitSpec2, RuntimeException.class); } /**************** SORTED SET COMMANDS ******************************/ @Test public void testZaddStringByteArray() { cmd = Command.ZADD.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testZremStringByteArray() { cmd = Command.ZADD.code + " byte[] | " + Command.ZREM.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testZscoreStringByteArray() { cmd = Command.ZSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testZrankStringByteArray() { cmd = Command.ZRANK.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i<=SMALL_CNT; i++) assertTrue(provider.zadd(setkey, i, dataList.get(i)), "zadd of random element should be true"); for(int i=0;i<=SMALL_CNT; i++) assertEquals (provider.zrank(setkey, dataList.get(i)), i, "zrank of element"); // edge cases assertEquals (provider.zrank(setkey, dataList.get(SMALL_CNT+1)), -1, "zrank against non-existent member should be -1"); assertEquals (provider.zrank("no-such-set", dataList.get(0)), -1, "zrank against non-existent key should be -1"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrevrankStringByteArray() { cmd = Command.ZREVRANK.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i<=SMALL_CNT; i++) assertTrue(provider.zadd(setkey, i, dataList.get(i)), "zadd of random element should be true"); for(int i=0;i<=SMALL_CNT; i++) assertEquals (provider.zrevrank(setkey, dataList.get(i)), SMALL_CNT - i, "zrevrank of element"); // edge cases assertEquals (provider.zrevrank(setkey, dataList.get(SMALL_CNT+1)), -1, "zrevrank against non-existent member should be -1"); assertEquals (provider.zrevrank("no-such-set", dataList.get(0)), -1, "zrevrank against non-existent key should be -1"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrangeWithscoresStringByteArray() { cmd = Command.ZRANGE$OPTS.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i zvalues = provider.zrange(setkey, 0, SMALL_CNT); List zsubset = provider.zrangeSubset(setkey, 0, SMALL_CNT); for(int i=0;i0) assertTrue(zsubset.get(i).getScore() >= zsubset.get(i-1).getScore(), "range member score should be bigger or equal to previous range member. idx: " + i); } } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrevrangeWithscoresStringByteArray() { cmd = Command.ZREVRANGE$OPTS.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i zvalues = provider.zrevrange(setkey, 0, SMALL_CNT); List zsubset = provider.zrevrangeSubset(setkey, 0, SMALL_CNT); for(int i=0;i= zsubset.get(i+1).getScore(), "range member score should be smaller or equal to previous range member. idx: " + i); if(i>0) assertTrue(zsubset.get(i).getScore() <= zsubset.get(i-1).getScore(), "range member score should be bigger or equal to previous range member. idx: " + i); } } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZrangebyscoreStringByteArray() { cmd = Command.ZRANGEBYSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i range = provider.zrangebyscore(setkey, 0, SMALL_CNT); assertTrue(range.size() > 0, "should have non empty results for range by score here"); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testZrangebyscoreWithScoresStringByteArray() { cmd = Command.ZRANGEBYSCORE$OPTS.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i range = provider.zrangebyscoreSubset(setkey, 0, SMALL_CNT); assertTrue(range.size() > 0, "should have non empty results for range by score here"); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testZremrangebyscoreStringByteArray() { cmd = Command.ZREMRANGEBYSCORE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i 0, "should have non-zero number of rem cnt for zcount"); assertEquals(count, SMALL_CNT+1, "should have specific number of rem cnt for zcount"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZcountStringByteArray() { cmd = Command.ZCOUNT.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i 0, "should have non-zero number of rem cnt for zremrangebyscore"); assertEquals(remCnt, SMALL_CNT+1, "should have specific number of rem cnt for zremrangebyscore"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } @Test public void testZremrangebyrankStringByteArray() { cmd = Command.ZREMRANGEBYRANK.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i 0, "should have non-zero number of rem cnt for zremrangebyrank"); assertEquals(remCnt, SMALL_CNT+1, "should have specific number of rem cnt for zremrangebyrank"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } catch (RuntimeException rte) { fail(cmd + " RUNTIME-ERROR => " + rte.getLocalizedMessage(), rte); } } @Test public void testZincrbyStringByteArray() { cmd = Command.ZSCORE.code + " byte[] | " + Command.ZINCRBY.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testZrangeStringByteArray() { cmd = Command.ZRANGE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i range = provider.zrange(setkey, 0, SMALL_CNT); for(int i=1;i= provider.zscore(setkey, range.get(i-1)).doubleValue(), "range member score should be bigger or equal to previous range member"); } for(int i=0;i " + e.getLocalizedMessage(), e); } } /**************** SET COMMANDS ******************************/ /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sadd(java.lang.String, byte[])}. */ @Test public void testSaddStringByteArray() { cmd = Command.SADD.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sadd(java.lang.String, java.lang.String)}. */ @Test public void testSaddStringString() { cmd = Command.SADD.code + " String"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sadd(java.lang.String, java.lang.Number)}. */ @Test public void testSaddStringNumber() { cmd = Command.SADD.code + " Number"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sadd(java.lang.String, java.io.Serializable)}. */ @Test public void testSaddStringT() { cmd = Command.SADD.code + " Java Object"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } @Test public void testSrandmember() { cmd = Command.SRANDMEMBER.code + " String "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); // add a small set String setkey = keys.get(0); for(int i=0;i members = null; members = toStr(provider.smembers(setkey)); assertTrue(members.size() == SMALL_CNT); // get random member String randomMemeber = toStr(provider.srandmember(setkey)); boolean found = false; for(String m : members){ if(m.equals(randomMemeber)) { found = true; break; } } assertTrue(found, "random set element should have been in the members list"); // test edget conditions byte[] membytes = null; // empty set provider.sadd("empty", "delete-me"); provider.srem("empty", "delete-me"); membytes = provider.srandmember("empty"); assertEquals(membytes, null, "empty set random member should be null"); assertEquals(toStr(membytes), null, "empty set random member should be null"); // non-existent key membytes = provider.srandmember("no-such-key"); assertEquals(membytes, null, "non-existent key/set random member should be null"); assertEquals(toStr(membytes), null, "non-existent key/set random member should be null"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#smembers(java.lang.String)}. */ @Test public void testSmembers() { cmd = Command.SMEMBERS.code + " byte[] "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i members = null; members = provider.smembers(setkey); assertTrue(members.size() == SMALL_CNT); // byte[] don't play nice with equals -- values are random so if size matches, its ok // for(int i=0;i " + e.getLocalizedMessage(), e); } cmd = Command.SMEMBERS.code + " String "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i members = null; members = toStr(provider.smembers(setkey)); assertTrue(members.size() == SMALL_CNT); for(int i=0;i " + e.getLocalizedMessage(), e); } cmd = Command.SMEMBERS.code + " Number "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i members = null; members = toLong (provider.smembers(setkey)); assertTrue(members.size() == SMALL_CNT); for(int i=0;i " + e.getLocalizedMessage(), e); } cmd = Command.SMEMBERS.code + " Java Object "; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i members = null; members = decode (provider.smembers(setkey)); assertTrue(members.size() == SMALL_CNT); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sismember(java.lang.String, byte[])}. */ @Test public void testSmoveStringByteArray() { cmd = Command.SMOVE.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String srckey = keys.get(0); String destkey = keys.get(1); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sismember(java.lang.String, byte[])}. */ @Test public void testSismemberStringByteArray() { cmd = Command.SISMEMBER.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sismember(java.lang.String, java.lang.String)}. */ @Test public void testSismemberStringString() { cmd = Command.SISMEMBER.code + " String"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sismember(java.lang.String, java.lang.Number)}. */ @Test public void testSismemberStringNumber() { cmd = Command.SISMEMBER.code + " Number"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sismember(java.lang.String, java.io.Serializable)}. */ @Test public void testSismemberStringT() { cmd = Command.SISMEMBER.code + " Java Object"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#scard(java.lang.String)}. */ @Test public void testScard() { cmd = Command.SCARD.code + " Java Object"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#scard(java.lang.String)}. */ @Test public void testZcard() { cmd = Command.ZADD.code + " Java Object | " + Command.ZCARD.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sinter(java.lang.String, java.lang.String[])}. */ @Test public void testSinter() { cmd = Command.SINTER.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey1 = keys.get(0); String setkey2 = keys.get(1); String setunique = keys.get(2); for(int i=0;i 0, "should be common elements in set 1 and 2"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sinterstore(java.lang.String, java.lang.String[])}. */ @Test public void testSinterstore() { cmd = Command.SINTERSTORE.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey1 = keys.get(0); String setkey2 = keys.get(1); String setunique = keys.get(2); String interset = keys.get(3); for(int i=0;i 0, "interset set should be non-empty"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sunion(java.lang.String, java.lang.String[])}. */ @Test public void testSunion() { cmd = Command.SUNION.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey1 = keys.get(0); String setkey2 = keys.get(1); String setunique = keys.get(2); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sunionstore(java.lang.String, java.lang.String[])}. */ @Test public void testSunionstore() { cmd = Command.SUNIONSTORE.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey1 = keys.get(0); String setkey2 = keys.get(1); String setunique = keys.get(2); String union = keys.get(3); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sdiff(java.lang.String, java.lang.String[])}. */ @Test public void testSdiff() { cmd = Command.SDIFF.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey1 = keys.get(0); String setkey2 = keys.get(1); String setkey3 = keys.get(2); String setexpectedkey = keys.get(3); // // - per the redis doc -- // note that basically, SDIFF k, k1, ..., kn is a diff between k and union (k1, .., kn) // provider.sadd(setkey1, "x"); provider.sadd(setkey1, "a"); provider.sadd(setkey1, "b"); provider.sadd(setkey1, "c"); provider.sadd(setkey2, "c"); provider.sadd(setkey3, "a"); provider.sadd(setkey3, "d"); provider.sadd(setexpectedkey, "x"); provider.sadd(setexpectedkey, "b"); List sdiffResults = DefaultCodec.toStr(provider.sdiff(setkey1, setkey2, setkey3)); assertEquals(provider.scard(setexpectedkey), sdiffResults.size(), "sdiff result and expected set should have same cardinality"); for(String s : sdiffResults) assertTrue(provider.sismember(setexpectedkey, s), s + " should be a member of the expected result set"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#sdiff(java.lang.String, java.lang.String[])}. */ @Test public void testSdiffstore() { cmd = Command.SDIFFSTORE.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey1 = keys.get(0); String setkey2 = keys.get(1); String setkey3 = keys.get(2); String setdiffreskey = keys.get(3); // // - per the redis doc -- // note that basically, SDIFF k, k1, ..., kn is a diff between k and union (k1, .., kn) // provider.sadd(setkey1, "x"); provider.sadd(setkey1, "a"); provider.sadd(setkey1, "b"); provider.sadd(setkey1, "c"); provider.sadd(setkey2, "c"); provider.sadd(setkey3, "a"); provider.sadd(setkey3, "d"); provider.sdiffstore (setdiffreskey, setkey1, setkey2, setkey3); assertEquals(provider.scard(setdiffreskey), provider.sdiff(setkey1, setkey2, setkey3).size(), "sdiff result and sdiffstore dest set should have same cardinality"); assertTrue(provider.sismember(setdiffreskey, "x"), "x should be a member of the expected result set"); assertTrue(provider.sismember(setdiffreskey, "b"), "b should be a member of the expected result set"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#srem(java.lang.String, byte[])}. */ @Test public void testSremStringByteArray() { cmd = Command.SISMEMBER.code + " byte[]"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#srem(java.lang.String, java.lang.String)}. */ @Test public void testSremStringString() { cmd = Command.SISMEMBER.code + " String"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#srem(java.lang.String, java.lang.Number)}. */ @Test public void testSremStringNumber() { cmd = Command.SISMEMBER.code + " Number"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#srem(java.lang.String, java.io.Serializable)}. */ @Test public void testSremStringT() { cmd = Command.SISMEMBER.code + " Java Object"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); String setkey = keys.get(0); for(int i=0;i " + e.getLocalizedMessage(), e); } } /************************ DB COMMANDS ***********************/ /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#dbsize()}. */ @Test public void testDbsize() { cmd = Command.DBSIZE.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.flushdb(); assertTrue (provider.dbsize() == 0); for (int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#randomkey()}. */ @Test public void testRandomkey() { cmd = Command.RANDOMKEY.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); assertTrue(provider.dbsize() == 0); // String iamempty = provider.randomkey(); byte[] iamempty = provider.randomkey(); assertNull(iamempty, "randomkey of an empty db should be null, but instead it was: " + iamempty); for (int i=0; i " + e.getLocalizedMessage(), e); } } // /** // * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#move(java.lang.String, int)}. // */ // @Test // public void testMove() { // test = Command.MOVE.code ; // Log.log("TEST: %s command", test); // try { // jredis.flushdb(); // assertTrue (jredis.dbsize() == 0, "db1 should be empty"); // // jredis.select(db2).flushdb(); // assertTrue (jredis.dbsize() == 0, "db2 should be empty"); // // jredis.set(keys.get(0), dataList.get(0)); // assertTrue (jredis.dbsize() == 1, "db2 should have 1 key at this point"); // // jredis.move(keys.get(0), db1); // assertTrue (jredis.dbsize() == 0, "db2 should be empty again"); // jredis.select(db1); // assertTrue (jredis.dbsize() == 1, "db1 should have 1 key at this point"); // // } // catch (RedisException e) { fail(test + " ERROR => " + e.getLocalizedMessage(), e); } // } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#type(java.lang.String)}. */ @Test public void testType() { cmd = Command.TYPE.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set(keys.get(0), dataList.get(0)); provider.sadd(keys.get(1), dataList.get(1)); provider.rpush(keys.get(2), dataList.get(2)); assertTrue(provider.type(keys.get(0))==RedisType.string, "type should be string"); assertTrue(provider.type(keys.get(1))==RedisType.set, "type should be set"); assertTrue(provider.type(keys.get(2))==RedisType.list, "type should be list"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#debug()}. */ @Test public void testDebug() { cmd = Command.DEBUG.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); provider.set("foo", "bar"); ObjectInfo info = provider.debug("foo"); assertNotNull(info); Log.log("DEBUG of key => %s", info); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#info()}. */ @Test public void testInfo() { cmd = Command.INFO.code ; Log.log("TEST: %s command", cmd); try { provider.flushdb(); Map infoMap = provider.info(); for (RedisInfo info : RedisInfo.values()){ if(infoMap.get(info.name()) == null){ Log.problem("Note that expected INFO entry %s is apparently deprecated - IGNORING", info); } else { Log.log("%s => %s", info.name(), infoMap.get(info.name())); } } } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#keys()}. */ @Test public void testKeys() { cmd = Command.KEYS.code + " (*)"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); for (int i=0; i redisBinkeys = provider.keys(); List rediskeys = new ArrayList(redisBinkeys.size()); for(byte[] bk : redisBinkeys) rediskeys.add(new String(bk)); assertEquals(SMALL_CNT, rediskeys.size(), "size of key list should be SMALL_CNT"); for(int i=0; i " + e.getLocalizedMessage(), e); } } /** * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#keys(java.lang.String)}. */ @Test public void testKeysString() { cmd = Command.KEYS.code + " (using patterns)"; Log.log("TEST: %s command", cmd); try { provider.flushdb(); for (int i=0; i redisBinkeys = provider.keys("*"+patternA+"*"); List rediskeys = new ArrayList(redisBinkeys.size()); for(byte[] bk : redisBinkeys) rediskeys.add(new String(bk)); assertEquals(SMALL_CNT, rediskeys.size(), "size of key list should be SMALL_CNT"); for(int i=0; i " + e.getLocalizedMessage(), e); } } @Test public void testEcho() { cmd = Command.ECHO.code; Log.log("TEST: %s command", cmd); try { provider.flushdb(); assertEquals(dataList.get(0), provider.echo(dataList.get(0)), "data and echo results"); byte[] zerolenData = new byte[0]; assertEquals(zerolenData, provider.echo(zerolenData), "zero len byte[] and echo results"); boolean expected = false; try { provider.echo((byte[])null); } catch(IllegalArgumentException e) { expected = true; } assertTrue(expected, "expecting exception for null value to ECHO"); } catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } } // /** // * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#shutdown()}. // */ // @Test // public void testShutdown() { // fail("Not yet implemented"); // } // // ======================================================================== // Test MULTI/EXEC/DISCARD *BASICS* // ======================================================================== /** * Test the basics of multi/exec/discard * TODO: requires supports() in JRedis. */ // @Test // public void testMultiDiscardBasics() { // cmd = Command.MULTI + " | " + Command.DISCARD + " | basics"; // Log.log("TEST: %s command", cmd); // // try { // provider.flushdb(); // } // catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } // // try { // provider.multi(); // provider.discard(); // } // catch (RedisException e) { fail(cmd + " ERROR => " + e.getLocalizedMessage(), e); } // // boolean didRaiseEx; // didRaiseEx = false; // try { // provider.discard(); // } // catch (IllegalArgumentException e) {didRaiseEx = true;} // catch (Throwable whatsthis) { fail ("unexpected exception raised", whatsthis);} // if(!didRaiseEx){ fail ("Expected exception not raised."); } // } // ======================================================================== // Test Properties // ======================================================================== /** the JRedis implementation being tested */ // private JRedis provider = null; // ------------------------------------------------------------------------ // JRedis Provider initialize methods // ------------------------------------------------------------------------ // /** // * Sets the {@link JRedis} implementation provider for the test suite // */ // @BeforeTest // public void setJRedisProvider () { // try { // JRedis jredis = newJRedisProviderInstance(); // // setJRedisProviderInstance(jredis); // prepTestDBs(); // // Log.log("JRedisClientNGTest.setJRedisProvider - done"); // } // catch (ClientRuntimeException e) { // Log.error(e.getLocalizedMessage()); // } // } // // /** // * Extension point: Tests for specific implementations of {@link JRedis} // * implement this method to create the provider instance. // * @return {@link JRedis} implementation instance // */ // protected abstract JRedis newJRedisProviderInstance () ; // // /** // * Must be called by a BeforeTest method to set the jredis parameter. // * @param jredisProvider that is being tested. // */ // protected final void setJRedisProviderInstance (JRedis jredisProvider) { // this.jredis = jredisProvider; // Log.log( "TEST: " + // "\n\t-----------------------------------------------\n" + // "\tProvider Class: %s" + // "\n\t-----------------------------------------------\n", // jredisProvider.getClass().getCanonicalName()); // } // /** // * @return the {@link JRedis} instance used for the provider tests // */ // protected final JRedis getJRedisProviderInstance() { // return jredis; // } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/JRedisServiceTest.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.ri.alphazero; // //import static org.testng.Assert.fail; //import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; //import org.jredis.connector.ConnectionSpec; //import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; //import org.jredis.ri.alphazero.support.Log; //import org.testng.annotations.AfterTest; //import org.testng.annotations.BeforeSuite; //import org.testng.annotations.Parameters; //import org.testng.annotations.Test; // ///** // * As of now, this class simply runs the same set of {@link JRedis} contract // * compliance using {@link JRedisService} as the provider. // * // * TODO: figure out a good way to meaningfully test service (e.g. concurrent // * and random method usage ..) // * // * @author Joubin Houshyar (alphazero@sensesay.net) // * @version alpha.0, Oct 9, 2009 // * @since alpha.0 // * // */ //@Test(sequential = true, suiteName="JRedisService-tests") ////public class JRedisServiceTest extends JRedisProviderTestsBase { //public class JRedisServiceTest extends ConcurrentJRedisProviderTestsBase { // // // ------------------------------------------------------------------------ // // JRedisService specific Test Suite Parameters with default values // // ------------------------------------------------------------------------ // protected int connectionCnt = 1; // // // ------------------------------------------------------------------------ // // TEST SETUP // // ------------------------------------------------------------------------ // /** // * {@link JRedisService} test suite requires the additional params. // * @param connectionCount // */ // @Parameters({ // "jredis.service.connection.cnt" // }) // @BeforeSuite // public void serviceSuiteParametersInit( // int connectionCount // ) // { // this.connectionCnt = connectionCount; // Log.log("JRedisServiceTest: Using %d connections", connectionCount); // Log.log("JRedisService Suite parameters initialized "); // } // // /* (non-Javadoc) // * @see org.jredis.ri.alphazero.JRedisProviderTestNGBase#newJRedisProviderInstance() // */ // protected JRedis newProviderInstance () { // JRedis provider = null; // try { // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(this.host, this.port, this.db2, this.password.getBytes()); // provider = new JRedisService(connectionSpec, this.connectionCnt); // } // catch (ClientRuntimeException e) { // Log.error(e.getLocalizedMessage()); // } // return provider; // } // // // ------------------------------------------------------------------------ // // The Tests // // ========================================================= JRedisClient // /** // * We define and run any additional, provider specific tests here. The // * basic generally applicable JRedis interface method test are defined // * in the super class. // * // * Here we test Quit in a post test method to insure all tests have been // * completed. // */ // // ------------------------------------------------------------------------ // /** // * Test method for {@link org.jredis.ri.alphazero.JRedisSupport#auth(java.lang.String)}. // */ // @AfterTest // public void testQuit() { // Log.log("TEST: QUIT command -- WARNING: using quit with JRedisService should not be allowed!"); // try { // JRedis service = getProviderInstance(); // service.quit (); // } // catch (Exception e) { // fail("QUIT" + e); // } // } //} ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/support/ConvertTest.java ================================================ /* * Copyright (c) 2009, Joubin Houshyar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of JRedis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package org.jredis.ri.alphazero.support; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * @author Joubin Houshyar (alphazero@sensesay.net) * */ @Test(suiteName="support-tests") public class ConvertTest { @Test public void testGetNaturalNumber() { Log.log("Testing bytes to number conversion ..."); byte[] data = null; // test null boolean inputChecking; inputChecking = false; try { Convert.toInt(data); } catch (IllegalArgumentException e){ inputChecking = true;} finally { assertTrue(inputChecking);} // test garbage data inputChecking = false; data = "2be?".getBytes(); try { Convert.toInt(data); } catch (IllegalArgumentException e){ inputChecking = true;} finally { assertTrue(inputChecking);} // test big data // this is bigger than an bit inputChecking = false; data = "1234567890123456".getBytes(); try { Convert.toInt(data); } catch (IllegalArgumentException e){ inputChecking = true; } finally { assertTrue(inputChecking);} { // test 0 data = "0".getBytes(); int value = Convert.toInt(data); assertEquals(0, value); // test -0 data = "-0".getBytes(); // test +0 data = "+0".getBytes(); assertEquals(0, Convert.toInt(data)); // test -00000 data = "-0000".getBytes(); assertEquals(0, Convert.toInt(data)); // test 00000 data = "0000".getBytes(); // test +00000 data = "+0000".getBytes(); assertEquals(0, Convert.toInt(data)); // test 00001 data = "00001".getBytes(); assertEquals(1, Convert.toInt(data)); // test 0000100 data = "0000100".getBytes(); assertEquals(100, Convert.toInt(data)); // test 00001 data = "+00001".getBytes(); assertEquals(1, Convert.toInt(data)); // test +0000100 data = "+0000100".getBytes(); assertEquals(100, Convert.toInt(data)); // test 00001 data = "-00001".getBytes(); assertEquals(-1, Convert.toInt(data)); // test +0000100 data = "-0000100".getBytes(); assertEquals(-100, Convert.toInt(data)); // test 1 data = "1".getBytes(); assertEquals(1, Convert.toInt(data)); // test -1 data = "-1".getBytes(); assertEquals(-1, Convert.toInt(data)); // test +1 data = "+1".getBytes(); assertEquals(1, Convert.toInt(data)); // test $+1 data = "$+1".getBytes(); assertEquals(1, Convert.toInt(data, 1, data.length-1)); // test $-1 data = "$-1".getBytes(); assertEquals(-1, Convert.toInt(data, 1, data.length-1)); // test $+1GARBAGEDATA data = "$+1GARBAGEDATA".getBytes(); assertEquals(1, Convert.toInt(data, 1, 2)); // test $-1GARAGEDATA data = "$-1GARAGEDATA".getBytes(); assertEquals(-1, Convert.toInt(data, 1, 2)); // do a sensible range int java ; for(int i=-50000; i<50000; i++) { data = Convert.toBytes(i); java = Integer.parseInt(new String(data), 10); assertEquals (java, Convert.toInt(data)); } // now lets go to the limit // data = Integer.toString(Integer.MAX_VALUE).getBytes(); assertEquals(Integer.MAX_VALUE, Convert.toInt(data, 0, data.length)); data = Integer.toString(Integer.MIN_VALUE).getBytes(); assertEquals(Integer.MIN_VALUE, Convert.toInt(data, 0, data.length)); } // now lets do the longs { // test big data // this is bigger than an bit but fine for a long inputChecking = false; data = "1234567890123456".getBytes(); assertEquals (1234567890123456L, Convert.toLong(data)); // test 0 data = "0".getBytes(); long value = Convert.toLong(data); assertEquals(0, value); // test -0 data = "-0".getBytes(); // test +0 data = "+0".getBytes(); assertEquals(0, Convert.toLong(data)); // test -00000 data = "-0000".getBytes(); assertEquals(0, Convert.toLong(data)); // test 00000 data = "0000".getBytes(); // test +00000 data = "+0000".getBytes(); assertEquals(0, Convert.toLong(data)); // test 00001 data = "00001".getBytes(); assertEquals(1, Convert.toLong(data)); // test 0000100 data = "0000100".getBytes(); assertEquals(100, Convert.toLong(data)); // test 00001 data = "+00001".getBytes(); assertEquals(1, Convert.toLong(data)); // test +0000100 data = "+0000100".getBytes(); assertEquals(100, Convert.toLong(data)); // test 00001 data = "-00001".getBytes(); assertEquals(-1, Convert.toLong(data)); // test +0000100 data = "-0000100".getBytes(); assertEquals(-100, Convert.toLong(data)); // test 1 data = "1".getBytes(); assertEquals(1, Convert.toLong(data)); // test -1 data = "-1".getBytes(); assertEquals(-1, Convert.toLong(data)); // test +1 data = "+1".getBytes(); assertEquals(1, Convert.toLong(data)); // test $+1 data = "$+1".getBytes(); assertEquals(1, Convert.toLong(data, 1, data.length-1)); // test $-1 data = "$-1".getBytes(); assertEquals(-1, Convert.toLong(data, 1, data.length-1)); // test $+1GARBAGEDATA data = "$+1GARBAGEDATA".getBytes(); assertEquals(1, Convert.toLong(data, 1, 2)); // test $-1GARAGEDATA data = "$-1GARAGEDATA".getBytes(); assertEquals(-1, Convert.toLong(data, 1, 2)); // now lets go to the limit // data = Long.toString(Long.MAX_VALUE).getBytes(); assertEquals(Long.MAX_VALUE, Convert.toLong(data, 0, data.length)); data = Long.toString(Long.MIN_VALUE).getBytes(); assertEquals(Long.MIN_VALUE, Convert.toLong(data, 0, data.length)); } // just for scoping .. } /** * Test method for {@link org.jredis.alphazero.util.util.S27.jredis_deprecated.client.util.Convert#toBytes(int, boolean)}. */ @Test public void testToBytes() { Log.log("Testing number to bytes conversion ..."); byte[] javadata = null; byte[] data = null; // test MIN int n; n=Integer.MIN_VALUE; javadata = Integer.toString(n).getBytes(); data = Convert.toBytes(n); assertEquals (data.length, javadata.length, "buffer length"); for(int j=0; j byte @ ["+j+"]"); // test MAX n=Integer.MAX_VALUE; javadata = Integer.toString(n).getBytes(); data = Convert.toBytes(n); assertEquals (data.length, javadata.length, "buffer length"); for(int j=0; j byte @ ["+j+"]"); // test a bit smaller than Convert.INT_N_65535 n=Convert.INT_N_65535 - 444; javadata = Integer.toString(n).getBytes(); data = Convert.toBytes(n); assertEquals (data.length, javadata.length, "buffer length"); for(int j=0; j byte @ ["+j+"]"); // test a bit larger than Convert.INT_P_65535 n=Convert.INT_P_65535 + 444; javadata = Integer.toString(n).getBytes(); data = Convert.toBytes(n); assertEquals (javadata.length, data.length, "buffer length"); for(int j=0; j byte @ ["+j+"]"); // test the exact range range for(int i=Convert.INT_N_65535; i byte @ ["+j+"]"); } } } ================================================ FILE: core/ri/src/test/java/org/jredis/ri/alphazero/support/GZipTest.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.alphazero.support; import java.nio.charset.Charset; import java.util.Random; import org.testng.annotations.*; import org.testng.Assert; import static org.jredis.ri.alphazero.support.GZip.*; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Aug 24, 2009 * @since alpha.0 * */ @Test(suiteName="support-tests") public class GZipTest { public final static Charset SUPPORTED_CHARSET = Charset.forName ("UTF-8"); @Test public void testCompression() { Log.log("Testing compress/decompress of 1000 random 24KB strings ..."); int cnt = 1000; int size = 1024 * 24; for(int i=0; i commands. The client libraries seem to approach this in various ways. Python treats all ops on the list extremeties as stack semantics (push and pop) defaulting to the redis list's 'tail' push and pop. Ruby also usses push and pop with explicit _tail or _head articulation. The Erlang client uses a direct mapping, as with JRedis, as of now. I personally find the redis push/pop commands confusing. Perhaps this is due to mild dyslexia, but the thought occurs that others will share in this difficulty. In designing JRedis, after initial experiments with java like semantics (redis.addToSet(...), etc.) a decision was made to use a one to one mapping for the api's end-user methods. But the list's r/l ambiguity seems to detract from the effectiveness of the api. So, in line with maintaining a close analog with Redis command set, and to address semantic ambiguities, JRedis api methods will change as follows: All Redis 'string' remain the same. The 'map' like operations (set, get, setnx, and mget) are fairly self effident. Perhaps setnx is a candidate for further consideration, but redis.get(key, value) is as obvious as it is going to get. For the remaning types, JRedis will adopt a uniform naming convention, with 'l' commands mapping to Redis 'list' operations, and 's' commands to 'set'. Set commands are already consistent, beginning with s. SINTER and SINTERSTORE are a bit cryptic for the novice, but there are no issues of ambiguity here. And potentially will have SJOIN and SJOINSTORE as some point and everything is fine. List commands will be modified so they all begin with 'l'. Stack semantic ops on 'lists' will be just that: lpush is a push: 'head' of the list. (Its perfectly clear what it means - the natural place things are pushed is the head.) So: redis.lpush (list, "go to the head of the class"); Same with lpop: List Pop. Pop the first element of the list. redis.lpop () // returns the poped head of the list rpush is 'adding an item to a list'. Its an append but 'add' is also fairly clear: the natural place you add things to a 'list' is the the 'tail'. (And Redis does not support 'insert' so its a non issue for now.) Either lappend or ladd will be fairly obvious to the end user. ... One version: [we'll skip over commands that won't change, such as dbsize, randomkey, etc.] => subset: set => SET get => GET mget => MGET setnew => SETNX incr => INCR decr => DECR incrby => INCRBY decrby => DECRBY => push pop append take range trim indexof modify remove => add remove card ismember members ... Another version: lset, lget, mget, setnx incr, decr, incrby, decrby lpush, lpop, ladd, ltake, lrange, ltrim, indexof, lset, lrem sadd, srem, scard, sismember, smembers ================================================ FILE: examples/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: examples/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: examples/pom.xml ================================================ 4.0.0 org.jredis jredis a.0-SNAPSHOT JRedis - EXAMPLES org.jredis jredis-examples a.0-SNAPSHOT jar org.jredis jredis-core-all a.0-SNAPSHOT maven-assembly-plugin simple-install package attached jar-with-dependencies ================================================ FILE: examples/src/main/java/org/jredis/examples/HelloAgain.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.ri.alphazero.JRedisClient; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import static org.jredis.ri.alphazero.support.DefaultCodec.*; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Apr 15, 2009 * @since alpha.0 * */ public class HelloAgain { public static final String key = "jredis::examples::HelloAgain::message"; public static final byte[] bkey = key.getBytes(); public static void main(String[] args) { String password = "jredis"; if(args.length > 0) password = args[0]; new HelloAgain().run(password); } private void run(String password) { try { ConnectionSpec spec = DefaultConnectionSpec.newSpec().setCredentials(password); JRedis jredis = new JRedisClient(spec); jredis.ping(); if(!jredis.exists(bkey)) { jredis.set(bkey, "Hello Again!"); System.out.format("Hello! You should run me again!\n"); } else { String msg = toStr ( jredis.get(bkey) ); System.out.format("%s\n", msg); } jredis.quit(); } catch (RedisException e){ if (e.getCommand()==Command.PING){ System.out.format("I'll need that password! Try again with password as command line arg for this program.\n"); } } catch (ProviderException e){ System.out.format("Oh no, an 'un-documented feature': %s\nKindly report it.", e.getMessage()); } catch (ClientRuntimeException e){ System.out.format("%s\n", e.getMessage()); } } } ================================================ FILE: examples/src/main/java/org/jredis/examples/PipelineInAction.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples; import static org.jredis.connector.Connection.Socket.Property.SO_RCVBUF; import static org.jredis.connector.Connection.Socket.Property.SO_SNDBUF; import static org.jredis.ri.alphazero.support.DefaultCodec.toLong; import static org.jredis.ri.alphazero.support.DefaultCodec.toStr; import java.util.Random; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.JRedisFuture; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.bench.Util; import org.jredis.bench.Util.Timer; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.ResponseStatus; import org.jredis.ri.alphazero.JRedisPipeline; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; /** * Pipelines are an order of magnitude faster than the request/reply connectors. * Get a sense of how much faster it is. * *

    use JVM flags -server -Xms512m -Xmx2560m for better results (adjust your * mem settings per your box's limits.) * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 5, 2009 * @since alpha.0 * */ public class PipelineInAction { @SuppressWarnings("boxing") public static void main (String[] args) { final ConnectionSpec spec = DefaultConnectionSpec.newSpec(); spec.setCredentials("jredis".getBytes()); spec.setDatabase(13); spec.setSocketProperty(SO_RCVBUF, 1024 * 24); spec.setSocketProperty(SO_SNDBUF, 1024 * 24); usingSynchSemantics(spec); final boolean forever = true; runJRedisPipelineSET (spec, 100000, 3, forever); } /** * @param spec */ @SuppressWarnings("boxing") private static void usingSynchSemantics (ConnectionSpec spec) { JRedisPipeline pipeline = new JRedisPipeline(spec); try { long start = System.currentTimeMillis(); pipeline.ping(); pipeline.flushall(); String cntrKey = "my-cntr"; Random rand = new Random(); byte[] data = new byte[8]; for(int i=0; i<100000; i++) pipeline.incr(cntrKey); long cntr = toLong (pipeline.sync().get(cntrKey)); for(int i=0; i<100000; i++){ rand.nextBytes(data); pipeline.set("random:"+i, "value:" + rand.nextInt()); } String randomVal = toStr (pipeline.sync().get("random:"+999)); System.out.format ("end using sync() = %d msec\n", System.currentTimeMillis() - start); System.out.format("%s => %d\n", cntrKey, cntr); System.out.format("%s => %s\n", "random:"+999, randomVal); } catch (RedisException e) { Log.problem("RedisException: " + e); } finally{ pipeline.sync().quit(); } } /** * pipelines SET reqCnt times and then waits on response of the last INCR. * If foverever flag is true, will do so forever. Each cycle prints out timing stats. * @param spec * @param reqCnt * @param forever */ @SuppressWarnings({ "unused", "boxing" }) private static void runJRedisPipelineGET (ConnectionSpec spec, int reqCnt, int size, boolean forever) { long totTime = 0; long avgRespTime = 0; float avgThroughput = 0; long iters = 0; JRedisFuture pipeline = new JRedisPipeline(spec); try { String key = "pipeKey"; byte[] data = new byte[size]; (new Random()).nextBytes(data); pipeline.del(key); pipeline.set(key, data); do { int cnt = 0; Util.Timer timer = Timer.startNewTimer(); Future futureBytes = null; while(cnt < reqCnt){ futureBytes = pipeline.get(key); cnt++; } long reqDoneTime = timer.mark(); assert futureBytes != null; @SuppressWarnings("null") byte[] value = futureBytes.get(); long respDoneTime = timer.mark(); // System.out.format("JRedisPipeline: %d GETs invoked @ %5d (%.2f ops/s)\n", cnt, reqDoneTime, timer.opsPerSecAtDelta(cnt, reqDoneTime)); float throughput = timer.opsPerSecAtMark(cnt); // System.out.format("JRedisPipeline: %d GETs completed @ %5d (%.2f ops/s) [%d msecs to comp] \n", cnt, timer.deltaAtMark(), throughput, respDoneTime-reqDoneTime); if(iters > 0){ totTime += respDoneTime; avgRespTime = (totTime) / iters; avgThroughput =(float)( reqCnt * 1000) / (float) avgRespTime; System.out.format("JRedisPipeline: %d GETs [%d bytes/GET] average response time @ %dms (%.2f ops/s) last: %dms\n", cnt, data.length, avgRespTime, avgThroughput, respDoneTime); // Assert.isEquivalent(data, value); } iters ++; // System.out.println (); } while(forever); pipeline.quit(); } catch (ProviderException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @SuppressWarnings({ "unused", "boxing" }) private static void runJRedisPipelinePING (ConnectionSpec spec, int reqCnt, int size, boolean forever) { long totTime = 0; long avgRespTime = 0; float avgThroughput = 0; long iters = 0; JRedisFuture pipeline = new JRedisPipeline(spec); try { do { int cnt = 0; Util.Timer timer = Timer.startNewTimer(); Future futureStat = null; while(cnt < reqCnt){ futureStat = pipeline.ping(); cnt++; } long reqDoneTime = timer.mark(); assert futureStat != null; @SuppressWarnings("null") ResponseStatus rstat = futureStat.get(); long respDoneTime = timer.mark(); // System.out.format("JRedisPipeline: %d PINGs invoked @ %5d (%.2f ops/s)\n", cnt, reqDoneTime, timer.opsPerSecAtDelta(cnt, reqDoneTime)); float throughput = timer.opsPerSecAtMark(cnt); // System.out.format("JRedisPipeline: %d PINGs completed @ %5d (%.2f ops/s) [%d msecs to comp] \n", cnt, timer.deltaAtMark(), throughput, respDoneTime-reqDoneTime); if(iters > 0){ totTime += reqDoneTime; avgRespTime = (totTime) / iters; avgThroughput =(float)( reqCnt * 1000) / (float) avgRespTime; System.out.print("\r"); System.out.format("JRedisPipeline: %d PINGs average response time @ %dms (%.2f ops/s)", cnt, avgRespTime, avgThroughput); } iters ++; // System.out.println (); } while(forever); pipeline.quit(); } catch (ProviderException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @SuppressWarnings("boxing") private static void runJRedisPipelineLPUSH (ConnectionSpec spec, int reqCnt, int size, boolean forever) { JRedisFuture pipeline = new JRedisPipeline(spec); long totTime = 0; long avgRespTime = 0; float avgThroughput = 0; long iters = 0; try { String key = "pipeKey"; byte[] data = new byte[size]; (new Random()).nextBytes(data); Future futureLong = pipeline.del(key); futureLong.get(); do { int cnt = 0; Util.Timer timer = Timer.startNewTimer(); Future futureStat = null; while(cnt < reqCnt){ futureLong = pipeline.lpush(key, data); cnt++; } long reqDoneTime = timer.mark(); assert futureStat != null; @SuppressWarnings({ "null", "unused" }) ResponseStatus rstat = futureStat.get(); long respDoneTime = timer.mark(); System.out.format("JRedisPipeline: %d LPUSHs invoked @ %5d (%.2f ops/s)\n", cnt, reqDoneTime, timer.opsPerSecAtDelta(cnt, reqDoneTime)); System.out.format("JRedisPipeline: %d LPUSHs completed @ %5d (%.2f ops/s) [%d msecs to comp] \n", cnt, timer.deltaAtMark(), timer.opsPerSecAtMark(cnt), respDoneTime-reqDoneTime); if(iters > 0){ totTime += reqDoneTime; avgRespTime = (totTime) / iters; avgThroughput =(float)( reqCnt * 1000) / (float) avgRespTime; System.out.format("JRedisPipeline: %d LPUSHs average response time @ %dms (%.2f ops/s) \n", cnt, avgRespTime, avgThroughput); } iters ++; System.out.println (); } while(forever); pipeline.quit(); } catch (ProviderException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } /** * pipelines SET reqCnt times and then waits on response of the last INCR. * If foverever flag is true, will do so forever. Each cycle prints out timing stats. * @param spec * @param reqCnt * @param forever */ @SuppressWarnings("boxing") private static void runJRedisPipelineSET (ConnectionSpec spec, int reqCnt, int size, boolean forever) { JRedisFuture pipeline = new JRedisPipeline(spec); long totTime = 0; long avgRespTime = 0; float avgThroughput = 0; long iters = 0; try { String key = "pipeKey"; byte[] data = new byte[size]; (new Random()).nextBytes(data); Future futureLong = pipeline.del(key); futureLong.get(); do { int cnt = 0; Util.Timer timer = Timer.startNewTimer(); Future futureStat = null; while(cnt < reqCnt){ futureStat = pipeline.set(key, data); cnt++; } assert futureStat != null; @SuppressWarnings({ "null", "unused" }) ResponseStatus rstat = futureStat.get(); long respDoneTime = timer.mark(); if(iters > 0){ totTime += respDoneTime; avgRespTime = (totTime) / iters; avgThroughput =(float)( reqCnt * 1000) / (float) avgRespTime; System.out.format("JRedisPipeline: %d SETs [%d bytes/GET] average response time @ %dms (%.2f ops/s) \n", cnt, data.length, avgRespTime, avgThroughput); } iters ++; } while(forever); pipeline.quit(); } catch (ProviderException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } /** * pipelines INCRs reqCnt times and then waits on response of the last INCR. * If foverever flag is true, will do so forever. Each cycle prints out timing stats. * @param spec * @param reqCnt * @param forever */ @SuppressWarnings("boxing") private static void runJRedisPipelineINCR (ConnectionSpec spec, int reqCnt, int size, boolean forever) { JRedisFuture pipeline = new JRedisPipeline(spec); long totTime = 0; long avgRespTime = 0; float avgThroughput = 0; long iters = 0; try { String key = "pipeCounter"; Future futureDelCnt = pipeline.del(key); futureDelCnt.get(); do { int cnt = 0; Util.Timer timer = Timer.startNewTimer(); Future futureLong = null; while(cnt < reqCnt){ futureLong = pipeline.incr(key); cnt++; } long reqDoneTime = timer.mark(); assert futureLong != null; @SuppressWarnings("null") long counter = futureLong.get(); long respDoneTime = timer.mark(); System.out.format("JRedisPipeline: %d INCRs invoked @ %5d (%.2f ops/s)\n", cnt, reqDoneTime, timer.opsPerSecAtDelta(cnt, reqDoneTime)); System.out.format("JRedisPipeline: %d INCRs completed @ %5d (%.2f ops/s) [%d msecs to comp] \n", cnt, timer.deltaAtMark(), timer.opsPerSecAtMark(cnt), respDoneTime-reqDoneTime); System.out.format ("counter is now: %d\n\n", counter); if(iters > 0){ totTime += reqDoneTime; avgRespTime = (totTime) / iters; avgThroughput =(float)( reqCnt * 1000) / (float) avgRespTime; System.out.format("JRedisPipeline: %d INCRs average response time @ %dms (%.2f ops/s) \n", cnt, avgRespTime, avgThroughput); } iters ++; System.out.println (); } while(forever); pipeline.quit(); } catch (ProviderException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } ================================================ FILE: examples/src/main/java/org/jredis/examples/UsingConnectionSpec.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_BANDWIDTH; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_CONN_TIME; import static org.jredis.connector.Connection.Socket.Property.SO_PREF_LATENCY; import static org.jredis.connector.Connection.Socket.Property.SO_RCVBUF; import static org.jredis.connector.Connection.Socket.Property.SO_SNDBUF; import java.net.InetAddress; import java.net.UnknownHostException; import org.jredis.RedisException; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisClient; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; /** * Illustrates using the {@link ConnectionSpec} as a parameter when creating the {@link JRedisClient}. * @author Joubin (alphazero@sensesay.net) * @see ConnectionSpec */ public class UsingConnectionSpec { public static void main (String[] args) { exampleUsingDefaultConnectionSpec (); exampleUsingCustomTCPSettings(); } /** * Alright, so here we're going to fiddle with the various TCP settings that the JRedisClient * will use in its connection. */ @SuppressWarnings("boxing") private static void exampleUsingCustomTCPSettings () { // try our own values for various flags and properties // { // Note that if your localhost:6379 redis server expects a password // this will fail try { String password = "jredis"; InetAddress address = InetAddress.getLocalHost(); // 1 - get the default connection spec // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(); // 2 - customize it // here we're demonstrating the full set of parameters -- obviously you can just set what you need // but DO NOTE that the SO_PREF_XXX properties of TCP sockets must be defined as a set. See // ConnectionSpec for javadoc details. // // Here we are spec'ing a connection that is NOT kept alive, and obviously is really keen on making sure // we connect as fast as possible. This is a good connectionspec for disposable JRedisClients that are used // to issue a few commands and then discarded. We're minimizing the connection overhead cost. connectionSpec // to be or not to be -- you decide // .setSocketFlag(Connection.Socket.Flag.SO_KEEP_ALIVE, Boolean.FALSE) // DO NOT keep socket alive // connect retries on connection breaks // .setReconnectCnt(2) // reconnect attempts set to 2 retries // TCP algorithm preferences // .setSocketProperty(SO_PREF_CONN_TIME, 0) // connection time is highester pref .setSocketProperty(SO_PREF_LATENCY, 1) // latency is 2nd pref .setSocketProperty(SO_PREF_BANDWIDTH, 2) // bandwidth is 3rd pref // TCP buffer sizes -- more than likely your platform's default settings are quite large // but if you are itching to try your own settings, please do. Remember: connections // will use whatever is the larger value: you OS's TCP buffer sizes or your ConnectionSpecs // so you can NOT use these settings to shrink the SND and RCV buffer sizes. // .setSocketProperty(SO_RCVBUF, 1024 * 128) // set RCV buffer to 128KB .setSocketProperty(SO_SNDBUF, 1024 * 128) // set SND buffer to 128KB // obviously we can still set the basic props as well .. // .setAddress(address) .setCredentials(password.getBytes()) .setDatabase(13); // finally - use it to create the JRedisClient instance // JRedisClient jredis = new JRedisClient(connectionSpec); jredis.ping(); Log.log("Sweet success -- we're connected using custom TCP settings"); } catch (RedisException e) { Log.error("Failed to connect to Redis using JRedisClient custom ConnectionSpec -- password perhaps? => " + e.getLocalizedMessage()); } catch (UnknownHostException e) { Log.error("Unknownhost: " + e.getLocalizedMessage()); } } } /** * On the first try, We're not really using the {@link ConnectionSpec} here, but it shows how to use * the {@link JRedisClient#JRedisClient(ConnectionSpec)} constructor. Defaults are * localhost, 6379, and no password, and database 0. *

    * Then we try setting the basic {@link ConnectionSpec} properties: host, port, password, and database. */ @SuppressWarnings("boxing") private static void exampleUsingDefaultConnectionSpec () { // 1st example: using defaults // if your server expects a AUTH password, this will fail so see next block { // Note that if your localhost:6379 redis server expects a password // this will fail ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(); JRedisClient jredis = new JRedisClient(connectionSpec); try { jredis.ping(); Log.log("Sweet success -- we're connected using all default values."); } catch (RedisException e) { Log.error("Failed to connect to Redis using JRedisClient default ConnectionSpec -- password perhaps? => " + e.getLocalizedMessage()); } } // 2nd example: using defaults but setting the password and the database. // also demonstrated using the method chaining on the ConnectionSpec property setters. { // Note that if your localhost:6379 redis server expects a password // this will fail String password = "jredis"; int database = 11; ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(); connectionSpec .setCredentials(password.getBytes()) .setDatabase(database); JRedisClient jredis = new JRedisClient(connectionSpec); try { jredis.ping(); Log.log("Sweet success -- we're connected using %s as password to %d database.", password, database); } catch (RedisException e) { Log.error("Failed to connect to Redis using JRedisClient default ConnectionSpec -- password perhaps? => " + e.getLocalizedMessage()); } } // final example: using defaults but setting the full set of basic settings { // Note that if your localhost:6379 redis server expects a password // this will fail try { String password = "jredis"; int port = 6379; int database = 11; InetAddress address = InetAddress.getLocalHost(); // 1 - get the default connection spec for the basic settings // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec(address, port, database, password.getBytes()); // finally - use it to create the JRedisClient instance // JRedisClient jredis = new JRedisClient(connectionSpec); jredis.ping(); Log.log("Sweet success -- we're connected using default specs and various server info settings."); } catch (RedisException e) { Log.error("Failed to connect to Redis using JRedisClient default ConnectionSpec -- password perhaps? => " + e.getLocalizedMessage()); } catch (UnknownHostException e) { Log.error("Unknownhost: " + e.getLocalizedMessage()); } } } } ================================================ FILE: examples/src/main/java/org/jredis/examples/UsingJRedisFuture.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.jredis.JRedis; import org.jredis.JRedisFuture; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.ResponseStatus; import org.jredis.ri.alphazero.support.DefaultCodec; import org.jredis.ri.alphazero.support.Log; /** * Example usage of {@link JRedisFuture}, demonstrating the asynchronous semantics. *

    * Note that this is an abstract class and concrete extensions will use specific * implementation of {@link JRedisFuture}. Refer to the extension class for the * specifics of the associated {@link JRedisFuture} provider class. * * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 11, 2009 * @since alpha.0 * */ public abstract class UsingJRedisFuture { // ------------------------------------------------------------------------ // EXAMPLES: // ------------------------------------------------------------------------ /** * The basics of using {@link Future} objects and {@link JRedisFuture}. *

    Note the key differences between using {@link JRedis} and {@link JRedisFuture}: *

      *
    • Methods have identical parameters, but all asynchronous methods return a flavor of {@link Future} *
    • Exception handling is significantly different. *
    * @throws InterruptedException */ @SuppressWarnings("boxing") public void theBasics () throws InterruptedException { Log.log("theBasics()"); /*******************************************/ // fire and forget -- no exception handling and not even bothering about the response /*******************************************/ // this may throw exceptions on the invoke jredis.ping(); /*******************************************/ // invoke and wait for response -- was it OK? /*******************************************/ Future fResp = null; try { /* * Alright, asynch call is just like the synchronous flavor, but the return type is * a Future object. Here we are doing a PING which simply returns a STATUS response * from redis, so the return type is Future * */ fResp = jredis.ping(); /* * So we have a Future object, but is that the actual response? No. You need to wait * for it to actually complete. * * Here we're waiting using blocking semantics: we wait until its done. */ ResponseStatus response = fResp.get(); /** * So we have our response at this point. We can check it, etc. */ if(response.isError()){ Log.error("PING returned an ERR response -- is it an authorization issue?"); } } catch (InterruptedException e) { /* * This can happen if the calling thread (which is under your control, not JRedis) is * interrupted. Given that, its entirely up to you to determine what you should do here. * If this is all greek to you, then simply log and re-throw it back up, or add InterruptedException * to your method signatures. */ Log.problem("thread was interrupted while waiting for the response to be processed."); throw e; } catch (ExecutionException e) { /** * Remember the exceptions that a synchronous JRedis method can throw (ClientRuntime, ProviderException, * and of course RedisException)? Well, ExecutionException is the overall wrapper exception that is thrown * by (all) Future instances to permit the propagation of those exceptions to the caller in an asynch manner. * So, basically, if any problems arose after your call successfully returned (from the JRedisFuture method), * here/now is where/when you find out about it. * * So what need to happen is for you to get the underlying 'cause' of the generalized ExecutionException. */ Log.problem("An execution exception occurred"); } /*******************************************/ // invoke and wait for response using timeouts /*******************************************/ byte[] setval = "bar".getBytes(); try { for(int i=0; i<100000; i++){ jredis.ping(); jredis.incr("cntr"); } jredis.set("foo", setval); /* * Here we're waiting using blocking semantics with timeout */ Future fVal = jredis.get("foo"); long t1 = System.nanoTime(); int toCnt = 0; while(true){ try { byte[] val = fVal.get(100L, TimeUnit.MICROSECONDS); String value = DefaultCodec.toStr(val); System.out.format("=> %s\n", value); break; } catch (TimeoutException e) { toCnt++; // System.out.println('.'); } } t1 = System.nanoTime() - t1; Log.log("done after %d timeouts (%d nanos)", toCnt, t1); } catch (InterruptedException e) { /* * This can happen if the calling thread (which is under your control, not JRedis) is * interrupted. Given that, its entirely up to you to determine what you should do here. * If this is all greek to you, then simply log and re-throw it back up, or add InterruptedException * to your method signatures. */ Log.problem("thread was interrupted while waiting for the response to be processed."); throw e; } catch (ExecutionException e) { /** * Remember the exceptions that a synchronous JRedis method can throw (ClientRuntime, ProviderException, * and of course RedisException)? Well, ExecutionException is the overall wrapper exception that is thrown * by (all) Future instances to permit the propagation of those exceptions to the caller in an asynch manner. * So, basically, if any problems arose after your call successfully returned (from the JRedisFuture method), * here/now is where/when you find out about it. * * So what need to happen is for you to get the underlying 'cause' of the generalized ExecutionException. */ Log.problem("An execution exception occurred"); } } // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** {@link JRedisFuture} provider instance */ protected final JRedisFuture jredis; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * Creates a new instances and runs the example methods. * @param connectionSpec used for creating the connection */ public UsingJRedisFuture (ConnectionSpec connectionSpec){ this.jredis = getProviderInstance(connectionSpec); try { runExamples(); } catch (InterruptedException e) { Log.problem("Interrupted while running the examples."); e.printStackTrace(); } jredis.quit(); } /** * @throws InterruptedException * */ private void runExamples () throws InterruptedException { Log.log("running the JRedisFuture usage examples with %s as the provider implementation.", jredis.getClass().getSimpleName()); theBasics(); } // ------------------------------------------------------------------------ // Extension point // ------------------------------------------------------------------------ /** * Extension point. * @param connectionSpec used for creating the connection. * @return the {@link JRedisFuture} implementation instance */ abstract protected JRedisFuture getProviderInstance (ConnectionSpec connectionSpec); } ================================================ FILE: examples/src/main/java/org/jredis/examples/UsingJRedisPipeline.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples; import static org.jredis.ri.alphazero.support.DefaultCodec.toLong; import static org.jredis.ri.alphazero.support.DefaultCodec.toStr; import java.util.Random; import org.jredis.JRedisFuture; import org.jredis.RedisException; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisPipeline; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; /** * Extension of {@link UsingJRedisFuture} with a {@link JRedisPipeline} as the * provider. *

    See {@link UsingJRedisFuture} for demonstration of generic asynchronous * semantics. *

    See {@link UsingJRedisPipeline#exampleUseofSyncInPipeline(ConnectionSpec)} for {@link JRedisPipeline} specific * mix mode usage incorporating asynchronous and synchronous calls. * * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 11, 2009 * @since alpha.0 * @see UsingJRedisFuture * */ public class UsingJRedisPipeline extends UsingJRedisFuture { public static void main (String[] args) { final int database = 13; ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec("localhost", 6379, database, "jredis".getBytes()); exampleUseofSyncInPipeline(connectionSpec); } /** * Example highlights usage pattern of pipeline with both the natural asynchronous * semantics and synchronous semantics provided by {@link JRedisPipeline#sync()} * * @param connectionSpec */ @SuppressWarnings("boxing") private static void exampleUseofSyncInPipeline (ConnectionSpec connectionSpec) { // Note that we are using a JRedisPipeline reference and not a generic // JRedisFuture here -- this exposes the additional JRedisPipeline pipeline = new JRedisPipeline(connectionSpec); /* * Alright, so a hokey example of situations where we would like to * asynchronously pipeline a bunch of commands, and then at various point in * the process need to sync up with redis and get values before continuing. * * Obviously we can do this using JRedisFuture as well by calling get() on the * returned Future object, but the sync() method may be enhanced in future to * provide additional features. Regardless, it does some of the boiler plate * ExecutionException handling code so its a bit prettier. */ try { long start = System.currentTimeMillis(); /* a sequence of asynchronous calls */ pipeline.ping(); pipeline.flushdb(); Random rand = new Random(); byte[] data = new byte[8]; for(int i=0; i<1000000; i++){ rand.nextBytes(data); pipeline.lpush("my-list", data); } /* * switch to synchronous semantics * the following call will block until * all the responses for above + the llen() * itself have been received. */ long llen = pipeline.sync().llen("my-list"); String cntrKey = "my-cntr"; for(int i=0; i<100000; i++) { pipeline.incr(cntrKey); } /* sync call */ long cntr = toLong (pipeline.sync().get(cntrKey)); for(int i=0; i<100000; i++){ pipeline.set("random:"+i, "value:" + rand.nextInt()); } /* sync call */ String randomVal = toStr (pipeline.sync().get("random:"+999)); pipeline.flushdb(); System.out.format ("end using sync() = %d msec\n", System.currentTimeMillis() - start); System.out.format("%s => %d\n", cntrKey, cntr); System.out.format("%s => %s\n", "random:"+999, randomVal); System.out.format("%s has %s items\n", "my-list", llen); } catch (RedisException e) { Log.problem("RedisException: " + e); } finally{ pipeline.sync().quit(); Log.log("shutting down."); } } /** * @param connectionSpec */ public UsingJRedisPipeline (ConnectionSpec connectionSpec) { super(connectionSpec); } /* (non-Javadoc) * @see org.jredis.examples.UsingJRedisFuture#getProviderInstance(org.jredis.connector.ConnectionSpec) */ @Override protected JRedisFuture getProviderInstance (ConnectionSpec connectionSpec) { return new JRedisPipeline(connectionSpec); } } ================================================ FILE: examples/src/main/java/org/jredis/examples/UsingJRedisPipelineService.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisClient; import org.jredis.ri.alphazero.JRedisPipelineService; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import static org.jredis.ri.alphazero.support.DefaultCodec.*; /** * JRedisPipelineService provides blocking synchronous semantics backed by a pipeline and is suitable * for concurrent usages with a single (socket) connection to the server. There is really nothing * different here than using a vanial {@link JRedisClient}. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 6, 2009 * @since alpha.0 * */ public class UsingJRedisPipelineService { final JRedis jredis; private UsingJRedisPipelineService() { // same as usual. ConnectionSpec spec = DefaultConnectionSpec.newSpec(); spec.setDatabase(11).setCredentials("jredis".getBytes()); // only need to use the specific class. jredis = new JRedisPipelineService(spec); } private void run () { try { jredis.ping(); basicStuff(); elicitErrors(); } catch (RedisException e) { e.printStackTrace(); } // Use the connection concurrently final int wcnt = 10; final int opcnt = 20000; final Thread[] workers = new Thread[wcnt]; final JRedis client = jredis; for(int i=0; i %s\n", wkey, wvalue); } }); workers[i].start(); } for(Thread t : workers){ try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } // jredis.quit(); } private void elicitErrors () { String key = "foo" ; try { jredis.set(key, "bar"); jredis.sadd(key, "foobar"); } catch (RedisException e) { Log.log("Expected elicited error: %s", e.getMessage()); } } private void basicStuff () throws RedisException { jredis.flushdb(); String key = "foo" ; jredis.set(key, "bar"); String value = toStr(jredis.get(key)); System.out.format("%s => %s\n", key, value); } public static void main (String[] args) { (new UsingJRedisPipelineService()).run(); } } ================================================ FILE: examples/src/main/java/org/jredis/examples/UsingJRedisService.java ================================================ ///* // * Copyright 2009 Joubin Houshyar // * // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. // * You may obtain a copy of the License at // * // * http://www.apache.org/licenses/LICENSE-2.0 // * // * Unless required by applicable law or agreed to in writing, software // * distributed under the License is distributed on an "AS IS" BASIS, // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * See the License for the specific language governing permissions and // * limitations under the License. // */ // //package org.jredis.examples; // //import org.jredis.RedisException; //import org.jredis.connector.ConnectionSpec; //import org.jredis.ri.alphazero.JRedisService; //import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; // ///** // * [TODO: document me!] // * // * @author Joubin Houshyar (alphazero@sensesay.net) // * @version alpha.0, Sep 1, 2009 // * @since alpha.0 // * // */ // //public class UsingJRedisService { // public static JRedisService service = null; // /** // * Demonstrated using the {@link JRedisService} class. Its also a bench // * that shows the performance of the service with lots of threads banging on it. // * Don't forget to flush db#11 after running this as it adds a whole bunch of keys. // * @param args // */ // public static void main (String[] args) { // int database = 11; // ConnectionSpec connectionSpec = DefaultConnectionSpec.newSpec("localhost", 6379, database, "jredis".getBytes()); // int connCnt = 7; // int userCnt = 10; // int opsCnt = 100000; // // // create the service -- well this is it as far as usage goes: set the number of connections for the service pool // // You can use this anywhere you would use JRedis instances and it is thread safe. // // // service = new JRedisService(connectionSpec, connCnt); // // // create a bunch of dummy users for the service // Thread[] users = new Thread[userCnt]; // for(int i=0; iNote that the example flushes DB 10. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Nov 25, 2009 * @since alpha.0 | Redis 1.07 */ public class UsingBulkCommands { public static void main (String[] args) { usingSyncClient(); usingAsyncClient(); } /** * Using the synchronous interface */ public static void usingSyncClient () { ConnectionSpec spec = DefaultConnectionSpec.newSpec() .setCredentials("jredis".getBytes()) .setDatabase(10); JRedis jredis = new JRedisClient(spec); System.out.println ("\nusing the SyncClient: \n\n"); useMSet (jredis); useMSetNX (jredis); jredis.quit(); } private static void useMSet (JRedis jredis) { // BulkSetMapping provides a set of static methods to create new specific // flavors of KeyValueSet for the JRedis.mset(..) method. // use this when your values are all of the same type. // Here we are using String values KeyValueSet.Strings kvSet = BulkSetMapping.newStringKVSet(); kvSet .add("foo", "woof") .add("bar", "meow") .add("paz", "the salt") .add("x?", "yz!"); try { jredis.flushdb(); jredis.mset(kvSet); } catch (RedisException e) { e.printStackTrace(); } } private static void useMSetNX (JRedis jredis) { // Here we are using a mixed set of value types Map kvMap = new HashMap(); kvMap.put("foo", "bar".getBytes()); kvMap.put("cat", "meow".getBytes()); kvMap.put("dog", "woof".getBytes()); kvMap.put("bird", "whale fail".getBytes()); kvMap.put("pi", String.valueOf(3.141592653589793).getBytes()); try { jredis.flushdb(); jredis.set("bird", "tweet"); // <= force an error on msetnx boolean stat = jredis.msetnx(kvMap); if(!stat) { System.out.format("Couldn't msetnx - one of these already exists: %s\n", kvMap.keySet()); } // and now we can find out which one existed: for(String key : kvMap.keySet()){ if(jredis.exists(key)){ System.out.format("key '%s' [value: %s] already existed!\n", key, new String(jredis.get(key))); } } } catch (RedisException e) { e.printStackTrace(); } } /** * Using the asynchronous interface */ public static void usingAsyncClient () { ConnectionSpec spec = DefaultConnectionSpec.newSpec() .setCredentials("jredis".getBytes()) .setDatabase(10); JRedisFuture jredis = new JRedisAsyncClient(spec); System.out.println ("\nusing the AsyncClient: \n\n"); useMSet(jredis); useMSetNX (jredis); jredis.quit(); } private static void useMSetNX (JRedisFuture jredis) { Map kvMap = new HashMap(); kvMap.put("foo", "bar".getBytes()); kvMap.put("cat", "meow".getBytes()); kvMap.put("dog", "woof".getBytes()); kvMap.put("bird", "whale fail".getBytes()); kvMap.put("pi", String.valueOf(3.141592653589793).getBytes()); try { jredis.flushdb(); jredis.set("bird", "tweet"); Future future = jredis.msetnx(kvMap); if(!future.get()) { System.out.format("Couldn't msetnx - one of these already exists: %s\n", kvMap.keySet()); } // and now we can find out which one existed: for(String key : kvMap.keySet()){ if(jredis.exists(key).get()){ System.out.format("key '%s' [value: %s] already existed!\n", key, new String(jredis.get(key).get())); } } } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } private static void useMSet (JRedisFuture jredis) { // BulkSetMapping provides a set of static methods to create new specific // flavors of KeyValueSet for the JRedis.mset(..) method. // use this when your values are all of the same type. // Here we are using Number values KeyValueSet.Numbers kvSet = BulkSetMapping.newNumberKVSet(); kvSet .add("integer", 10) .add("real", 10.55) .add("scientific", 1.5e4) .add("sepher", 0); try { jredis.flushdb(); Future future = jredis.mset(kvSet); if(future.get().isError()){ throw new RuntimeException("MSET failed! (How could that be?)"); } jredis.exists("foo"); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } ================================================ FILE: examples/src/main/java/org/jredis/examples/commands/UsingZrangeSubset.java ================================================ /* * Copyright 2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.examples.commands; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jredis.JRedis; import org.jredis.JRedisFuture; import org.jredis.RedisException; import org.jredis.RedisType; import org.jredis.ZSetEntry; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.ri.alphazero.JRedisAsyncClient; import org.jredis.ri.alphazero.JRedisClient; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.DefaultCodec; /** * [TODO: document me!] * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Mar 20, 2010 * @since alpha.0 * */ public class UsingZrangeSubset { static final String zset = "example-sorted-set"; static final Random rand = new Random(System.currentTimeMillis()); public static void main (String[] args) { usingSyncClient(); usingAsyncClient(); } /** * Using the synchronous interface */ public static void usingSyncClient () { ConnectionSpec spec = DefaultConnectionSpec.newSpec() .setCredentials("jredis".getBytes()) .setDatabase(10); JRedis jredis = new JRedisClient(spec); System.out.println ("** using JRedis **"); useZRangeSubset (jredis); jredis.quit(); } /** * Using the asynchronous interface */ public static void usingAsyncClient () { ConnectionSpec spec = DefaultConnectionSpec.newSpec() .setCredentials("jredis".getBytes()) .setDatabase(10); JRedisFuture jredis = new JRedisAsyncClient(spec); System.out.println ("\n\n** using JRedisFuture **"); useZRangeSubset(jredis); jredis.quit(); } /** * The z[rev]rangeSubset commands return non-standard results tpes (see {@link ZSetEntry}). Nothing * that unexpected as far as the semantics are concerned, but just in case it is not perfectly clear, this * example illustrates its intended use. * * So here we'll flush the db, {@link Command#ZADD} a few (scored) entries to a {@link RedisType#zset}, and * then use {@link JRedis#zrangeSubset(String, long, long)} and {@link JRedis#zrevrangeSubset(String, long, long)} * and dump the results to console. * * @param jredis */ private static void useZRangeSubset (JRedis jredis) { try { jredis.flushdb(); // we'll add a few String values to our set with random scores // for(int i=0; i<100; i++){ jredis.zadd(zset, rand.nextDouble(), getRandomAsciiString(rand, 8)); } // let's get the subset in range {0, 10} in ascending order // List subset = jredis.zrangeSubset(zset, 0, 10); System.out.format ("\n\n%s elements from 0 to 10 - natural order:\n\t---\n", zset); for(int j=0; j revsubset = jredis.zrevrangeSubset(zset, 0, 10); System.out.format ("\n\n%s elements from 0 to 10 - reverse order:\n\t---\n", zset); for(int j=0; j> futureSubset = jredis.zrangeSubset(zset, 0, 10); Future> futureRevSubset = jredis.zrevrangeSubset(zset, 0, 10); try { System.out.format ("\n\n%s elements from 0 to 10 - natural order:\n\t---\n", zset); List subset = futureSubset.get(); for(int j=0; j revsubset = futureRevSubset.get(); System.out.format ("\n\n%s elements from 0 to 10 - reverse order:\n\t---\n", zset); for(int j=0; j Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: extensions/3rd party Licenses/NET.SPY.MEMCACHED NOTICE ================================================ NOTICE: Portions of the reference implementation libaray for JRedis/extensions uses verbatim and/or modified derivatives of original work by Dustin Sallings in net.spy.memcached (Java client for Memcached). Documentation in extensions/ri highlights such usage in JRedis library. Original work's Copyright notice follows: -- BEGIN -- Copyright (c) 2006-2009 Dustin Sallings Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- END -- END OF NOTICE ================================================ FILE: extensions/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: extensions/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: extensions/README.markdown ================================================ # JRedis Extensions Extended functionality for [JRedis] [JRedis]: http://github.com/alphazero/jredis ================================================ FILE: extensions/api/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: extensions/api/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: extensions/api/README.markdown ================================================ # API/Sepcifications of JRedis Extensions Extended functionality for [JRedis] [JRedis]: http://github.com/alphazero/jredis ================================================ FILE: extensions/api/pom.xml ================================================ 4.0.0 org.jredis jredis-extensions ${jredisVersion} JRedis - Extensions - API org.jredis jredis-extensions-api ${jredisVersion} jar org.jredis jredis-core-api ${jredisVersion} org.jredis jredis-core-ri ${jredisVersion} org.testng testng 6.1.1 test maven-assembly-plugin 2.1 simple-install package attached jar-with-dependencies ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/ClusterModel.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import java.util.HashSet; import java.util.Set; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; /** * Represent (a potentially active) model of a cluster. *

    * Implementors may throw {@link NotSupportedException} for the optional methods. * @author joubin (alphazero@sensesay.net) * @date Mar 28, 2010 * */ public interface ClusterModel { /** * Indicates if the implementation supports the cluster type. * @param type * @return */ public boolean supports(ClusterType type); /** * Indicates if the {@link ClusterModel} implementation supports dynamic cluster configurations, * @return true if yes, otherwise false. */ public boolean supportsReconfiguration(); /** * @return the {@link ClusterSpec} that accurately represents the current state * of the model. */ public ClusterSpec getSpec(); /** * Optional. *

    * Must raise a {@link ClusterModel.Event.Type#NodeAdded} event with the deleted node as * the event's info. Event must be raised after the model has transitioned to the new state. * @param nodeSpec * @throws IllegalArgumentException */ public void addNode(ClusterNodeSpec nodeSpec) throws IllegalArgumentException; /** * Optional. *

    * Must raise a {@link ClusterModel.Event.Type#NodeAdded} event with the new node as * the event's info. Event must be raised after the model has transitioned to the new state. * @param nodeSpec * @throws IllegalArgumentException */ public void removeNode(ClusterNodeSpec nodeSpec) throws IllegalArgumentException; // ------------------------------------------------------------------------ // Cluster semantics /** * Maps the given key to a specified node of the cluster, by returning its * {@link ClusterNodeSpec}. * @param key * @return */ public ClusterNodeSpec getNodeForKey (byte[] key); // ------------------------------------------------------------------------ // Event management /** * Optinal * @param modelListener * @return */ public boolean addListener(Listener modelListener); /** * Optinal * @param modelListener * @return */ public boolean removeListener(Listener modelListener); // ======================================================================== // Inner Types // ======================================================================== // ------------------------------------------------------------------------ // EventListener // ------------------------------------------------------------------------ /** * Your basic ClusterModel.Event Listener. * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ public interface Listener { public void onEvent(ClusterModel.Event event); } // ------------------------------------------------------------------------ // Model Events // ------------------------------------------------------------------------ /** * Optional event interface for dynamic models which support event * generation. * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ public static class Event extends org.jredis.Event { /** */ private static final long serialVersionUID = 1L; /** generated at Event construction time */ private final long timestamp; /** * @param src * @param type * @param info */ public Event (ClusterModel src, Type type, ClusterNodeSpec info) { super(src, type, info); timestamp = System.currentTimeMillis(); } /** @return event approximate event generation time. */ public long getTimestamp () { return timestamp; } /** ClusterModel.Event.Types */ public enum Type { Initialized, NodeAdded, NodeRemoved } } // ------------------------------------------------------------------------ // Support base // ------------------------------------------------------------------------ /** * Provides basic support for the {@link ClusterModel} interface for the * general semantics and features, and, extension points for specialized * {@link ClusterModel}s. *

    * Support for optional event management methods are provided. * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ abstract public static class Support implements ClusterModel { // -------------------------------------------------------------------- // properties // -------------------------------------------------------------------- /** */ final protected ClusterSpec clusterSpec; /** */ final private Set listeners = new HashSet(); /** */ final protected Object configLock = new Object(); // -------------------------------------------------------------------- // Constructor // -------------------------------------------------------------------- /** * Instantiates and initializes the model. This constructor will * invoke {@link Support#initializeModel()} immediately after setting * all parameters, including {@link ClusterSpec}. *

    * Due to initialization cycle, this constructor may throw {@link ClientRuntimeException} * or {@link ProviderException}s. Refer to the implementation for details. *

    * Methods that modify the cluster configuration, {@link ClusterModel#addNode(ClusterNodeSpec)} and * {@link ClusterModel#removeNode(ClusterNodeSpec)}, will first use the * {@link ClusterModel#supportsReconfiguration()} to check if such operations * are supported. If not, a {@link NotSupportedException} is thrown. *
    * Otherwise, this implementaiton wwill first obtain the {@link Support#configLock}, * and then modify the associated {@link ClusterSpec}, and will invoke the * extension point {@link Support#onNodeAddition(ClusterNodeSpec)} or * {@link Support#onNodeRemoval(ClusterNodeSpec)} to signal the configuration * change to the specialized subclass. Finally, after releasing the model's * config lock, all listerners are notified of the configuration change with * an appropriate {@link ClusterModel.Event}. * * @param clusterSpec */ protected Support(ClusterSpec clusterSpec){ if(null == clusterSpec) throw new IllegalArgumentException("clusterSpec param is null"); if(!supportsReconfiguration() && clusterSpec.getNodeSpecs().size() == 0) throw new IllegalArgumentException("clusterSpec has no ClusterNodeSpecs and this model can not be reconfigured."); this.clusterSpec = clusterSpec; initialize(); } // -------------------------------------------------------------------- // Interface // -------------------------------------------------------------------- /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#addNode(org.jredis.cluster.ClusterNodeSpec) */ final public void addNode (ClusterNodeSpec nodeSpec) throws IllegalArgumentException { if(supportsReconfiguration()){ synchronized (configLock) { clusterSpec.addNode(nodeSpec); onNodeAddition (nodeSpec); } notifyListeners(new ClusterModel.Event(this, ClusterModel.Event.Type.NodeAdded, nodeSpec)); } else { throw new NotSupportedException("Cluster reconfiguration not supported."); } } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#removeNode(org.jredis.cluster.ClusterNodeSpec) */ final public void removeNode (ClusterNodeSpec nodeSpec) throws IllegalArgumentException { if(supportsReconfiguration()){ if(!clusterSpec.getNodeSpecs().contains(nodeSpec)) throw new IllegalArgumentException("NodeSpec not part of cluster spec!"); synchronized (configLock) { clusterSpec.removeNode(nodeSpec); onNodeAddition (nodeSpec); } notifyListeners(new ClusterModel.Event(this, ClusterModel.Event.Type.NodeRemoved, nodeSpec)); } else { throw new NotSupportedException("Cluster reconfiguration not supported."); } } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#getSpec() */ final public ClusterSpec getSpec () { return clusterSpec; } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#addListener(org.jredis.cluster.ClusterModelListener) */ final public boolean addListener (Listener modelListener) { return listeners.add(modelListener); } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#removeListener(org.jredis.cluster.ClusterModelListener) */ final public boolean removeListener (Listener modelListener) { return listeners.remove(modelListener); } // -------------------------------------------------------------------- // Internal ops // -------------------------------------------------------------------- private final void initialize () { initializeModel(); } private final void notifyListeners(ClusterModel.Event e) { for(ClusterModel.Listener l : listeners) l.onEvent(e); } // -------------------------------------------------------------------- // Extension Points // -------------------------------------------------------------------- /** * @param newNode * @return */ abstract protected boolean onNodeAddition(ClusterNodeSpec newNode); /** * @param newNode * @return */ abstract protected boolean onNodeRemoval(ClusterNodeSpec newNode); /** * */ abstract protected void initializeModel(); } } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/ClusterNodeSpec.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import java.util.Set; import org.jredis.connector.ConnectionSpec; /** * Contract: *
    * Any given pair of {@link ClusterNodeSpec}s with identical ids (per {@link ClusterNodeSpec#getId()}) must * also return identical results for {@link ClusterNodeSpec#getKeyForReplicationInstance(int)} and will be considered equivalent. * Implementations are required to appropriately override {@link Object#equals(Object)} to enforce this contract for use in * Collections (such as {@link Set}. * * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 * */ // this is just a data structure with a few methods - make it a class public interface ClusterNodeSpec { /** * @return */ public ConnectionSpec getConnectionSpec(); /** * All implementations must provide a cluster wide unique {@link String} identifier for a node. * @return */ public String getId (); /** * Each node in a consistent hashing scheme is represented a number of times to satisfy the requirements of the * Consistent Hashing. The implementation must satisfy the global uniqueness of the returned key within the associated * cluster. *

    * This is an optional method, but is required for all implementations supporting Consistent Hashing for sharded clusters. *

    * See Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web * (sec 4). * @param rangeReplicationIndex * @return a globally unique {@link String} key. */ public String getKeyForReplicationInstance (int rangeReplicationIndex); // ------------------------------------------------------------------------ // Reference Implementation // ------------------------------------------------------------------------ /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 * */ public abstract static class Support implements ClusterNodeSpec { // ------------------------------------------------------------------------ // Attrs // ------------------------------------------------------------------------ /** {@link ConnectionSpec} of this node */ final protected ConnectionSpec connSpec; /** Cluster wide unique identifier */ final protected String id; // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ /** * @param connSpec * @throws IllegalArgumentException */ public Support(ConnectionSpec connSpec){ if(null == connSpec) throw new IllegalArgumentException("ConnectionSpec is null"); this.connSpec = connSpec; this.id = generateId(); } // ------------------------------------------------------------------------ // Interface // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ClusterNodeSpec#getConnectionSpec() */ // @Override final public ConnectionSpec getConnectionSpec () { return this.connSpec; } /* (non-Javadoc) @see org.jredis.cluster.ClusterNodeSpec#getId() */ // @Override final public String getId () { return this.id;} // ------------------------------------------------------------------------ // Identity // ------------------------------------------------------------------------ /** * Test for equality to enforce {@link ClusterNodeSpec} identity contract spec, * will compare the ids of the two objects. * @throws IllegalArgumentException if arg is null or not a {@link ClusterNodeSpec} */ @Override final public boolean equals(Object o) { if(null == o) throw new IllegalArgumentException("null argument"); ClusterNodeSpec n = null; try { n = (ClusterNodeSpec) o; } catch (ClassCastException e) { throw new IllegalArgumentException ("object is not a ClusterNodeSpec");} return this.getId().equals(n.getId()); } /** * Enforece {@link ClusterNodeSpec} identity contract by returning the hashcode of the * id. Delegates to {@link String#hashCode()} */ @Override final public int hashCode() { return this.getId().hashCode(); } @Override public String toString() { return this.getId(); } // ------------------------------------------------------------------------ // Extension points // ------------------------------------------------------------------------ /** * Method is called once (and only once) by the constructor to set the * final {@link Support#id} field. This (default) implementation simply * creates a string of form :<0-padded-5-digit-port-number>:<0 padded 2-digit-db-number. *

    * ex: * * "127.0.0.1:06379:02" * *

    * Optional extension point. * @return */ protected abstract String generateId () ; } } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/ClusterSpec.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 * */ // this is just a datastructure with a few methods -- make it a class public interface ClusterSpec { // ------------------------------------------------------------------------ // Node/Key space management and distribution // ------------------------------------------------------------------------ // TODO: may wish to use a simple enum here to spec the cluster type (i.e. ConsistentHashing) // and let the RIs supply a model to suite it. // // /** // * @return the stretegy used to distribute keys across the node space. // */ // public ClusterStrategy getStrategy(); // public ClusterType getType (); public ClusterSpec setType (ClusterType clusterType); // ------------------------------------------------------------------------ // Membership // ------------------------------------------------------------------------ /** * @return */ public Set getNodeSpecs(); /** * @param nodeSpec * @return * @throws IllegalArgumentException if nodeSpec provided is already present. */ public boolean addNode(ClusterNodeSpec nodeSpec); /** * @param nodeSpec * @throws IllegalArgumentException if nodeSpec provided is already present. */ public boolean removeNode (ClusterNodeSpec nodeSpec); /** * Note that the collection must not include a null member. * * @param nodeSpecs is a {@link Collection} instead of a {@link Set} to * relax the type requirements, but the required semantics remains set like, * and duplicate (per {@link ClusterNodeSpec#equals(Object)} elements will * be rejected. * @throws IllegalArgumentException if collection includes a null member * @return per contract of {@link Set#addAll(Collection)} */ public boolean addAll(Collection nodeSpecs); /** * Note that the collection must not include a null member. * * @param nodeSpecs * @throws IllegalArgumentException if collection includes a null member * @return per contract of {@link Set#removeAll(Collection)} */ public boolean removeAll(Collection nodeSpecs); // ======================================================================== // INNER CLASSES // ======================================================================== public abstract static class Support implements ClusterSpec { // /** */ // final protected ClusterModel distributionStrategy; private ClusterType type; /** */ final protected Set nodeSpecs = new HashSet(); // ------------------------------------------------------------------------ // constructor (template) // ------------------------------------------------------------------------ protected Support () { } // ------------------------------------------------------------------------ // interface // ------------------------------------------------------------------------ public ClusterType getType() { return type; } public ClusterSpec setType(ClusterType type) { this.type = type; return this; } /* (non-Javadoc) @see org.jredis.cluster.ClusterSpec#addAll(java.util.List) */ // @Override public boolean addAll (Collection nodes) { if(nodes.contains(null)) throw new IllegalArgumentException("collection includes a null member"); return nodeSpecs.addAll(nodes); } /* (non-Javadoc) @see org.jredis.cluster.ClusterSpec#removeAll(java.util.Collection) */ // @Override public boolean removeAll (Collection nodes) { if(nodes.contains(null)) throw new IllegalArgumentException("collection includes a null member"); return nodeSpecs.removeAll(nodes); } /* (non-Javadoc) @see org.jredis.cluster.ClusterSpec#addNode(org.jredis.cluster.ClusterNodeSpec) */ // @Override public boolean addNode (ClusterNodeSpec nodeSpec) { if(null == nodeSpec) throw new IllegalArgumentException("null nodeSpec"); return this.nodeSpecs.add(nodeSpec); } /* (non-Javadoc) @see org.jredis.cluster.ClusterSpec#removeNode(org.jredis.cluster.ClusterNodeSpec) */ public boolean removeNode (ClusterNodeSpec nodeSpec) { if(null == nodeSpec) throw new IllegalArgumentException("null nodeSpec"); return nodeSpecs.remove(nodeSpec); } // ------------------------------------------------------------------------ // accessors // ------------------------------------------------------------------------ // // /* (non-Javadoc) @see org.jredis.cluster.ClusterSpec#getDistributionStrategy() */ // final public ClusterStrategy getStrategy() { // return this.distributionStrategy; // } /* (non-Javadoc) @see org.jredis.cluster.ClusterSpec#getNodes() */ // @Override final public Set getNodeSpecs () { Set set = new HashSet(nodeSpecs.size()); for(ClusterNodeSpec spec : nodeSpecs) set.add(spec); return set; } // // ------------------------------------------------------------------------ // // Extension points // // ------------------------------------------------------------------------ // protected abstract ClusterModel newStrategy(); } } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/ClusterType.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; /** * We'll enumerate the various cluster types. * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 30, 2010 * */ public enum ClusterType { CONSISTENT_HASH, STATIC_HASH } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/connector/ClusterConnection.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.connector; import java.util.Collection; import org.jredis.NotSupportedException; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; /** * An extension of {@link Connection}, as the interface to a cluster of Redis * servers (nodes). *

    * Given that this interface effectively represents a group of {@link Connection}s, * the implementations must provide a modified semantics regarding the * {@link ConnectionSpec} related methods and properties. *

    *

  • In general, we can not expect all the connections in a given cluster to have * identical {@link ConnectionSpec}s. Calls to {@link Connection#getSpec()} must * throw a {@link NotSupportedException}. *
  • All the underlying {@link Connection}s for the cluster must have the * same {@link Connection.Modality} as that which is returned by the cluster's * {@link Connection#getModality()} method. *

    * Further, some implementations may elect to not support Redis {@link Command}s * which are problematic in context of a cluster, and must accurately indicate * provision of support for {@link Command}s using the * {@link ClusterConnection#supports(Command)} method and * {@link ClusterConnection#getSupportedCommands()}. * * * @author joubin (alphazero@sensesay.net) * @date Apr 3, 2010 * @see Connection * @see ClusterModel * @see ClusterSpec * @see ClusterNodeSpec * @see Command */ public interface ClusterConnection extends Connection{ /** * * @return the {@link ClusterSpec} instance associated with this {@link ClusterConnection}. * The returned results must be identical to that which is returned by the * {@link ClusterModel#getSpec()} of the associated {@link ClusterModel} of this * connection. */ public ClusterSpec getClusterSpec(); /** * @return the {@link ClusterModel} instance associated with this {@link ClusterConnection} */ public ClusterModel getClusterModel(); /** * Indicates whether the specific {@link Command} is supported by this {@link ClusterConnection}. * @param cmd * @return */ public boolean supports (Command cmd); /** * @return the set of {@link Command}s supported by this {@link ClusterConnection} */ public Collection getSupportedCommands (); } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/model/ConsistentHashCluster.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.model; import java.util.SortedMap; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.ClusterType; /** * Generalized interface for a {@link ClusterModel} using a Consistent Hashing strategy, * per the original paper by Karger, et al [1]. *

    * Requirements: *

  • Implementors must return {@link ClusterType#CONSISTENT_HASH} for {@link ClusterSpec#getType()} * * @author joubin (alphazero@sensesay.net) * @date Mar 30, 2010 * @see [1]: Consistent Hashing and Random Trees */ // TODO: extend from a new super DynamicClusterModel and fold all NodeMap event/mod apis to that interface public interface ConsistentHashCluster extends ClusterModel { /** * Return the consistent hash node map. Semantics of the this map are per the consistent hashing strategy paper and Ketama's implementation. * @return * @see ConsistentHashCluster.NodeMap */ NodeMap getNodeMap (); // ======================================================================== // Inner Types // ======================================================================== public interface NodeMap extends SortedMap { } // ------------------------------------------------------------------------ // Support // ------------------------------------------------------------------------ public abstract static class Support extends ClusterModel.Support implements ConsistentHashCluster { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** what is a sensible value here? */ protected static final double DEFAULT_REPLICATION_CONST = 100; /** */ protected NodeMap nodeMap; /** */ protected int nodeReplicationCnt; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * @param clusterSpec */ protected Support (ClusterSpec clusterSpec) { super(clusterSpec); } // ------------------------------------------------------------------------ // Extension points // ------------------------------------------------------------------------ /** * @return a new (un-initialized) instance of {@link ClusterNodeMap}. This instance * will be installed as the class's nodeMap attribute. */ abstract protected NodeMap newClusterNodeMap(); /** * The meats and potatoes of this type of model. Invocation of this method * means a complete computation of the node map per the current {@link ClusterSpec}. */ abstract protected void mapNodes(); /** * TODO: need to change this to getReplicationConstant and * TODO: extend ClusterSpec to allow setting of arbitrary parameters so it can be user configured. * @return */ abstract protected int replicationCount(); /** * Can be overriden, but extending classes MUST call super.initializeComponents() as the * first statement in the overriding method. */ abstract protected void initializeComponents () ; // protected void initializeComponents () { // nodeReplicationCnt = replicationCount(); // nodeMap = newClusterNodeMap(); // } // ------------------------------------------------------------------------ // Inner ops // ------------------------------------------------------------------------ /** * This method will first invoke {@link ConsistentHashCluster.Support#initializeComponents()}, * and then will invoke the abstract {@link ConsistentHashCluster.Support#mapNodes()}. *

    * When this method returns, the cluster model is expected to be ready to service request to * map keys to nodes. * * @see org.jredis.cluster.ClusterModel.Support#initializeModel() */ @Override final protected void initializeModel () { initializeComponents(); // hashAlgo = new KetamaHashProvider(); nodeReplicationCnt = replicationCount(); nodeMap = newClusterNodeMap(); mapNodes(); } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel.Support#onNodeAddition(org.jredis.cluster.ClusterNodeSpec) */ @Override protected boolean onNodeAddition (ClusterNodeSpec newNode) { throw new RuntimeException("not implemented"); } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel.Support#onNodeRemoval(org.jredis.cluster.ClusterNodeSpec) */ @Override protected boolean onNodeRemoval (ClusterNodeSpec newNode) { throw new RuntimeException("not implemented"); } /** * TODO: return the map or clone it? WHY IS METHOD EVEN NECESSARY? * @see org.jredis.cluster.model.ConsistentHashCluster#getNodeMap() * @return ??? */ public NodeMap getNodeMap () { return nodeMap; } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#supports(org.jredis.cluster.ClusterType) */ final public boolean supports (ClusterType type) { return type == ClusterType.CONSISTENT_HASH; } } } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/model/StaticHashCluster.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.model; import java.util.Set; import org.jredis.NotSupportedException; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.ClusterType; import org.jredis.cluster.support.HashAlgorithm; /** * A basic {@link ClusterModel} that uses hashing of keys to distribute keys * on a static set of nodes. This type of cluster model obviously does not * support reconfiguration and brittle in face of server node failures. *

    * Given that, the implementors of interface: *

  • must return false for {@link ClusterModel#supportsReconfiguration()} *
  • must return true for {@link ClusterModel#supports(org.jredis.cluster.ClusterType)} for {@link ClusterType#STATIC_HASH} *
  • must return false for {@link ClusterModel#supports(org.jredis.cluster.ClusterType)} for all other {@link ClusterType}s. *
  • must throw {@link NotSupportedException} for {@link ClusterModel#addNode(org.jredis.cluster.ClusterNodeSpec)} *
  • must throw {@link NotSupportedException} for {@link ClusterModel#removeNode(org.jredis.cluster.ClusterNodeSpec)} * * @author joubin (alphazero@sensesay.net) * @date Mar 30, 2010 * @see ConsistentHashCluster * @see ClusterType */ public interface StaticHashCluster extends ClusterModel { public abstract static class Support extends ClusterModel.Support implements StaticHashCluster { // ------------------------------------------------------------------------ // Props // ------------------------------------------------------------------------ /** */ protected HashAlgorithm hashAlgo; /** */ protected int nodeCnt; /** */ protected ClusterNodeSpec[] nodes; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * @param clusterSpec */ protected Support (ClusterSpec clusterSpec) { super(clusterSpec); } // ------------------------------------------------------------------------ // Extension points // ------------------------------------------------------------------------ /** * Extensions are expected to plugin their specific {@link HashAlgorithm} here. * @return */ abstract protected HashAlgorithm newHashAlgorithm(); // ------------------------------------------------------------------------ // finalized super overrides // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ClusterModel.Support#initializeModel() */ @Override final protected void initializeModel () { hashAlgo = newHashAlgorithm (); Set nodeSpecs = clusterSpec.getNodeSpecs(); nodeCnt = nodeSpecs.size(); nodes = new ClusterNodeSpec[nodeCnt]; nodes = nodeSpecs.toArray(nodes); } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel.Support#onNodeAddition(org.jredis.cluster.ClusterNodeSpec) */ @Override final protected boolean onNodeAddition (ClusterNodeSpec newNode) { throw new NotSupportedException ("node addition"); } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel.Support#onNodeRemoval(org.jredis.cluster.ClusterNodeSpec) */ @Override final protected boolean onNodeRemoval (ClusterNodeSpec newNode) { throw new NotSupportedException ("nodeRemoval"); } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#supports(org.jredis.cluster.ClusterType) */ final public boolean supports (ClusterType type) { return type == ClusterType.STATIC_HASH; } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#supportsReconfiguration() */ final public boolean supportsReconfiguration () { return false; } } } ================================================ FILE: extensions/api/src/main/java/org/jredis/cluster/support/HashAlgorithm.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.support; /** * silly me? Remove this. * * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 * */ public interface HashAlgorithm { public long hash(byte[] kb); } ================================================ FILE: extensions/pom.xml ================================================ 4.0.0 org.jredis jredis ${jredisVersion} JRedis - Extensions [Build POM] org.jredis jredis-extensions ${jredisVersion} pom api ri org.apache.maven.plugins maven-surefire-plugin jredis.cluster.node.cnt100 jredis.cluster.node.address.baselocalhost jredis.cluster.node.port.base6379 Apache 2 http://www.apache.org/licenses/LICENSE-2.0.txt all Copyright 2009-2010 (c) Joubin Houshyar - All Right Resererved joubin Joubin Houshyar alphazero@sensesay.net http://www.sensesay.net ReleaseManager Designer Developer UTC-5 ================================================ FILE: extensions/ri/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: extensions/ri/NOTICE ================================================ Copyright 2009-2012, Joubin Houshyar. All parts of the original works in JReids are licensed under the Apache License ver 2.0 http://www.apache.org/licenses. Each sub-project contains a NOTICE file with additional notices, which may contain additional licensing information. END OF NOTICE ================================================ FILE: extensions/ri/README.markdown ================================================ # Reference implementation of JRedis Extensions Extended functionality for [JRedis] [JRedis]: http://github.com/alphazero/jredis ================================================ FILE: extensions/ri/pom.xml ================================================ 4.0.0 org.jredis jredis-extensions ${jredisVersion} JRedis - Extensions - RI org.jredis jredis-extensions-ri ${jredisVersion} jar org.jredis jredis-core-api ${jredisVersion} org.jredis jredis-core-ri ${jredisVersion} org.jredis jredis-extensions-api ${jredisVersion} org.testng testng 6.1.1 test maven-assembly-plugin 2.1 simple-install package attached jar-with-dependencies ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/DefaultClusterNodeSpec.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster; import java.net.Socket; import java.util.Formatter; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; /** * A basic {@link ClusterNodeSpec} that uses the basic configuration information * (address, port) to generate unique ids and replication instance keys. Note that * while {@link ConnectionSpec} as of JRedis 1.0 distinguishes between DBs, given * that the the Redis 1.3.n compliant library will have stateful connections that would * allow use of SELELCT to change the DB, this class also does NOT distinguish between * two {@link ConnectionSpec}s that are identical but have different DB specification. * * @author joubin (alphazero@sensesay.net) * @date Mar 25, 2010 * */ public class DefaultClusterNodeSpec extends ClusterNodeSpec.Support implements ClusterNodeSpec { // ------------------------------------------------------------------------ // Attrs // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Constructor(s) // ------------------------------------------------------------------------ /** * @param connSpec * @throws IllegalArgumentException */ public DefaultClusterNodeSpec(ConnectionSpec connSpec){ super(connSpec); } // ------------------------------------------------------------------------ // Interface // ------------------------------------------------------------------------ public static ClusterNodeSpec getSpecFor(Socket conn){ if(null == conn) throw new IllegalArgumentException("null arg [conn]"); ConnectionSpec connSpec = DefaultConnectionSpec.newSpec(conn.getInetAddress(), conn.getPort(), 0, null); return new DefaultClusterNodeSpec(connSpec); } // ------------------------------------------------------------------------ // Identity // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // Extension points // ------------------------------------------------------------------------ /** * Method is called once (and only once) by the constructor to set the * final {@link Support#id} field. This (default) implementation simply * creates a string of form :<0-padded-5-digit-port-number>. *

    ex: "127.0.0.1:06379" *

    * Method is not finalized to allow further specializations * @return */ protected String generateId () { Formatter fmt = new Formatter(); fmt.format("%s:%05d", this.connSpec.getAddress().getHostAddress(), this.connSpec.getPort() ); return fmt.toString(); } /** * Default implementation will simply return a string of form * [<@see org.jredis.cluster.ClusterNodeSpec#getKeyForCHRangeInstance(int)>] *

    * Optional extension point. * @param rangeReplicationIndex * @see org.jredis.cluster.ClusterNodeSpec#getKeyForReplicationInstance(int) */ // @Override public String getKeyForReplicationInstance (int rangeReplicationIndex) { Formatter fmt = new Formatter(); fmt.format("%s[%d]", this.id, rangeReplicationIndex); return fmt.toString(); } } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/DefaultClusterSpec.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster; import java.util.Collection; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.ClusterType; import org.jredis.cluster.ClusterSpec.Support; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; /** * The default ClusterSpec uses the {@link KetamaConsitentHashCluster_reture} as its {@link ClusterModel_deprecated}. * * @author joubin (alphazero@sensesay.net) * @date Mar 25, 2010 * */ public class DefaultClusterSpec extends Support implements ClusterSpec { /** Default ClusterSpec.Type is {@link ClusterType#CONSISTENT_HASH} */ public static final ClusterType DEFAULT_CLUSTER_TYPE = ClusterType.CONSISTENT_HASH; // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ public DefaultClusterSpec () { this(null); } public DefaultClusterSpec (Collection nodeSpecs) { super(); this.setType(DEFAULT_CLUSTER_TYPE); if(null != nodeSpecs){ addAll(nodeSpecs); } } // ------------------------------------------------------------------------ // public interface // ------------------------------------------------------------------------ /** * @param host * @param firstPort * @param lastPort * @param templateConnSpec * @return */ public static ClusterSpec newSpecForRange (ConnectionSpec templateConnSpec, int firstPort, int lastPort) { ClusterSpec spec = new DefaultClusterSpec(); for(int i=firstPort; i<=lastPort; i++){ ConnectionSpec connSpec = DefaultConnectionSpec.newSpec() .setAddress(templateConnSpec.getAddress()) .setPort(i) .setDatabase(templateConnSpec.getDatabase()) .setCredentials(templateConnSpec.getCredentials()); ClusterNodeSpec nodeSpec = new DefaultClusterNodeSpec(connSpec); spec.addNode(nodeSpec); } return spec; } // ------------------------------------------------------------------------ // super overrides // ------------------------------------------------------------------------ } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/connection/ClusterConnectionBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster.connection; import static org.jredis.ri.alphazero.support.Assert.*; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.RedisException; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.connector.ClusterConnection; import org.jredis.connector.Connection; import org.jredis.connector.ConnectionSpec; import org.jredis.protocol.Command; import org.jredis.protocol.Response; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ abstract public class ClusterConnectionBase implements ClusterConnection, Connection.Listener { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** */ final protected ClusterModel model; /** */ final private Set supportedCmds = new HashSet(); /** */ final private Map connections = new HashMap(); /** Connector Listeners */ final private Set listeners = new HashSet(); // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * Convenience form for default immediate connection to the nodes. * @param model * @throws ClientRuntimeException * @see {@link ClusterConnectionBase#ClusterConnectionBase(ClusterModel, boolean)} */ protected ClusterConnectionBase (ClusterModel model) throws ClientRuntimeException { this(model, true); } /** * @param model * @param connectImmediately * @throws ClientRuntimeException */ protected ClusterConnectionBase (ClusterModel model, boolean connectImmediately) throws ClientRuntimeException { this.model = notNull(model, "ClusterModel param for constructor", ClientRuntimeException.class); // model integrity checks // ClusterSpec spec = notNull(model.getSpec(), "ClusterModel's ClusterSpec property", ClientRuntimeException.class); Collection nodeSpecs = notNull(spec.getNodeSpecs(), "ClusterSpec's nodes", ClientRuntimeException.class); isTrue(nodeSpecs.size() > 1, "ClusterSpec node count", ClientRuntimeException.class); // initialize cluster's connections initialize(); } // ------------------------------------------------------------------------ // Interface // ===================================================== ClusterConnection /* * General methods of the interface are supported in this base class and the * rest left for the specialized extensions. */ // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.connector.ClusterConnection#getClusterModel() */ final public ClusterModel getClusterModel () { return model; } /* (non-Javadoc) @see org.jredis.cluster.connector.ClusterConnection#getClusterSpec() */ final public ClusterSpec getClusterSpec () { return model.getSpec(); } /* (non-Javadoc) @see org.jredis.connector.Connection#getSpec() */ final public ConnectionSpec getSpec () { throw new NotSupportedException ("Per specification -- see org.jredis.cluster.ClusterConnection's specification."); } /* (non-Javadoc) @see org.jredis.cluster.connector.ClusterConnection#getSupportedCommands() */ final public Collection getSupportedCommands () { return Collections.unmodifiableSet(supportedCmds); } /* (non-Javadoc) @see org.jredis.cluster.connector.ClusterConnection#supports(org.jredis.protocol.Command) */ final public boolean supports (Command cmd) { return supportedCmds.contains(cmd); } /* (non-Javadoc) @see org.jredis.connector.Connection#queueRequest(org.jredis.protocol.Command, byte[][]) */ public Future queueRequest (Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { byte[] key = verifyAndGetKeyForRequest(cmd, args); return getConnectionForKey(key).queueRequest(cmd, args); } /* (non-Javadoc) @see org.jredis.connector.Connection#serviceRequest(org.jredis.protocol.Command, byte[][]) */ public Response serviceRequest (Command cmd, byte[]... args) throws RedisException, ClientRuntimeException, ProviderException { byte[] key = verifyAndGetKeyForRequest(cmd, args); return getConnectionForKey(key).serviceRequest(cmd, args); } // ------------------------------------------------------------------------ // Event management /* (non-Javadoc) @see org.jredis.connector.Connection#addListener(org.jredis.connector.Connection.Listener) */ final public boolean addListener(Listener connListener){ return listeners.add(connListener); } /* (non-Javadoc) @see org.jredis.connector.Connection#removeListener(org.jredis.connector.Connection.Listener) */ final public boolean removeListener(Listener connListener){ return listeners.remove(connListener); } // ------------------------------------------------------------------------ // Interface // =================================================== Connection.Listener /* * TODO: monitor all connections and deal with faults */ // ------------------------------------------------------------------------ public void onEvent(Connection.Event event) { Connection conn = event.getSource(); if(!connections.containsKey(conn)){ throw new ProviderException("ClusterConnection receiving events for unrelated connection!"); } // TODO: deal with it! Connection.Event.Type type = event.getType(); switch (type) { case DISCONNECTED: break; case CONNECTED: break; case FAULTED: break; } throw new ProviderException("[BUG] lazy programmer -- TODOs here!"); } // ------------------------------------------------------------------------ // Internal ops // ------------------------------------------------------------------------ final private byte[] verifyAndGetKeyForRequest(Command cmd, byte[]...args) { notNull(args, "[BUG]: args for request is null!", ProviderException.class); isTrue(args.length > 0, "[BUG]: expecting at least 1 arg for the request (and a key at that)", ProviderException.class); isTrue(supports(cmd), cmd.name() + " is not supported", NotSupportedException.class); return args[0]; } final protected void initialize () throws ClientRuntimeException, ProviderException { mapSupportedCommands(); initializeConnections(); initializeComponents(); } final private void initializeConnections () throws ClientRuntimeException, ProviderException { for(ClusterNodeSpec nodeSpec : model.getSpec().getNodeSpecs()){ Connection conn = null; if(getModality() == Connection.Modality.Synchronous){ conn = notNull(createSynchConnection(nodeSpec), "", ProviderException.class); } else { conn = notNull(createAsynchConnection(nodeSpec), "", ProviderException.class); } connections.put(nodeSpec.getId(), conn); // TODO: add set as listener to connection } } /** * Default implementation simply includes all {@link Command}s with {@link Command.RequestType}s * that include key params in the request. */ final private void mapSupportedCommands () { // filter out the unsupported commands // for(Command cmd : Command.values()){ switch (cmd.requestType){ // -- NOT SUPPORTED -- case BULK_SET: case NO_ARG: case VALUE: if(!affirmLackOfSupportFor(cmd)) supportedCmds.add(cmd); break; // -- SUPPORTED -- case KEY: case KEY_CNT_VALUE: case KEY_IDX_VALUE: case KEY_KEY: case KEY_KEY_VALUE: case KEY_NUM: case KEY_NUM_NUM: case KEY_NUM_NUM_OPTS: case KEY_SPEC: case KEY_VALUE: case MULTI_KEY: if(affirmSupportFor(cmd)) supportedCmds.add(cmd); break; } } } final protected Connection getConnectionForKey(byte[] key){ ClusterNodeSpec nodeSpec = model.getNodeForKey(key); String nodeId = nodeSpec.getId(); // TEMP TEMP TEMP. return connections.get(nodeId); } // ------------------------------------------------------------------------ // Internal ops : Extension points // ------------------------------------------------------------------------ /** * Extension point for subclasses. This method is guaranteed to be called * exactly once during the instantiation process. */ abstract protected void initializeComponents () ; /** * By default returns true. Override to veto default command mappings. * @param cmd * @return */ protected boolean affirmSupportFor (Command cmd) { return true; } /** * By default returns true. Override to veto default command mappings. * @param cmd * @return */ protected boolean affirmLackOfSupportFor (Command cmd) {return true; } /** * @param nodeSpec * @return */ protected Connection createAsynchConnection (ClusterNodeSpec nodeSpec) { throw new ProviderException("Not implemented in the abstract base!"); } /** * @param nodeSpec * @return */ protected Connection createSynchConnection (ClusterNodeSpec nodeSpec) { throw new ProviderException("Not implemented in the abstract base!"); } } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/connection/SynchClusterConnection.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster.connection; import java.util.concurrent.Future; import org.jredis.ClientRuntimeException; import org.jredis.NotSupportedException; import org.jredis.ProviderException; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.connector.Connection; import org.jredis.protocol.Command; import org.jredis.protocol.Response; import org.jredis.ri.alphazero.connection.SynchConnection; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Apr 3, 2010 * */ public class SynchClusterConnection extends ClusterConnectionBase { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** * @param model * @throws ClientRuntimeException */ protected SynchClusterConnection (ClusterModel model, boolean connectImmediately) throws ClientRuntimeException { super(model, connectImmediately); } // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * @param model * @throws ClientRuntimeException */ protected SynchClusterConnection (ClusterModel model) throws ClientRuntimeException { super(model); } /* (non-Javadoc) @see org.jredis.ri.cluster.connection.ClusterConnectionBase#initializeComponents() */ @Override protected void initializeComponents () { // TODO Auto-generated method stub throw new ProviderException("[LAZY] implement me!"); } // ------------------------------------------------------------------------ // Interface // ===================================================== ClusterConnection /* * General methods of the interface are supported in this base class and the * rest left for the specialized extensions. */ // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.connector.Connection#getModality() */ final public Modality getModality () { return Connection.Modality.Synchronous; } /* (non-Javadoc) @see org.jredis.connector.Connection#queueRequest(org.jredis.protocol.Command, byte[][]) */ final public Future queueRequest (Command cmd, byte[]... args) throws ClientRuntimeException, ProviderException { throw new NotSupportedException("Not supported by abstract base class"); } // ------------------------------------------------------------------------ // Super overrides // ------------------------------------------------------------------------ /** * @param nodeSpec * @return */ protected Connection createSynchConnection (ClusterNodeSpec nodeSpec) { Connection conn = null; conn = new SynchConnection(nodeSpec.getConnectionSpec(), true); return conn; } } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/model/BasicStaticHashCluster.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster.model; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.model.StaticHashCluster; import org.jredis.cluster.support.HashAlgorithm; /** * As barebones as it gets. Uses the key's hashCode() (that is {@link byte[]#hashCode()}) * to compute a node index, using a basic hashcode % nodeCnt as the index to the nodes list. * @author joubin (alphazero@sensesay.net) * @date Mar 30, 2010 * */ public class BasicStaticHashCluster extends StaticHashCluster.Support implements StaticHashCluster { // ------------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------------ /** * @param clusterSpec */ public BasicStaticHashCluster (ClusterSpec clusterSpec) { super(clusterSpec); } // ------------------------------------------------------------------------ // super overrides // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.model.StaticHashCluster.Support#newHashAlgorithm() */ @Override protected HashAlgorithm newHashAlgorithm () { // TDOO: get this from the clusterspec return new HashAlgorithm() { public long hash (byte[] kb) { return kb.hashCode(); } }; } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#getNodeForKey(byte[]) */ public ClusterNodeSpec getNodeForKey (byte[] key) { int nodeIdx = (int) (hashAlgo.hash(key)%nodeCnt); return nodes[nodeIdx]; } } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/model/KetamaClusterModel.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster.model; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.jredis.ProviderException; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; //import org.jredis.cluster.model.ClusterNodeMap; import org.jredis.cluster.model.ConsistentHashCluster; import org.jredis.ri.alphazero.support.Log; import org.jredis.ri.cluster.support.CryptoHashUtils; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ public class KetamaClusterModel extends ConsistentHashCluster.Support implements ConsistentHashCluster { // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** */ protected KetamaHashProvider hashAlgo; // ------------------------------------------------------------------------ // Properties // ------------------------------------------------------------------------ /** * Instantiate and initialize the node mape of a Ketama-based {@link ClusterModel}. * @param clusterSpec */ public KetamaClusterModel (ClusterSpec clusterSpec) { super(clusterSpec); } // ------------------------------------------------------------------------ // Interface // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#getNodeForKey(byte[]) */ public ClusterNodeSpec getNodeForKey (byte[] key) { long hash = hashAlgo.hash(key); final ClusterNodeSpec rv; if(!nodeMap.containsKey(hash)) { // Java 1.6 adds a ceilingKey method, but I'm still stuck in 1.5 // in a lot of places, so I'm doing this myself. SortedMap tailMap=nodeMap.tailMap(hash); if(tailMap.isEmpty()) { hash = nodeMap.firstKey(); } else { hash=tailMap.firstKey(); } } rv = nodeMap.get(hash); return rv; } // /** // * TODO: return the map or clone it? WHY IS METHOD EVEN NECESSARY? // * @see org.jredis.cluster.model.ConsistentHashCluster#getNodeMap() // * @return ??? // */ // public NodeMap getNodeMap () { // return nodeMap; // } /* (non-Javadoc) @see org.jredis.cluster.ClusterModel#supportsReconfiguration() */ public boolean supportsReconfiguration () { return false; } // ------------------------------------------------------------------------ // Inner Ops // ------------------------------------------------------------------------ /** * Per original paper on consistent hashing, the replication count of any given bucket is * k*log(C), where C is the number of buckets (i.e. nodes). We're using {@link KetamaNodeMapper#DEFAULT_REPLICATION_CONST} * as k. * * @param nodeCnt number of server nodes ("buckets" per original paper) in the Ketama cluster * @return */ @Override final protected int replicationCount(){ int nodeCnt = clusterSpec.getNodeSpecs().size(); return (int) (Math.log(nodeCnt) * DEFAULT_REPLICATION_CONST); } @Override final protected NodeMap newClusterNodeMap() {return new KetamaNodeMap(); } @Override final protected void initializeComponents() { // super.initializeComponents(); hashAlgo = new KetamaHashProvider(); } /** * This method is a slightly modified version of net.spy.memcached.KetamaNodeLocator's constructor. * @see GIT HUB LINK HERE ... */ @Override final protected void mapNodes () { try { Set nodes = clusterSpec.getNodeSpecs(); for(ClusterNodeSpec node : nodes) { mapNode(node); } if(nodeMap.size() != (nodeReplicationCnt/4) * nodes.size() * 4) { Log.error("nodeMap size: " + nodeMap.size() + " | expected: " + nodeReplicationCnt * nodes.size()); throw new ProviderException ("[BUG]: expecting node map size to be multiple of replication count * cluster node count"); } } catch (ClassCastException e) { throw new ProviderException ("[BUG] KetamaNodeMappingAlgorithm requires a KetamaHashAlgorithm"); } } private boolean mapNode(ClusterNodeSpec node){ // Dustin says: "Ketama does some special work with md5 where it reuses chunks." for(int i=0; i implements SortedMap{ public class KetamaNodeMap extends TreeMap implements ConsistentHashCluster.NodeMap { // TODO: iterator, etc. } } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/model/KetamaHashProvider.java ================================================ /* -- BEGIN NOTICE -- * * This class uses in parts extant and/or modified code from net.spy.memcached.HashAlgorithm * by Dustin Sallings. See this module's 3rd party license folder for license details. * * -- END NOTICE -- * * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster.model; import org.jredis.ClientRuntimeException; import org.jredis.cluster.support.HashAlgorithm; import org.jredis.ri.cluster.support.CryptoHashUtils; /** * The Ketama consistent hash algorithm as implemented by Dustin Sallings, * with some minor (non-algorithmic) modifications. *

    * Note that if certain expected cryptographic algorithms expected to be * present in your JRE are not available, {@link ClientRuntimeException}s * will be thrown. * * @author joubin (alphazero@sensesay.net) * @date Mar 25, 2010 * */ public class KetamaHashProvider implements HashAlgorithm { // ------------------------------------------------------------------------ // Interface // ------------------------------------------------------------------------ /** * Uses MD5 digest. *

    * Contains code from net.spy.memecached. * @ Copyright (c) 2006-2009 Dustin Sallings * * @param b bytes to be hashed */ // @Override public long hash (byte[] b) { if(null == b || b.length ==0) throw new IllegalArgumentException(); /* Copyright (c) 2006-2009 Dustin Sallings */ /* -- BEGIN code segment */ byte[] kb; long rv = 0; kb = CryptoHashUtils.computeMd5(b); rv = ((long) (kb[3] & 0xFF) << 24) | ((long) (kb[2] & 0xFF) << 16) | ((long) (kb[1] & 0xFF) << 8) | (kb[0] & 0xFF); /* -- END code segment */ return rv; } // ------------------------------------------------------------------------ // Ketama specific and for Ketama package only // ------------------------------------------------------------------------ /** * Rip of the inner loop of the KetamaNodeLocator's constructor, in full. *

    * @ Copyright (c) 2006-2009 Dustin Sallings * * @param digest * @param h * @return */ long hash (byte[] digest, int h) { return ((long)(digest[3+h*4]&0xFF) << 24) | ((long)(digest[2+h*4]&0xFF) << 16) | ((long)(digest[1+h*4]&0xFF) << 8) | (digest[h*4]&0xFF); } } ================================================ FILE: extensions/ri/src/main/java/org/jredis/ri/cluster/support/CryptoHashUtils.java ================================================ /* -- BEGIN NOTICE -- * * This class uses in parts extant and/or modified code from net.spy.memcached.HashAlgorithm * by Dustin Sallings. See this module's 3rd party license folder for license details. * * -- END NOTICE -- * * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.ri.cluster.support; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.jredis.ClientRuntimeException; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 26, 2010 * */ public class CryptoHashUtils { /** * Get the md5 of the given key. * @throws ClientRuntimeException if MD5 algorithm is not supported. * @throws IllegalArgumentException if input is null or zero length * * @Copyright (c) 2006-2009 Dustin Sallings */ public static byte[] computeMd5(byte[] b) throws ClientRuntimeException{ if(null == b) throw new IllegalArgumentException ("null input"); if(b.length == 0) throw new IllegalArgumentException ("zero length input"); MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); md5.reset(); md5.update(b); } catch (NoSuchAlgorithmException e) { throw new ClientRuntimeException("MD5 Message Digest algorithm is not present in this JRE", e); } return md5.digest(); } /** * @param s * @return * @throws ClientRuntimeException * @throws IllegalArgumentException if input is null or zero length */ public static byte[] computeMd5(String s) throws ClientRuntimeException{ if(null == s) throw new IllegalArgumentException ("null input"); return computeMd5(s.getBytes()); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/ClusterModelProviderTestBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import java.util.HashMap; import java.util.Map; import org.jredis.NotSupportedException; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.jredis.ri.cluster.DefaultClusterNodeSpec; import org.jredis.test.util.RunningAverage; import org.testng.annotations.Test; import static org.jredis.cluster.ClusterSuiteTestData.*; import static org.testng.Assert.*; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ //@Test(suiteName="extensions-cluster-model") public abstract class ClusterModelProviderTestBase extends RefImplTestSuiteBase { // ------------------------------------------------------------------------ // Extension point // ------------------------------------------------------------------------ /** * @param clusterSpec * @return */ protected abstract ClusterModel newClusterModel (ClusterSpec clusterSpec) ; /** * @return a {@link ClusterSpec} suitable for instantiating a provider instance */ protected abstract ClusterSpec newClusterSpec () ; /** * @return the ClusterSpec.Type that the provider is supposed to support */ protected abstract ClusterType getSupportedClusterType (); // ------------------------------------------------------------------------ // Specification Interface tested // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ProviderTestBase#getSpecificationClass() */ @Override protected Class getSpecificationClass () { return ClusterModel.class; } /* (non-Javadoc) @see org.jredis.cluster.ProviderTestBase#newProviderInstance() */ @Override protected ClusterModel newProviderInstance () { return newClusterModel(newClusterSpec()); } // ------------------------------------------------------------------------ // Test general contract of SPECS for Cluster and its Nodes // ------------------------------------------------------------------------ @Test public void metaTest (){ Log.log("[META] test the test suite assumptions!"); ClusterType clusterType = getSupportedClusterType(); assertNotNull(clusterType, "getSupportedClusterType"); ClusterSpec testSpec = newClusterSpec(); assertNotNull(testSpec, "newClusterSpec should not return null"); assertEquals(testSpec.getType(), clusterType, "asserted supported cluster type and type from the newClusterSpec should be the same"); ClusterModel model = newProviderInstance(); assertNotNull(model, "newProviderInstance should not return null"); } @Test public void testClusterSpecPropertyOps () { Log.log("test ClusterSpec accessors"); // null spec arg on construct must raise an error // boolean didRaiseEx; didRaiseEx = false; try { @SuppressWarnings("unused") ClusterModel m = newClusterModel(null); } catch (IllegalArgumentException e) { didRaiseEx = true; } catch (RuntimeException whatsthis) { fail("unexpected exception raised during op", whatsthis); } assertTrue(didRaiseEx, "IllegalArgumentException raise is expected"); // spec arg with no nodes on construct is not an error if // model supports reconfiguration // boolean supportsReconfig = newProviderInstance().supportsReconfiguration(); didRaiseEx = false; try { ClusterSpec s = newClusterSpec(); s.removeAll(s.getNodeSpecs()); assertTrue(s.getNodeSpecs().size() == 0, "cluster spec should have no node specs now"); newClusterModel(s); } catch (IllegalArgumentException e) { didRaiseEx = true; } catch (RuntimeException whatsthis) { fail("unexpected exception raised during op", whatsthis); } assertTrue(didRaiseEx && !supportsReconfig , "expected only if non reconfigurable"); ClusterSpec spec = newClusterSpec(); ClusterModel model = newClusterModel(spec); assertNotNull(newProviderInstance().getSpec(), "clusterSpec property should be non-null"); assertEquals(model.getSpec(), spec, "expecting returned property to be the spec used in constructor"); } @Test public void testReconfigOfNonReconfigurableModel () { Log.log("test reconfiguration for static configuration models"); // skip the test if not applicable to this provider // if(provider.supportsReconfiguration()) { Log.log("Skipping test; not applicable."); return; } // create a new model, get its spec, and pick one node to remove // this should raise an exception ClusterModel model = newProviderInstance(); ClusterSpec clusterSpec = provider.getSpec(); ClusterNodeSpec nodeSpec = null; for(ClusterNodeSpec n : clusterSpec.getNodeSpecs()){ nodeSpec = n; break; } boolean didRaiseEx; didRaiseEx = false; try { model.removeNode(nodeSpec); } catch (NotSupportedException e) { didRaiseEx = true; } catch (RuntimeException whatsthis) { fail("unexpected exception raised during op", whatsthis); } assertTrue(didRaiseEx, "NotSupportedException raise is expected"); // so far so good. // now lets add a node didRaiseEx = false; try { model.addNode(new DefaultClusterNodeSpec(DefaultConnectionSpec.newSpec())); } catch (NotSupportedException e) { didRaiseEx = true; } catch (RuntimeException whatsthis) { fail("unexpected exception raised during op", whatsthis); } assertTrue(didRaiseEx, "NotSupportedException raise is expected"); } @Test public void testReconfigOfReconfigurableModel () { Log.log("test reconfiguration for dynamic configuration models"); // skip the test if not applicable to this provider // if(!provider.supportsReconfiguration()) { Log.log("Skipping test; not applicable."); return; } // create a new model, get its spec, and pick one node to remove // ClusterModel model = newProviderInstance(); ClusterSpec clusterSpec = provider.getSpec(); ClusterNodeSpec nodeSpec = null; for(ClusterNodeSpec n : clusterSpec.getNodeSpecs()){ nodeSpec = n; break; } model.removeNode(nodeSpec); // now lets add a node // model.addNode(new DefaultClusterNodeSpec(DefaultConnectionSpec.newSpec().setPort(9999))); } @Test public void testKeyDistribution (){ long keycnt = data.MEDIUM_CNT; Log.log("test key distribution with " + keycnt + " keys"); // get the node for a number of keys and check the distribution // across the nodes. Very difficult to have a definitive tests here // without spec'ing distribution metric (e.g. limits on std-dev), but // at least all nodes should have keys asigned to them, as a general start. Map distribution = new HashMap(); ClusterModel model = newProviderInstance(); assertNotNull(model, "newProviderInstance should not return null"); // we simply count the keys assigned to each node // for(int i=0; i 0, "No node should have zero keys assigned to it"); assertTrue(avg.getMax() > 0, "No node should have zero keys assigned to it"); Log.log("Distributed %d keys in a %d node cluster:\n\t Key/node distribution -- AVG: %d - MIN: %d - MAX: %d\n", keycnt, nodeCnt, avg.get(), avg.getMin(), avg.getMax()); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/ClusterNodeSpecProviderTestBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import java.util.HashSet; import java.util.Set; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 */ // TODO: look into the data provider attrib of the annotation ... @Test(suiteName="extensions-cluster-specs-1") abstract public class ClusterNodeSpecProviderTestBase extends RefImplTestSuiteBase { // ------------------------------------------------------------------------ // Specification Interface tested // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ProviderTestBase#getSpecificationClass() */ protected final Class getSpecificationClass () { return ClusterNodeSpec.class; } /** * Ok, so its a hack. (TODO: add a context param to this method in super). * @param connectionSpec * @return */ protected abstract ClusterNodeSpec newProviderInstance (ConnectionSpec connectionSpec) ; // ------------------------------------------------------------------------ // Test general contract of SPECS for Cluster and its Nodes // ------------------------------------------------------------------------ @Test public void testIdentityContract () { Log.log("Testing ClusterNodeSpec identity contract enforcement: [Object.equals() | Object.hashCode()]"); int db = 10; int anotherDb = 2; ConnectionSpec node1Spec = DefaultConnectionSpec.newSpec("127.0.0.1", 6379, db, null); ConnectionSpec node2Spec = DefaultConnectionSpec.newSpec("127.0.0.1", 6379, db, null); ConnectionSpec node3Spec = DefaultConnectionSpec.newSpec("127.0.0.1", 6379, anotherDb, null); ClusterNodeSpec node1 = newProviderInstance(node1Spec); ClusterNodeSpec node2 = newProviderInstance(node2Spec); ClusterNodeSpec node3 = newProviderInstance(node3Spec); assertTrue(node1.getId().equals(node2.getId()), "ids should be identical"); assertTrue(node1.hashCode() == node2.hashCode(), "hashCodes should be equal"); assertTrue(node1.equals(node2), "nodes must be considered equivalent"); assertTrue(node2.equals(node1), "nodes must be considered equivalent [transitive test]"); // test equivalence of DB# of node's ConnectionSpec in identity tests // these should all pass assertTrue(node1.getId().equals(node3.getId()), "ids should be identical"); assertTrue(node1.hashCode() == node3.hashCode(), "hashCodes should be equal"); assertTrue(node1.equals(node3), "nodes must be considered equivalent"); assertTrue(node3.equals(node1), "nodes must be considered equivalent [transitive test]"); // now lets raise some errors boolean didRaiseError; // test arg constraint checking // didRaiseError = false; ClusterNodeSpec nullRef = null; try { node1.equals(nullRef); } catch (IllegalArgumentException e){ didRaiseError = true; } if(!didRaiseError) fail("Expecting an IllegalArgumentException raised for null input arg to equals()"); didRaiseError = false; Object foo = new Object(); try { node1.equals(foo); } catch (IllegalArgumentException e){ didRaiseError = true; } if(!didRaiseError) fail("Expecting an IllegalArgumentException raised for invalid object type arg to equals()"); } /** * We have the full port range of an IPv4 address as our clusterNodes. Here * we make sure the ids are unique. */ @Test public void testIdGeneration () { Log.log("Testing ClusterNodeSpec.getId() ..."); Set generatedIdSet = new HashSet(data.connSpecs.length); Log.log("... testing a cluster with member cnt: " + data.connSpecs.length); for(ConnectionSpec connSpec : data.connSpecs){ ClusterNodeSpec nodeSpec = newProviderInstance(connSpec); String nodeId = nodeSpec.getId(); assertTrue(generatedIdSet.add(nodeId), "generated ID should be unique but was not: " + nodeId); } } /** * We test the Consitent Hash Key for uniqueness. Testing the full port range * reasonable_inst_cnt will * exhaust the memory so we'll limit to a subset of ports. */ @Test public void testGetKeyForCHRangeInstance () { int instanceCnt = 100; int nodeCnt = 100; Log.log("Testing CHRange key uqniueness for "+instanceCnt+" instances in the ring... this will take a while! (TODO: cnt should be a parameter!)"); Set chRangeKeys = new HashSet(nodeCnt*instanceCnt); int n = 0; for(ConnectionSpec connSpec : data.connSpecs){ ClusterNodeSpec nodeSpec = newProviderInstance(connSpec); for(int i=0; i 100) break; } } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/ClusterSpecProviderTestBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import java.util.Collection; import java.util.HashSet; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 */ //TODO: look into the data provider attrib of the annotation ... @Test(suiteName="extensions-cluster-specs-2") abstract public class ClusterSpecProviderTestBase extends RefImplTestSuiteBase { // ------------------------------------------------------------------------ // Extension point // ------------------------------------------------------------------------ protected abstract ClusterNodeSpec newNodeSpec (ConnectionSpec connectionSpec) ; // ------------------------------------------------------------------------ // Specification Interface tested // ------------------------------------------------------------------------ protected final Class getSpecificationClass () { return ClusterSpec.class; } // ------------------------------------------------------------------------ // Test general contract of SPECS for Cluster and its Nodes // ------------------------------------------------------------------------ @Test public void testGetType() { Log.log("Testing ClusterSpec.getType()"); assertNotNull(provider.getType(), "getType() must never return null"); } @Test public void testSetType() { Log.log("Testing ClusterSpec.setType()"); // Note: don't use provider instance as we are changing various settings here // and don't want to break assumptions down the hierarchy chain. ClusterType clusterType = null; ClusterSpec clusterSpec = newProviderInstance(); ClusterType prevType = clusterSpec.getType(); assertNotNull(prevType, "getType() must never return null"); // just pick something else for(ClusterType type : ClusterType.values()){ if(type != prevType){ clusterType = type; break; } } assertNotNull(clusterType, "[BUG] why couldn't we find another different type?"); // use the setter and test various requirements. ClusterSpec chainedRes = clusterSpec.setType(clusterType); testChainedResult(chainedRes, clusterSpec); assertEquals(clusterSpec.getType(), clusterType, "getType() result must match the ref used for setType()"); } @Test public void testAddAndRemoveAll() { Log.log("Testing ClusterSpec addAll() | removeAll()"); ClusterSpec clusterSpec = newProviderInstance(); Log.log("Test with cluster spec with %d node specs ..", clusterSpec.getNodeSpecs().size()); Collection nodes = new HashSet(); for(int i=0; i<10; i++){ nodes.add(newNodeSpec(data.connSpecs[i])); } assertTrue(clusterSpec.addAll(nodes), "addAll should return true"); assertFalse(clusterSpec.addAll(nodes), "dup addAll should return false"); assertTrue(clusterSpec.removeAll(nodes), "removeAll should return true"); assertFalse(clusterSpec.removeAll(nodes), "second removeAll should return false"); // test constraint against collections with a null member nodes.clear(); assertTrue(nodes.add(null), "collection w null input"); for(int i=0; i<10; i++) { nodes.add(newNodeSpec(data.connSpecs[i])); } assertTrue(nodes.contains(null), "collection has a null input"); boolean didRaiseEx; didRaiseEx = false; try { clusterSpec.addAll(nodes); } catch (IllegalArgumentException e) { didRaiseEx = true; } catch (RuntimeException whatsthis) { fail("unexpected exception raised during op", whatsthis); } assertTrue(didRaiseEx, "IllegalArgumentException raise is expected"); didRaiseEx = false; try { clusterSpec.removeAll(nodes); } catch (IllegalArgumentException e) { didRaiseEx = true; } catch (RuntimeException whatsthis) { fail("unexpected exception raised during op", whatsthis); } assertTrue(didRaiseEx, "IllegalArgumentException raise is expected"); } @Test public void testRemoveNodeSpec() { Log.log("Testing ClusterSpec.addNodeSpec()"); ClusterSpec clusterSpec = newProviderInstance(); for(int i=0; i<10; i++){ ClusterNodeSpec nodeSpec = newNodeSpec(data.connSpecs[i]); assertTrue(clusterSpec.addNode(nodeSpec)); } for(int i=0; i<10; i++){ ClusterNodeSpec nodeSpec = newNodeSpec(data.connSpecs[i]); assertTrue(clusterSpec.removeNode(nodeSpec)); } } @Test public void testAddNodeSpec() { Log.log("Testing ClusterSpec.addNodeSpec()"); ClusterSpec clusterSpec = newProviderInstance(); ClusterNodeSpec nodeSpec = null; nodeSpec = newNodeSpec(data.connSpecs[0]); assertTrue(clusterSpec.addNode(nodeSpec)); nodeSpec = newNodeSpec(data.connSpecs[1]); assertTrue(clusterSpec.addNode(nodeSpec)); // now lets raise some errors boolean didRaiseError; // should not allow adding of duplicate ClusterNodeSpecs didRaiseError = false; assertTrue(clusterSpec.addNode(newNodeSpec(data.defRedisDb10Port7777ConnSpec)), "add of unique spec should be possible and must return the clusterSpec instance"); assertFalse(clusterSpec.addNode(newNodeSpec(data.defRedisDb10Port7777ConnSpec_dup)), "add of duplicate spec is expected to raise a runtime exception"); // should not allow adding of null specs didRaiseError = false; ClusterNodeSpec nullRef = null; try { clusterSpec.addNode(nullRef); } catch (IllegalArgumentException e){ didRaiseError = true; } if(!didRaiseError) fail("Expecting an IllegalArgumentException raised for null input arg to add()"); } // ------------------------------------------------------------------------ // helper methods // ------------------------------------------------------------------------ private final void testChainedResult (ClusterSpec res, ClusterSpec expected) { assertNotNull(res, "fluent interface setters must return non null values"); assertEquals(res, expected, "setter result must be the same reference as the original"); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/ClusterSuiteTestData.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import static org.testng.Assert.fail; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; /** * We'll use a singleton for our test data to minimize jvm heap impact * of the potentially very large test data. *

    * This class uses the Reference Implementation classes where necessary. Implementations * are required to be interoperable and this should not present a problem for testing other * providers of the JReds Cluster specs. * * @author joubin (alphazero@sensesay.net) * @date Mar 27, 2010 */ public class ClusterSuiteTestData { /** singleton instance - otherwise multiple test runs kill the jvm heap */ static private final ClusterSuiteTestData instance = new ClusterSuiteTestData(); // ------------------------------------------------------------------------ // General RI Test Suite Parameters with default values to avoid XML // ------------------------------------------------------------------------ // data size final public int SMALL_DATA = 128; final public int MEDIUM_DATA = 1024 * 2; final public int LARGE_DATA = 1024 * 512; // iterations final public int SMALL_CNT = 100; final public int MEDIUM_CNT = 10000; final public int LARGE_CNT = 1000000; final public int NODE_CNT = MEDIUM_CNT; final public String CLUSTER_NODES_ADDRESS_BASE = "127.0.0.1"; final public int CLUSTER_NODES_PORT_BASE = 6379; final public int db = 10; // ------------------------------------------------------------------------ // General RI Test Suite test data // ------------------------------------------------------------------------ // public final Set clusterNodeSpecs = new HashSet(); public ConnectionSpec[] connSpecs; // public final ClusterNodeSpec[] clusterNodeSpecsArray; // public ClusterNodeSpec defaultRedisWithDb10ClusterNodeSpec; // public ClusterNodeSpec defaultRedisWithDb10ClusterNodeSpec_dup; public ConnectionSpec defRedisDb10Port7777ConnSpec; public ConnectionSpec defRedisDb10Port7777ConnSpec_dup; public Map dataMap = new HashMap(); // ------------------------------------------------------------------------ // Access // ------------------------------------------------------------------------ /** * @return the singleton instance of {@link ClusterSuiteTestData} */ public static ClusterSuiteTestData getInstance () { return instance; } // ------------------------------------------------------------------------ // Const // ------------------------------------------------------------------------ private ClusterSuiteTestData () { // ClusterNodeSpec nodeSpec = null; connSpecs = new ConnectionSpec[NODE_CNT]; for(int i=0;i", e); } return address; } static public ConnectionSpec getConnectionSpecFor (InetAddress address, int port, int db) { ConnectionSpec connSpec = DefaultConnectionSpec.newSpec().setAddress(address).setPort(port).setDatabase(db); return connSpec; } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/ProviderTestBase.java ================================================ /* * Copyright 2009 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import org.jredis.ClientRuntimeException; //import org.jredis.JRedis; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.BeforeTest; import static org.testng.Assert.*; /** * Support for tests of interface T implementation providers. * * @author Joubin Houshyar (alphazero@sensesay.net) * @version alpha.0, Oct 10, 2009 * @since alpha.0 * */ public abstract class ProviderTestBase { // ======================================================================== // Test Properties // ======================================================================== /** the JRedis implementation being tested */ protected T provider = null; protected Class specClass = null; // ------------------------------------------------------------------------ // JRedisFuture Provider initialize methods // ------------------------------------------------------------------------ /** * Sets the {@link JRedis} implementation provider for the test suite */ @BeforeTest public void initialize () { try { specClass = getSpecificationClass(); assertNotNull(specClass, "getSpecificationClass() returned null"); provider = newProviderInstance(); assertNotNull(provider, "newProviderInstance() returned null"); Log.log("\n\nTEST: " + "\n\t-----------------------------------------------\n" + "\tSpec Interface: %s\n" + "\tProvider Class: %s" + "\n\t-----------------------------------------------\n", specClass.getCanonicalName(), provider.getClass().getCanonicalName()); } catch (ClientRuntimeException e) { Log.error(e.getLocalizedMessage()); } } /** * Extension point: Tests for specific implementations of T * implement this method to create the provider instance. * @return T implementation instance */ protected abstract T newProviderInstance () ; /** * @return */ protected abstract Class getSpecificationClass () ; } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/RefImplTestSuiteBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster; import org.testng.Assert; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Parameters; /** * As base for all org.jredis.cluster[...] tests, this class is mainly responsible * for *

  • [-TODO] initialize the test suite parameters through testNG injection from module's pom *
  • [-TODO] get and initialize the test data singleton *
  • providing any required helper methods * * @author joubin (alphazero@sensesay.net) * @date Mar 24, 2010 */ public abstract class RefImplTestSuiteBase extends ProviderTestBase { // ------------------------------------------------------------------------ // General RI Test Suite Parameters with default values to avoid XML // ------------------------------------------------------------------------ protected int NODE_CNT = 10; protected String CLUSTER_NODES_ADDRESS_BASE = "127.0.0.1"; protected int CLUSTER_NODES_PORT_BASE = 6379; /** data holds the ref to a singleton class with all the test data */ protected ClusterSuiteTestData data; @Parameters({ // "jredis.test.password", // "jredis.test.host", // "jredis.test.port", // "jredis.test.db.1", // "jredis.test.db.2", // // "jredis.test.datasize.small", // "jredis.test.datasize.medium", // "jredis.test.datasize.large", // "jredis.test.cnt.small", // "jredis.test.cnt.medium", // "jredis.test.cnt.large", // "jredis.test.expire.secs", // "jredis.test.expire.wait.millisecs" "jredis.cluster.node.cnt", "jredis.cluster.node.address.base", "jredis.cluster.node.port.base" }) @BeforeSuite public void suiteParametersInit( // String password, // String host, // int port, // int db1, // int db2, int nodecnt, String nodesAddressBase, int nodesPortBase ) { NODE_CNT = nodecnt; CLUSTER_NODES_ADDRESS_BASE = nodesAddressBase; CLUSTER_NODES_PORT_BASE = nodesPortBase; // Log.log("nodecnt: %d", NODE_CNT); // Log.log("cluster nodes address base: %s", CLUSTER_NODES_ADDRESS_BASE); // Log.log("cluster nodes port base: %d", CLUSTER_NODES_PORT_BASE); // Log.log("nodecnt: %d", NODE_CNT); // Log.log("[extensions.cluster] Suite parameters initialized "); data = setupTestSuiteData(); Assert.assertNotNull(data, "ClusterSuiteTestData instance obtained is null"); } /** * a do nothing method - keeping it around in case I change my mind about using the * singleton test data class. * @return */ private ClusterSuiteTestData setupTestSuiteData () { return ClusterSuiteTestData.getInstance(); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/models/BasicStaticHashClusterTest.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.models; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.jredis.ri.cluster.DefaultClusterNodeSpec; import org.jredis.ri.cluster.DefaultClusterSpec; import org.jredis.ri.cluster.model.BasicStaticHashCluster; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 31, 2010 * */ @Test(suiteName="basic static hash") public class BasicStaticHashClusterTest extends StaticHashClusterProviderTestBase { // ------------------------------------------------------------------------ // super overrides // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#newClusterModel(org.jredis.cluster.ClusterSpec) */ @Override protected ClusterModel newClusterModel (ClusterSpec clusterSpec) { ClusterModel model = null; try { model = new BasicStaticHashCluster(clusterSpec); } catch (RuntimeException e){ Log.error("NOTE: propagating error > " + e.getLocalizedMessage()); throw e; } return model; } /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#newClusterSpec() */ @Override protected ClusterSpec newClusterSpec () { ClusterSpec spec = new DefaultClusterSpec(); for(int i=0; i<100; i++){ ClusterNodeSpec node = new DefaultClusterNodeSpec(DefaultConnectionSpec.newSpec("localhost", 6379+i, 0, null)); spec.addNode(node); } return spec; } // ------------------------------------------------------------------------ // BasicStaticHashCluster specific tests // ------------------------------------------------------------------------ @Test public void fooTest() { Log.log("Foo test for BasicStaticHash"); assertTrue(true); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/models/ConsistentHashClusterProviderTestBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.models; import java.util.Collection; import org.jredis.cluster.ClusterModelProviderTestBase; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.ClusterType; import org.jredis.cluster.model.ConsistentHashCluster; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 30, 2010 * */ public abstract class ConsistentHashClusterProviderTestBase extends ClusterModelProviderTestBase { /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#getSupportedClusterType() */ @Override protected ClusterType getSupportedClusterType () { return ClusterType.CONSISTENT_HASH; } // ------------------------------------------------------------------------ // ConsistentHashCluster generic tests // ------------------------------------------------------------------------ @Test public void compatibilityTest() { Log.log("Test provider support for Consistent Hashing"); assertNotNull(provider, "provider is null!"); assertTrue(provider.supports(ClusterType.CONSISTENT_HASH), "A ConsistentHashCluster model must support Type.CONSISTENT_HASH"); } @Test public void basicNodeMapTest() { Log.log("Basic nodemap test of Consistent Hashing cluster model"); ConsistentHashCluster model = (ConsistentHashCluster) newProviderInstance(); assertNotNull(model, "model should not be null"); ClusterSpec spec = model.getSpec(); assertNotNull(spec, "spec should not be null"); ConsistentHashCluster.NodeMap nodeMap = model.getNodeMap(); assertNotNull(nodeMap, "node map should not be null"); Collection nodes = nodeMap.values(); assertNotNull(nodes, "value set of node map should not be null"); // regardless of what hash (key) they are mapped to, // we expected the value set of NodeMap to contain each and every // ClusterNodeSpec in the ClusterSpec of the model // Log.log("NOTE: %d nodes", spec.getNodeSpecs().size()); Log.log("NOTE: size of value set is %d ", nodes.size()); int missCnt = 0; for(ClusterNodeSpec node : spec.getNodeSpecs()){ if(!nodes.contains(node)){ missCnt ++; } // assertTrue(nodes.contains(node), "nodeSpec should be in the value set of NodeMap: " + node); } Log.log("BUG: missing %d nodes!", missCnt); assertEquals(missCnt, 0, "There should be no nodes missing from the node map"); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/models/KetamaClusterModelTest.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.models; import org.jredis.cluster.ClusterModel; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; import org.jredis.ri.alphazero.support.Log; import org.jredis.ri.cluster.DefaultClusterNodeSpec; import org.jredis.ri.cluster.DefaultClusterSpec; import org.jredis.ri.cluster.model.KetamaClusterModel; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * {@link KetamaClusterModel} specific tests. All generic and C.H. specific tests are * defined higher in the hierarchy chain. * *

    Uses the {@link DefaultClusterSpec} for tests. * * @author joubin (alphazero@sensesay.net) * @date Mar 29, 2010 * */ @Test(suiteName="ketama") public class KetamaClusterModelTest extends ConsistentHashClusterProviderTestBase { // ------------------------------------------------------------------------ // super overrides // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#newClusterModel(org.jredis.cluster.ClusterSpec) */ @Override protected ClusterModel newClusterModel (ClusterSpec clusterSpec) { ClusterModel model = null; try { model = new KetamaClusterModel(clusterSpec); } catch (RuntimeException e){ Log.error("NOTE: propagating error > " + e.getLocalizedMessage()); throw e; } return model; } /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#newClusterSpec() */ @Override protected ClusterSpec newClusterSpec () { ClusterSpec spec = new DefaultClusterSpec(); for(int i=0; i<100; i++){ ClusterNodeSpec node = new DefaultClusterNodeSpec(DefaultConnectionSpec.newSpec("localhost", 6379+i, 0, null)); spec.addNode(node); } return spec; } // /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#getSupportedClusterType() */ // @Override // protected ClusterType getSupportedClusterType () { // return ClusterType.CONSISTENT_HASH; // } // ------------------------------------------------------------------------ // Ketama specific tests // ------------------------------------------------------------------------ @Test public void fooTest() { Log.log("Foo test for KetamaClusterModel"); assertTrue(true); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/models/KetamaHashAlgoTest.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.models; import org.jredis.cluster.support.HashAlgorithm; import org.jredis.cluster.support.HashAlgorithmProviderTestBase; import org.jredis.ri.cluster.model.KetamaHashProvider; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 27, 2010 * */ public class KetamaHashAlgoTest extends HashAlgorithmProviderTestBase { // ------------------------------------------------------------------------ // super overrides // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.cluster.ProviderTestBase#newProviderInstance() */ @Override protected HashAlgorithm newProviderInstance () { return new KetamaHashProvider(); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/models/StaticHashClusterProviderTestBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.models; import java.util.Set; import org.jredis.NotSupportedException; import org.jredis.cluster.ClusterModelProviderTestBase; import org.jredis.cluster.ClusterNodeSpec; import org.jredis.cluster.ClusterSpec; import org.jredis.cluster.ClusterType; import org.jredis.cluster.model.StaticHashCluster; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 31, 2010 * */ public abstract class StaticHashClusterProviderTestBase extends ClusterModelProviderTestBase { /* (non-Javadoc) @see org.jredis.cluster.ClusterModelProviderTestBase#getSupportedClusterType() */ @Override protected final ClusterType getSupportedClusterType () { return ClusterType.CONSISTENT_HASH; } // ------------------------------------------------------------------------ // StaticHashCluster generic tests // ------------------------------------------------------------------------ @Test public void compatibilityTest() { Log.log("Test provider conformance to static hashing requirements"); assertNotNull(provider, "provider is null!"); // 1 - Must support the STATIC HASH and nothing else assertTrue(provider.supports(ClusterType.STATIC_HASH), "A StaticHashCluster model must support Type.CONSISTENT_HASH"); assertFalse(provider.supports(ClusterType.CONSISTENT_HASH), "A StaticHashCluster model can not support other cluster types beyond Type.CONSISTENT_HASH"); // 2 - Must not be reconfigurable assertFalse(provider.supportsReconfiguration(), "Static hash clusters can not be reconfigured."); // 3 - Must not support any of the NodeMap modification ops, indicated by raising the specified ProviderException class boolean didRaiseEx; ClusterSpec clusterSpec = this.newClusterSpec(); Set nodeSpecs = clusterSpec.getNodeSpecs(); assertTrue(nodeSpecs.size() > 0, "node specs set size must be greater than zero."); ClusterNodeSpec aNodeSpec = (ClusterNodeSpec) nodeSpecs.toArray()[0]; didRaiseEx = false; try { provider.addNode(aNodeSpec); } catch (NotSupportedException expected) {didRaiseEx = true;} catch (Throwable whatsthis) {fail ("Unexpected exception raised by provider method", whatsthis);} if(!didRaiseEx) { fail("Provider failed to raise exception for addNode()");} didRaiseEx = false; try { provider.removeNode(aNodeSpec); } catch (NotSupportedException expected) {didRaiseEx = true;} catch (Throwable whatsthis) {fail ("Unexpected exception raised by provider method", whatsthis);} if(!didRaiseEx) { fail("Provider failed to raise exception for removeNode()");} } @Test public void testStaticHash() { Log.log("Basic nodemap test of Consistent Hashing cluster model"); StaticHashCluster model = (StaticHashCluster) newProviderInstance(); assertNotNull(model, "model should not be null"); ClusterSpec spec = model.getSpec(); assertNotNull(spec, "spec should not be null"); } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/cluster/support/HashAlgorithmProviderTestBase.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.cluster.support; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.HashSet; import java.util.Set; import org.jredis.ClientRuntimeException; import org.jredis.cluster.RefImplTestSuiteBase; import org.jredis.cluster.support.HashAlgorithm; import org.jredis.ri.alphazero.support.Log; import org.testng.annotations.Test; /** * [TODO: document me!] * * @author joubin (alphazero@sensesay.net) * @date Mar 26, 2010 * */ @Test(suiteName="extensions-cluster-specs-algorithms-1") abstract public class HashAlgorithmProviderTestBase extends RefImplTestSuiteBase{ // ------------------------------------------------------------------------ // Specification Interface tested // ------------------------------------------------------------------------ protected final Class getSpecificationClass () { return HashAlgorithm.class; } // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ @SuppressWarnings("static-access") @Test public void testHashByteArray() { Log.log("Testing HashAlgorithm hash(byte[])"); HashAlgorithm hashAlgo = newProviderInstance(); try { int c = 2000; int cnt = (int) (10 * Math.log(c)) * c; Log.log("Test hashing %d keys", cnt); Set hashSet = new HashSet(cnt); for(int i=0; i { // ------------------------------------------------------------------------ // Specification Interface tested // ------------------------------------------------------------------------ /* (non-Javadoc) @see org.jredis.ri.ProviderTestBase#newProviderInstance() */ @Override protected Object newProviderInstance () { return new CryptoHashUtils(); } protected final Class getSpecificationClass () { return CryptoHashUtils.class; } // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ /** * Test computeMd5 using byte[] */ @SuppressWarnings("static-access") @Test public void testComputeMd5 () { Log.log("Testing Crptographic function computeMd5(byte[])"); try { byte[] data1 = data.getRandomBytes(255); byte[] data1_md5 = CryptoHashUtils.computeMd5(data1); assertNotNull(data1_md5, "md5 digest should not be null"); assertNotSame(data1_md5, data1, "md5 digest result shoud not be the same as the input array"); assertTrue(data1_md5.length > 0, "md5 digest length should be non-zero"); // edge case - NULL input not allowed boolean didRaiseError = false; try { byte[] nullref = null; CryptoHashUtils.computeMd5(nullref); } catch (IllegalArgumentException e){ didRaiseError = true; } catch (RuntimeException what) { fail("Unexpected runtime exception raised",what); } assertTrue(didRaiseError, "Expecting a raised exception for null input"); // edge case - zero length input not allowed didRaiseError = false; try { byte[] zerobytes = data.getRandomBytes(0); CryptoHashUtils.computeMd5(zerobytes); } catch (IllegalArgumentException e){ didRaiseError = true; } catch (RuntimeException what) { fail("Unexpected runtime exception raised",what); } assertTrue(didRaiseError, "Expecting a raised exception for zero length input"); } catch (ClientRuntimeException e) { fail("Required cryptographic algorithm (MD5) is not available", e); } catch (RuntimeException whatsthis){ fail("Unexpected exception class thrown", whatsthis); } } } ================================================ FILE: extensions/ri/src/test/java/org/jredis/test/util/RunningAverage.java ================================================ /* * Copyright 2009-2010 Joubin Houshyar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jredis.test.util; public class RunningAverage { private float avg; private long max; private long min = Long.MAX_VALUE; private long n; public RunningAverage() { avg = 0; n = 0; } public long onMeasure (long delta){ avg =((avg*n)+delta)/(++n); if(delta > max) max = delta; if(delta < min) min = delta; return (long) avg; } public long get () { return (long) avg; } /** * @return */ public long getMin () { return min; } public long getMax () { return max; } /** * */ public long getCount () { return n; } } ================================================ FILE: pom.xml ================================================ 4.0.0 JRedis [Build POM] org.jredis jredis a.0-SNAPSHOT pom core examples org.apache.maven.plugins maven-source-plugin 2.1.2 attach-sources verify jar org.apache.maven.plugins maven-compiler-plugin 2.0.2 1.6 1.6 org.testng testng 7.7.0 test 2009 Apache 2 http://www.apache.org/licenses/LICENSE-2.0.txt all Copyright 2009 (c) Joubin Houshyar - All Right Reserved alphazero Joubin Houshyar alphazero@sensesay.net http://github.com/alphazero ReleaseManager Designer Developer UTC-5 robey Robey Pointer robey@lag.net http://github.com/robey Contributor UTC-8 szegedi Attila Szegedi http://github.com/szegedi Contributor UTC-8 anthonylauzon Anthony Lauzon http://github.com/anthonylauzon Contributor gv0tch0 Nik Kolev nkolev@gmail.com http://github.com/gv0tch0 Contributor UTC-5 ================================================ FILE: redis_version_compliance.txt ================================================ compliance: Redis 3.0 tested with: Redis 2.9.102 (4ad1b2f8/0) 64 bit -- compliance report (see api/src/test/org/jredis/compliance package -- /// non-compliance report for JRedis ////////////// [ 0] auth [ 1] setex [ 2] setbit [ 3] getbit [ 4] setrange [ 5] getrange [ 6] select [ 7] shutdown [ 8] lpushx [ 9] rpushx [10] linsert [11] sync [12] monitor [13] persist [14] zrevrangebyscore [15] exec [16] blpop [17] brpop [18] brpoplpush [19] strlen [20] hsetnx [21] hmset [22] hmget [23] zunionstore [24] zinterstore [25] config [26] hincrby [27] subscribe [28] unsubscribe [29] psubscribe [30] punsubscribe [31] publish [32] watch [33] unwatch [34] object [35] client ///////////////////////////////////////////////////////// /// non-compliance report for JRedisFuture ////////////// [ 0] auth [ 1] setex [ 2] setbit [ 3] getbit [ 4] setrange [ 5] getrange [ 6] select [ 7] shutdown [ 8] lpushx [ 9] rpushx [10] linsert [11] sync [12] monitor [13] persist [14] zrevrangebyscore [15] multi [16] exec [17] discard [18] blpop [19] brpop [20] brpoplpush [21] strlen [22] hsetnx [23] hmset [24] hmget [25] zunionstore [26] zinterstore [27] config [28] hincrby [29] subscribe [30] unsubscribe [31] psubscribe [32] punsubscribe [33] publish [34] watch [35] unwatch [36] object [37] client /////////////////////////////////////////////////////////