├── .gitattributes ├── .gitignore ├── .travis.decrypt.sh ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── build.gradle ├── gradle.properties.enc ├── gradle ├── secring.gpg └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── rethinkdb-orm-annotations ├── build.gradle └── src │ └── main │ └── java │ └── de │ └── jakobjarosch │ └── rethinkdb │ └── orm │ └── annotation │ ├── Index.java │ ├── PrimaryKey.java │ └── RethinkDBModel.java ├── rethinkdb-orm-samples ├── build.gradle └── src │ └── main │ └── java │ ├── EntryPoint.java │ ├── SubModel.java │ └── TestModel.java ├── rethinkdb-orm ├── build.gradle └── src │ ├── main │ └── java │ │ └── de │ │ └── jakobjarosch │ │ └── rethinkdb │ │ └── orm │ │ ├── RethinkDBDAOProcessor.java │ │ ├── dao │ │ ├── DAOIterator.java │ │ ├── GenericDAO.java │ │ └── ModelMapper.java │ │ └── model │ │ ├── ChangeFeedElement.java │ │ ├── IndexModel.java │ │ ├── PrimaryKeyModel.java │ │ └── geo │ │ ├── GeoFactory.java │ │ ├── ReqlGeo.java │ │ ├── ReqlLine.java │ │ ├── ReqlPoint.java │ │ └── ReqlPolygon.java │ └── test │ └── java │ └── de │ └── jakobjarosch │ └── rethinkdb │ └── orm │ └── dao │ ├── GenericDAOTest.java │ └── ModelMapperTest.java ├── rethinkdb-pool ├── build.gradle └── src │ └── main │ └── java │ └── de │ └── jakobjarosch │ └── rethinkdb │ └── pool │ ├── ConnectionFactory.java │ ├── ConnectionPoolMetrics.java │ ├── ConnectionStats.java │ ├── PersistentConnection.java │ ├── RethinkDBPool.java │ └── RethinkDBPoolBuilder.java └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # By default we handle everything with LF line endings 2 | * text eol=lf 3 | 4 | # Some filetypes should be handled as binary 5 | *.png binary 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | .idea/ 3 | *.iml 4 | 5 | # Gradle 6 | build/ 7 | .gradle/ 8 | gradle.properties 9 | 10 | # Defaults 11 | tmp/ 12 | -------------------------------------------------------------------------------- /.travis.decrypt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [[ -n "$TRAVIS_TAG" ]] && openssl aes-256-cbc -K $encrypted_65aa329e7553_key -iv $encrypted_65aa329e7553_iv -in gradle.properties.enc -out gradle.properties -d || true 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: java 7 | jdk: oraclejdk8 8 | 9 | install: true 10 | 11 | before_script: 12 | - sudo docker run -d -p 28015:28015 rethinkdb:2.3.4 13 | - ./.travis.decrypt.sh 14 | 15 | script: 16 | - make build 17 | 18 | deploy: 19 | provider: script 20 | script: 21 | - make travis-deploy 22 | on: 23 | tags: true 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL:=/bin/bash 2 | 3 | build: 4 | ./gradlew build 5 | 6 | travis-deploy: 7 | ./gradlew upload closeRepository 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RethinkDB ORM for Java 2 | 3 | [![Build Status](https://img.shields.io/travis/foxylion/rethinkdb-orm-java/master.svg?style=flat-square)](https://travis-ci.org/foxylion/rethinkdb-orm-java) 4 | [![Maven Version](https://img.shields.io/maven-central/v/de.jakobjarosch.rethinkdb/rethinkdb-orm.svg?style=flat-square)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.jakobjarosch.rethinkdb%22) 5 | ![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square) 6 | ![Maintenance](https://img.shields.io/maintenance/yes/2016.svg?style=flat-square) 7 | 8 | This is a lightweight OR mapper for [RethinkDB](https://www.rethinkdb.com/) and Java. 9 | It automatically maps your POJOs to a RethinkDB compatible data structure and vice versa. 10 | 11 | ## What do I get? 12 | 13 | - Lightweight OR mapper using annotation processors 14 | - Support to map fields to other database field names 15 | - Support to ignore fields from model or database 16 | - Possibility to automatically create tables and inidices 17 | - Connection pooling support across threads (also standalone available) 18 | 19 | ## How to use? 20 | 21 | The integration is using annotation processors to generate the DAO classes. 22 | Ensure that your IDE has [enabled annotation processing](https://immutables.github.io/apt.html). 23 | 24 | To get started you've to annotate your POJO. 25 | 26 | ```java 27 | @RethinkDBModel 28 | public class MyModel { 29 | @PrimaryKey private String id; 30 | private ReqlPoint location; 31 | } 32 | ``` 33 | 34 | The annotation processor will automatically generate a `MyModelDAO` class which 35 | can be used to create, read, update, delete your model (CRUD). It is also possible 36 | to stream the change sets. 37 | 38 | The DAO can be used very easily. 39 | 40 | ```java 41 | try (Connection connection = RethinkDB.r.connection().connect()) { 42 | MyModelDAO dao = new MyModelDAO(connection); 43 | MyModel model = dao.read("2a22fda0"); 44 | model.location = new ReqlPoint(12.234, 23.764); 45 | dao.update("2a22fda0", model); 46 | } 47 | ``` 48 | 49 | **More examples can be found [here](rethinkdb-orm-samples/src/main/java/EntryPoint.java).** 50 | 51 | ### Configure as a dependency 52 | 53 | The current version can be found [here](https://github.com/foxylion/rethinkdb-orm-java/releases). 54 | 55 | #### Maven 56 | ```xml 57 | 58 | de.jakobjarosch.rethinkdb 59 | rethinkdb-orm 60 | {{ current-version }} 61 | 62 | ``` 63 | 64 | #### Gradle 65 | ``` 66 | compile 'de.jakobjarosch.rethinkdb:rethinkdb-orm:{{ current-version }}' 67 | ``` 68 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.5.3" 7 | } 8 | } 9 | 10 | apply plugin: 'idea' 11 | apply plugin: 'io.codearte.nexus-staging' 12 | 13 | idea { 14 | module { 15 | // Add automatically generated classes (by apt) to classpath 16 | sourceDirs += file(project.projectDir.absolutePath + '/build/generated/source/apt/main') 17 | downloadJavadoc = true 18 | downloadSources = true 19 | } 20 | } 21 | 22 | def artifactRelease = (boolean) System.getenv('TRAVIS_TAG'); 23 | 24 | configure([project(':rethinkdb-orm'), project(':rethinkdb-orm-annotations'), project(':rethinkdb-pool')]) { 25 | apply plugin: 'java' 26 | apply plugin: 'signing' 27 | apply plugin: 'maven' 28 | 29 | task javadocJar(type: Jar, dependsOn: javadoc) { 30 | classifier = 'javadoc' 31 | from 'build/docs/javadoc' 32 | } 33 | 34 | task sourcesJar(type: Jar) { 35 | from sourceSets.main.allSource 36 | classifier = 'sources' 37 | } 38 | 39 | artifacts { 40 | archives jar 41 | archives javadocJar 42 | archives sourcesJar 43 | } 44 | 45 | if (artifactRelease) { 46 | signing { 47 | sign configurations.archives 48 | } 49 | } 50 | 51 | group = 'de.jakobjarosch.rethinkdb' 52 | version = System.getenv('TRAVIS_TAG') ?: 'master-SNAPSHOT' 53 | 54 | uploadArchives { 55 | repositories { 56 | if (artifactRelease) { 57 | mavenDeployer { 58 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 59 | 60 | repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') { 61 | authentication(userName: ossrhUser, password: ossrhPass) 62 | } 63 | pom.project { 64 | name 'RethinkDB ORM for Java' 65 | description 'A lightweight OR mapper for RethinkDB, written in Java.' 66 | url 'https://github.com/foxylion/rethinkdb-orm-java' 67 | scm { 68 | url 'https://github.com/foxylion/rethinkdb-orm-java' 69 | connection 'scm:git@github.com:foxylion/rethinkdb-orm-java.git' 70 | developerConnection 'scm:git@github.com:foxylion/rethinkdb-orm-java.git' 71 | } 72 | licenses { 73 | license { 74 | name 'The Apache License, Version 2.0' 75 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 76 | } 77 | } 78 | developers { 79 | developer { 80 | name 'Jakob Jarosch' 81 | email 'dev@jakobjarosch.de' 82 | organizationUrl 'https://www.jakobjarosch.de' 83 | } 84 | } 85 | } 86 | } 87 | } else { 88 | mavenInstaller { 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | if (artifactRelease) { 96 | nexusStaging { 97 | username = ossrhUser 98 | password = ossrhPass 99 | packageGroup = 'de.jakobjarosch' 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /gradle.properties.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxylion/rethinkdb-orm-java/c15fd5b89db365712f81acc5009b2584aac6bf08/gradle.properties.enc -------------------------------------------------------------------------------- /gradle/secring.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxylion/rethinkdb-orm-java/c15fd5b89db365712f81acc5009b2584aac6bf08/gradle/secring.gpg -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxylion/rethinkdb-orm-java/c15fd5b89db365712f81acc5009b2584aac6bf08/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 15 20:52:15 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /rethinkdb-orm-annotations/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'idea' 4 | } 5 | 6 | sourceCompatibility = 1.8 7 | targetCompatibility = 1.8 8 | -------------------------------------------------------------------------------- /rethinkdb-orm-annotations/src/main/java/de/jakobjarosch/rethinkdb/orm/annotation/Index.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(value = {ElementType.ANNOTATION_TYPE}) 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface Index { 11 | 12 | String[] fields(); 13 | 14 | boolean geo() default false; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /rethinkdb-orm-annotations/src/main/java/de/jakobjarosch/rethinkdb/orm/annotation/PrimaryKey.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.FIELD) 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface PrimaryKey { 11 | } 12 | -------------------------------------------------------------------------------- /rethinkdb-orm-annotations/src/main/java/de/jakobjarosch/rethinkdb/orm/annotation/RethinkDBModel.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.annotation; 2 | 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Target(ElementType.TYPE) 10 | @Retention(RetentionPolicy.CLASS) 11 | public @interface RethinkDBModel { 12 | 13 | String tableName() default ""; 14 | 15 | Index[] indices() default {}; 16 | } 17 | -------------------------------------------------------------------------------- /rethinkdb-orm-samples/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'idea' 4 | id 'net.ltgt.apt' version '0.6' 5 | } 6 | 7 | sourceCompatibility = 1.8 8 | targetCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compile 'org.slf4j:slf4j-simple:1.7.21' 16 | 17 | compile project(':rethinkdb-orm') 18 | apt project(':rethinkdb-orm') 19 | } 20 | -------------------------------------------------------------------------------- /rethinkdb-orm-samples/src/main/java/EntryPoint.java: -------------------------------------------------------------------------------- 1 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlPoint; 2 | import de.jakobjarosch.rethinkdb.pool.RethinkDBPool; 3 | import de.jakobjarosch.rethinkdb.pool.RethinkDBPoolBuilder; 4 | import rx.Subscription; 5 | import rx.schedulers.Schedulers; 6 | 7 | import java.time.ZonedDateTime; 8 | import java.util.Optional; 9 | 10 | public class EntryPoint { 11 | 12 | public static void main(String[] args) throws InterruptedException { 13 | final RethinkDBPool pool = new RethinkDBPoolBuilder().build(); 14 | try { 15 | TestModelDAO dao = new TestModelDAO(pool); 16 | 17 | // Initialize the table (creates the table, and indices) 18 | dao.initTable(); 19 | 20 | TestModel model = new TestModel("1"); 21 | model.setSubModel(new SubModel()); 22 | model.getSubModel().setLastUpdate(ZonedDateTime.now()); 23 | 24 | // Create the model in the database 25 | dao.create(model); 26 | 27 | model.setLocation(new ReqlPoint(127.0, 10.0)); 28 | 29 | // Update the model in the database 30 | dao.update("1", model); 31 | 32 | // Read the model from the database 33 | Optional dbModel = dao.read("1"); 34 | 35 | // Delete the model from the database 36 | dbModel.ifPresent(m -> { 37 | dao.delete("1"); 38 | }); 39 | 40 | // Subscriptions are handled by RxJava 41 | // This sample subscribes to the change feed in an async mode 42 | Subscription subscription = dao.changes() 43 | .subscribeOn(Schedulers.newThread()) 44 | .subscribe(change -> { 45 | System.out.println(Thread.currentThread().toString() + ": " + change); 46 | }); 47 | 48 | // Listen for 10 seconds on changes. 49 | Thread.sleep(10000); 50 | 51 | // Stop getting updates. 52 | subscription.unsubscribe(); 53 | 54 | Thread.sleep(1000); 55 | } finally { 56 | pool.shutdown(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rethinkdb-orm-samples/src/main/java/SubModel.java: -------------------------------------------------------------------------------- 1 | import java.time.LocalDateTime; 2 | import java.time.ZonedDateTime; 3 | 4 | public class SubModel { 5 | 6 | private ZonedDateTime lastUpdate; 7 | 8 | public ZonedDateTime getLastUpdate() { 9 | return lastUpdate; 10 | } 11 | 12 | public void setLastUpdate(ZonedDateTime lastUpdate) { 13 | this.lastUpdate = lastUpdate; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rethinkdb-orm-samples/src/main/java/TestModel.java: -------------------------------------------------------------------------------- 1 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 2 | import de.jakobjarosch.rethinkdb.orm.annotation.Index; 3 | import de.jakobjarosch.rethinkdb.orm.annotation.PrimaryKey; 4 | import de.jakobjarosch.rethinkdb.orm.annotation.RethinkDBModel; 5 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlPoint; 6 | 7 | @RethinkDBModel( 8 | tableName = "my_table", 9 | indices = { 10 | @Index(fields = {"location"}, geo = true), 11 | @Index(fields = {"field1", "field2"}) 12 | } 13 | ) 14 | public class TestModel { 15 | 16 | @PrimaryKey 17 | private String id; 18 | private ReqlPoint location; 19 | private SubModel subModel; 20 | 21 | TestModel() { 22 | } 23 | 24 | public TestModel(String id) { 25 | this.id = id; 26 | } 27 | 28 | public String getId() { 29 | return id; 30 | } 31 | 32 | public ReqlPoint getLocation() { 33 | return location; 34 | } 35 | 36 | public void setLocation(ReqlPoint location) { 37 | this.location = location; 38 | } 39 | 40 | public SubModel getSubModel() { 41 | return subModel; 42 | } 43 | 44 | public void setSubModel(SubModel subModel) { 45 | this.subModel = subModel; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rethinkdb-orm/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'idea' 4 | id 'net.ltgt.apt' version '0.6' 5 | } 6 | 7 | sourceCompatibility = 1.8 8 | targetCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compile 'com.fasterxml.jackson.core:jackson-databind:2.7.5' 16 | compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.5' 17 | 18 | compile 'io.reactivex:rxjava:1.0.10' 19 | compile project(':rethinkdb-pool') 20 | compile project(':rethinkdb-orm-annotations') 21 | 22 | testCompile 'org.mockito:mockito-all:2.0.2-beta' 23 | testCompile 'org.assertj:assertj-core:3.4.1' 24 | testCompile 'org.slf4j:slf4j-simple:1.7.21' 25 | 26 | // Annotation processor specific dependencies 27 | compile 'com.squareup:javapoet:1.7.0' 28 | compile 'com.google.auto.service:auto-service:1.0-rc2' 29 | apt 'com.google.auto.service:auto-service:1.0-rc2' 30 | } 31 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/RethinkDBDAOProcessor.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm; 2 | 3 | import com.google.auto.service.AutoService; 4 | import com.google.common.base.Joiner; 5 | import com.google.common.collect.Sets; 6 | import com.rethinkdb.net.Connection; 7 | import com.squareup.javapoet.*; 8 | import de.jakobjarosch.rethinkdb.orm.annotation.PrimaryKey; 9 | import de.jakobjarosch.rethinkdb.orm.annotation.RethinkDBModel; 10 | import de.jakobjarosch.rethinkdb.orm.dao.GenericDAO; 11 | import de.jakobjarosch.rethinkdb.orm.model.IndexModel; 12 | import de.jakobjarosch.rethinkdb.orm.model.PrimaryKeyModel; 13 | import de.jakobjarosch.rethinkdb.pool.PersistentConnection; 14 | import de.jakobjarosch.rethinkdb.pool.RethinkDBPool; 15 | 16 | import javax.annotation.processing.AbstractProcessor; 17 | import javax.annotation.processing.ProcessingEnvironment; 18 | import javax.annotation.processing.Processor; 19 | import javax.annotation.processing.RoundEnvironment; 20 | import javax.inject.Provider; 21 | import javax.lang.model.SourceVersion; 22 | import javax.lang.model.element.*; 23 | import javax.lang.model.type.TypeMirror; 24 | import javax.lang.model.util.ElementFilter; 25 | import javax.tools.Diagnostic.Kind; 26 | import javax.tools.JavaFileObject; 27 | import java.io.Writer; 28 | import java.util.Arrays; 29 | import java.util.List; 30 | import java.util.Set; 31 | import java.util.stream.Collectors; 32 | 33 | @AutoService(Processor.class) 34 | public class RethinkDBDAOProcessor extends AbstractProcessor { 35 | 36 | @Override 37 | public synchronized void init(ProcessingEnvironment processingEnvironment) { 38 | super.init(processingEnvironment); 39 | } 40 | 41 | @Override 42 | public Set getSupportedAnnotationTypes() { 43 | return Sets.newHashSet( 44 | RethinkDBModel.class.getCanonicalName() 45 | ); 46 | } 47 | 48 | @Override 49 | public SourceVersion getSupportedSourceVersion() { 50 | return SourceVersion.RELEASE_8; 51 | } 52 | 53 | @Override 54 | public boolean process(final Set annotations, 55 | final RoundEnvironment roundEnv) { 56 | try { 57 | process(roundEnv); 58 | } catch (Exception e) { 59 | processingEnv.getMessager().printMessage(Kind.ERROR, "Something went wrong: " + e); 60 | } 61 | return true; 62 | } 63 | 64 | private void process(RoundEnvironment roundEnv) throws Exception { 65 | for (final Element element : roundEnv.getElementsAnnotatedWith(RethinkDBModel.class)) { 66 | if (element instanceof TypeElement) { 67 | final TypeElement typeElement = (TypeElement) element; 68 | 69 | final RethinkDBModel modelAnnotation = typeElement.getAnnotation(RethinkDBModel.class); 70 | 71 | final PackageElement packageElement = (PackageElement) typeElement.getEnclosingElement(); 72 | 73 | final String packageName = packageElement.getQualifiedName().toString(); 74 | final String daoClassName = typeElement.getSimpleName() + "DAO"; 75 | final String daoQualifiedName = packageName.isEmpty() ? daoClassName : packageName + "." + daoClassName; 76 | 77 | final PrimaryKeyModel primaryKey = scanPrimaryKey(typeElement); 78 | final Set indices = scanIndices(modelAnnotation); 79 | 80 | final JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(daoQualifiedName); 81 | try (Writer w = fileObject.openWriter()) { 82 | 83 | ClassName modelType = ClassName.get(typeElement); 84 | ClassName primaryKeyType = ClassName.get(primaryKey.getPackageName(), primaryKey.getClassName()); 85 | TypeName connectionProviderType = ParameterizedTypeName.get(ClassName.get(Provider.class), ClassName.get(Connection.class)); 86 | ParameterizedTypeName genericDAO = ParameterizedTypeName.get(ClassName.get(GenericDAO.class), modelType, primaryKeyType); 87 | 88 | TypeSpec type = TypeSpec.classBuilder(daoClassName) 89 | .addModifiers(Modifier.PUBLIC) 90 | .superclass(genericDAO) 91 | 92 | .addMethod(MethodSpec.constructorBuilder() 93 | .addModifiers(Modifier.PUBLIC) 94 | .addParameter(RethinkDBPool.class, "pool") 95 | .addStatement("this(() -> pool.getConnection())") 96 | .build()) 97 | 98 | .addMethod(MethodSpec.constructorBuilder() 99 | .addModifiers(Modifier.PUBLIC) 100 | .addParameter(Connection.class, "connection") 101 | .addStatement("this(() -> new $T(connection))", PersistentConnection.class) 102 | .build()) 103 | 104 | .addMethod(MethodSpec.constructorBuilder() 105 | .addModifiers(Modifier.PUBLIC) 106 | .addParameter(connectionProviderType, "connectionProvider") 107 | .addStatement("super(connectionProvider, $T.class, $S, $S)", 108 | modelType, modelAnnotation.tableName(), primaryKey.getVariableName()) 109 | .addCode(createIndiceCodeBlock(indices)) 110 | .build()) 111 | 112 | .build(); 113 | 114 | JavaFile.builder(packageElement.getQualifiedName().toString(), type) 115 | .build().writeTo(w); 116 | } 117 | } 118 | } 119 | } 120 | 121 | private CodeBlock createIndiceCodeBlock(Set indices) { 122 | final CodeBlock.Builder builder = CodeBlock.builder(); 123 | for (IndexModel index : indices) { 124 | builder.addStatement("addIndex(" + index.isGeo() + ", $S)", Joiner.on(",").join(index.getFields())); 125 | } 126 | return builder.build(); 127 | } 128 | 129 | private PrimaryKeyModel scanPrimaryKey(TypeElement element) { 130 | final List variables = ElementFilter.fieldsIn(element.getEnclosedElements()); 131 | final Set primaryKeys = variables.stream().filter(v -> v.getAnnotation(PrimaryKey.class) != null).collect(Collectors.toSet()); 132 | 133 | if (primaryKeys.size() > 1) { 134 | log(Kind.ERROR, "Only one @PrimaryKey allowed."); 135 | throw new IllegalArgumentException(); 136 | } 137 | 138 | return primaryKeys.stream().findFirst() 139 | .map(v -> new PrimaryKeyModel(getTypeElement(v.asType()), v.getSimpleName().toString())) 140 | .orElse(new PrimaryKeyModel("java.lang", "String", "id")); 141 | } 142 | 143 | private Set scanIndices(RethinkDBModel element) { 144 | return Arrays.stream(element.indices()).map(i -> new IndexModel(i.geo(), i.fields())).collect(Collectors.toSet()); 145 | } 146 | 147 | private TypeElement getTypeElement(TypeMirror type) { 148 | return (TypeElement) processingEnv.getTypeUtils().asElement(type); 149 | } 150 | 151 | private void log(Kind level, String message, Object... arguments) { 152 | processingEnv.getMessager().printMessage(level, String.format(message, arguments)); 153 | } 154 | } -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/dao/DAOIterator.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.dao; 2 | 3 | 4 | import com.rethinkdb.net.Cursor; 5 | 6 | import java.io.Closeable; 7 | import java.io.IOException; 8 | import java.util.Iterator; 9 | import java.util.Map; 10 | import java.util.Optional; 11 | 12 | 13 | public class DAOIterator implements Iterator, Closeable { 14 | 15 | private final Iterator iterator; 16 | private final Optional closable; 17 | private final Class clazz; 18 | private final ModelMapper mapper; 19 | 20 | public DAOIterator(Iterator iterator, Class clazz, ModelMapper mapper) { 21 | this.iterator = iterator; 22 | this.closable = Optional.empty(); 23 | this.clazz = clazz; 24 | this.mapper = mapper; 25 | } 26 | 27 | public DAOIterator(Cursor cursor, Class clazz, ModelMapper mapper) { 28 | this.iterator = cursor; 29 | this.closable = Optional.of(cursor::close); 30 | this.clazz = clazz; 31 | this.mapper = mapper; 32 | } 33 | 34 | @Override 35 | public void close() throws IOException { 36 | if (closable.isPresent()) { 37 | closable.get().close(); 38 | } 39 | } 40 | 41 | @Override 42 | public boolean hasNext() { 43 | return iterator.hasNext(); 44 | } 45 | 46 | @Override 47 | public T next() { 48 | return mapper.map((Map) iterator.next(), clazz); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/dao/GenericDAO.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.dao; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.common.collect.Lists; 5 | import com.rethinkdb.RethinkDB; 6 | import com.rethinkdb.gen.ast.GetField; 7 | import com.rethinkdb.gen.ast.IndexCreate; 8 | import com.rethinkdb.gen.ast.ReqlExpr; 9 | import com.rethinkdb.gen.ast.Table; 10 | import com.rethinkdb.gen.exc.ReqlClientError; 11 | import com.rethinkdb.gen.exc.ReqlDriverError; 12 | import com.rethinkdb.gen.exc.ReqlInternalError; 13 | import com.rethinkdb.model.OptArgs; 14 | import com.rethinkdb.net.Connection; 15 | import com.rethinkdb.net.Cursor; 16 | import de.jakobjarosch.rethinkdb.orm.model.ChangeFeedElement; 17 | import de.jakobjarosch.rethinkdb.orm.model.IndexModel; 18 | import rx.Observable; 19 | 20 | import javax.inject.Provider; 21 | import java.util.*; 22 | import java.util.function.Function; 23 | import java.util.stream.Collectors; 24 | 25 | public class GenericDAO { 26 | 27 | private static final RethinkDB R = RethinkDB.r; 28 | private static final ModelMapper MAPPER = new ModelMapper(); 29 | 30 | 31 | private final Provider connectionProvider; 32 | private final Class clazz; 33 | private final String tableName; 34 | private final String primaryKey; 35 | 36 | private final Set indices = new HashSet<>(); 37 | 38 | public GenericDAO(Provider connection, Class clazz, String tableName, String primaryKey) { 39 | this.connectionProvider = connection; 40 | this.clazz = clazz; 41 | this.tableName = tableName; 42 | this.primaryKey = primaryKey; 43 | } 44 | 45 | protected void addIndex(boolean geo, String fields) { 46 | this.indices.add(new IndexModel(geo, fields.split(","))); 47 | } 48 | 49 | /** 50 | * Initialize the table, automatically create the table and indices if they do not yet exist. 51 | */ 52 | public void initTable() { 53 | try (Connection connection = connectionProvider.get()) { 54 | if (!hasTable(connection, tableName)) { 55 | R.tableCreate(tableName).optArg("primary_key", primaryKey).run(connection); 56 | } 57 | 58 | for (IndexModel index : indices) { 59 | String indexName = Joiner.on("_").join(index.getFields()); 60 | if (!hasIndex(connection, indexName)) { 61 | IndexCreate indexCreate = R.table(tableName) 62 | .indexCreate(indexName, row -> indexFieldsToReQL(row, index.getFields())); 63 | if (index.isGeo()) { 64 | indexCreate = indexCreate.optArg("geo", true); 65 | } 66 | 67 | indexCreate.run(connection); 68 | } 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Creates a model in the RethinkDB table. 75 | * 76 | * @param model Model which should be created. 77 | * @throws ReqlClientError Error is thrown when there was an error. 78 | * E.g. there was already a model with the same primary key. 79 | */ 80 | public void create(T model) { 81 | try (Connection connection = connectionProvider.get()) { 82 | Map map = MAPPER.map(model); 83 | Map result = R.table(tableName).insert(map).run(connection); 84 | 85 | if (((Long) result.get("errors")) > 0) { 86 | throw new ReqlClientError("Failed to create model: %s", ((String) result.get("first_error")).split("\n")[0]); 87 | } 88 | } 89 | } 90 | 91 | /** 92 | * Retrieves a model with the given primary key. 93 | * 94 | * @param id The primary key of the model which should be retrieved. 95 | * @return Maybe the model matching the given primary key. 96 | */ 97 | public Optional read(PK id) { 98 | try (Connection connection = connectionProvider.get()) { 99 | Map map = R.table(tableName).get(id).run(connection); 100 | return Optional.ofNullable(MAPPER.map(map, clazz)); 101 | } 102 | } 103 | 104 | /** 105 | * Returns all models in the table, use this with caution, 106 | * without filtering it could return a huge amount of data. 107 | * 108 | * @return Returns a list of all models in the table. 109 | */ 110 | public DAOIterator read() { 111 | return read(t -> t); 112 | } 113 | 114 | /** 115 | * Retrieves a iterator returning all models matching the given filter. 116 | *
117 | * Be sure to call {@link DAOIterator#close()} after finishing the iterator. 118 | * 119 | * @param filter The filter function which should be applied when executing the query. 120 | * @return An iterator for models matching the given filter. 121 | */ 122 | public DAOIterator read(Function filter) { 123 | try (Connection connection = connectionProvider.get()) { 124 | final Table table = R.table(tableName); 125 | Object result = filter.apply(table).run(connection); 126 | if (result instanceof List) { 127 | List list = ((List) result).stream() 128 | .map(map -> MAPPER.map((Map) map, clazz)) 129 | .collect(Collectors.toList()); 130 | return new DAOIterator<>(list.iterator(), clazz, MAPPER); 131 | } else if (result instanceof Map) { 132 | return new DAOIterator<>(Lists.newArrayList(result).iterator(), clazz, MAPPER); 133 | } else if (result instanceof Cursor) { 134 | Cursor cursor = (Cursor) result; 135 | return new DAOIterator<>(cursor, clazz, MAPPER); 136 | } else { 137 | throw new ReqlInternalError("Unknown return type for query: " + result.getClass()); 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * Updates a model. 144 | * 145 | * @param id The id of the model which should be updated. 146 | * @param model The model which should be updated. 147 | */ 148 | public void update(PK id, T model) { 149 | try (Connection connection = connectionProvider.get()) { 150 | Map map = MAPPER.map(model); 151 | Map result = R.table(tableName).get(id).update(map).run(connection); 152 | 153 | if (((Long) result.get("errors")) > 0) { 154 | throw new ReqlClientError("Failed to update model. %s", ((String) result.get("first_error")).split("\n")[0]); 155 | } 156 | } 157 | } 158 | 159 | /** 160 | * Updates a model in the non atomic way. See ReQL command: update 161 | * for more details. 162 | * 163 | * @param id The id of the model which should be updated. 164 | * @param model The model which should be updated. 165 | */ 166 | public void updateNonAtomic(PK id, T model) { 167 | try (Connection connection = connectionProvider.get()) { 168 | R.table(tableName).get(id).update(model).run(connection, OptArgs.of("non_atomic", true)); 169 | } 170 | } 171 | 172 | /** 173 | * Deletes a model from the table. 174 | * 175 | * @param id The primary key of the model which should be removed. 176 | */ 177 | public void delete(PK id) { 178 | try (Connection connection = connectionProvider.get()) { 179 | R.table(tableName).get(id).delete().run(connection); 180 | } 181 | } 182 | 183 | /** 184 | * Provides a change feed of all changes which occur after subscribing to the returned {@link Observable}. 185 | * 186 | * @return Returns an {@link Observable} which subscribes to all changes made after the subscription started. 187 | */ 188 | public Observable> changes() { 189 | return changes(t -> t); 190 | } 191 | 192 | /** 193 | * Provides a change feed of all matching changes which occur after subscribing to the returned {@link Observable}. 194 | * 195 | * @param filter A filter for the change feed to only show changes matching the filter. 196 | * @return Returns an {@link Observable} which subscribes to all matching changes made after the subscription started. 197 | */ 198 | @SuppressWarnings("unchecked") 199 | public Observable> changes(Function filter) { 200 | return Observable.create(subscriber -> { 201 | Cursor>> cursor = null; 202 | try (Connection connection = connectionProvider.get()) { 203 | final Table table = R.table(tableName); 204 | cursor = filter.apply(table).changes().run(connection); 205 | 206 | while (!subscriber.isUnsubscribed()) { 207 | Map> map = cursor.next(); 208 | ChangeFeedElement change = mapChangeFeedElement(map); 209 | subscriber.onNext(change); 210 | } 211 | } catch (ReqlDriverError e) { 212 | if (e.getCause() instanceof InterruptedException) { 213 | // We were interrupted because the subscription has been canceled. 214 | // So we won't do anything. 215 | } else { 216 | subscriber.onError(e); 217 | } 218 | } finally { 219 | if (cursor != null) { 220 | cursor.close(); 221 | } 222 | } 223 | }); 224 | } 225 | 226 | private boolean hasTable(Connection connection, String table) { 227 | List tables = R.tableList().run(connection); 228 | return tables.contains(table); 229 | } 230 | 231 | private boolean hasIndex(Connection connection, String indexName) { 232 | List indices = R.table(tableName).indexList().run(connection); 233 | return indices.contains(indexName); 234 | } 235 | 236 | private List indexFieldsToReQL(ReqlExpr row, String[] fields) { 237 | final GetField[] reQLFields = Arrays.stream(fields).map(f -> row.g(f)).collect(Collectors.toList()).toArray(new GetField[0]); 238 | return R.array(reQLFields); 239 | } 240 | 241 | private ChangeFeedElement mapChangeFeedElement(Map> map) { 242 | final Map oldVal = map.get("old_val"); 243 | final Map newVal = map.get("new_val"); 244 | 245 | final T oldValObj = oldVal != null ? MAPPER.map(oldVal, clazz) : null; 246 | final T newValObj = newVal != null ? MAPPER.map(newVal, clazz) : null; 247 | 248 | return new ChangeFeedElement<>(oldValObj, newValObj); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/dao/ModelMapper.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.dao; 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlGeo; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Map; 11 | 12 | class ModelMapper { 13 | 14 | private static final Logger LOGGER = LoggerFactory.getLogger(ModelMapper.class); 15 | 16 | private final ObjectMapper mapper; 17 | 18 | public ModelMapper() { 19 | mapper = new ObjectMapper(); 20 | mapper.registerModule(new JavaTimeModule()); 21 | } 22 | 23 | /** 24 | * Maps a object to a RethinkDB ready map. 25 | * 26 | * @param obj Object which should be converted. 27 | * @return A RethinkDB ready map which contains only primitive and ReqlGeo data types. 28 | */ 29 | public Map map(Object obj) { 30 | Map map = mapper.convertValue(obj, Map.class); 31 | map.forEach((k, v) -> reconstructGeoObjects(map, k, v)); 32 | return map; 33 | } 34 | 35 | /** 36 | * Converts a RethinkDB response into a given Model. 37 | * 38 | * @param map RethinkDB response as a map. 39 | * @param clazz Class which should be converted into. 40 | * @return The converted RethinkDB response. 41 | */ 42 | public T map(Map map, Class clazz) { 43 | return mapper.convertValue(map, clazz); 44 | } 45 | 46 | /** 47 | * The method reconstructs all previous {@link ReqlGeo} instances back from their serialized representation. 48 | * Those instances are required, otherwise RethinkDB would not store coordinates in the correct data type. 49 | *
50 | * The whole reconstruction is required because jackson is not able the preserve some types when converting 51 | * to a generic Map. 52 | * 53 | * @param parentMap The map which contains the given key and value 54 | * @param key The key of the given value 55 | * @param value The value which might be a map and contain the {@link ReqlGeo} class identifier. 56 | */ 57 | @SuppressWarnings("unchecked") 58 | private void reconstructGeoObjects(Map parentMap, Object key, Object value) { 59 | // We only dig deeper if it's a map, otherwise we are done. 60 | if (value instanceof Map) { 61 | Map childs = (Map) value; 62 | if (childs.containsKey(ReqlGeo.MAPPING_CLASS_KEY)) { 63 | // The childs contain the required key used to identify ReqlGeo data types. 64 | // The map will be replaced with a new instance of the ReqlGeo sub type. 65 | try { 66 | Class geoClassName = Class.forName(String.valueOf(childs.get(ReqlGeo.MAPPING_CLASS_KEY))); 67 | final Object geoEntry = map(childs, geoClassName); 68 | ((Map) parentMap).put(key, geoEntry); 69 | } catch (ClassNotFoundException e) { 70 | LOGGER.error("Failed to deserialize ReqlGeo class.", e); 71 | } 72 | } else { 73 | // This is no ReqlGeo specific child, try to dig deeper with a recursive call. 74 | childs.forEach((k, v) -> reconstructGeoObjects(childs, k, v)); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/ChangeFeedElement.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model; 2 | 3 | import com.google.common.base.MoreObjects; 4 | 5 | import java.util.Objects; 6 | import java.util.Optional; 7 | 8 | public class ChangeFeedElement { 9 | 10 | private final Optional oldValue; 11 | private final Optional newValue; 12 | 13 | public ChangeFeedElement(T oldValue, T newValue) { 14 | this.oldValue = Optional.ofNullable(oldValue); 15 | this.newValue = Optional.ofNullable(newValue); 16 | } 17 | 18 | public Optional getOldValue() { 19 | return oldValue; 20 | } 21 | 22 | public Optional getNewValue() { 23 | return newValue; 24 | } 25 | 26 | @Override 27 | public int hashCode() { 28 | return Objects.hash(oldValue, newValue); 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if(!(o instanceof ChangeFeedElement)) 34 | return false; 35 | ChangeFeedElement cfe = (ChangeFeedElement) o; 36 | return Objects.equals(this.getOldValue(), cfe.getOldValue()) && 37 | Objects.equals(this.getNewValue(), cfe.getNewValue()); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return MoreObjects.toStringHelper(this) 43 | .add("oldValue", getOldValue()) 44 | .add("newValue", getNewValue()) 45 | .toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/IndexModel.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model; 2 | 3 | public class IndexModel { 4 | 5 | private final boolean geo; 6 | private final String[] fields; 7 | 8 | public IndexModel(boolean geo, String[] fields) { 9 | this.geo = geo; 10 | this.fields = fields; 11 | } 12 | 13 | public boolean isGeo() { 14 | return geo; 15 | } 16 | 17 | public String[] getFields() { 18 | return fields; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/PrimaryKeyModel.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model; 2 | 3 | import javax.lang.model.element.PackageElement; 4 | import javax.lang.model.element.TypeElement; 5 | 6 | public class PrimaryKeyModel { 7 | 8 | private final String packageName; 9 | private final String className; 10 | private final String variableName; 11 | 12 | public PrimaryKeyModel(TypeElement type, String variableName) { 13 | this.packageName = ((PackageElement) type.getEnclosingElement()).getQualifiedName().toString(); 14 | this.className = type.getSimpleName().toString(); 15 | this.variableName = variableName; 16 | } 17 | 18 | public PrimaryKeyModel(String packageName, String className, String variableName) { 19 | this.packageName = packageName; 20 | this.className = className; 21 | this.variableName = variableName; 22 | } 23 | 24 | public String getPackageName() { 25 | return packageName; 26 | } 27 | 28 | public String getClassName() { 29 | return className; 30 | } 31 | 32 | public String getVariableName() { 33 | return variableName; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/geo/GeoFactory.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model.geo; 2 | 3 | class GeoFactory { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/geo/ReqlGeo.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model.geo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.rethinkdb.ast.ReqlAst; 5 | import com.rethinkdb.gen.proto.TermType; 6 | import com.rethinkdb.model.Arguments; 7 | import com.rethinkdb.model.OptArgs; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | 15 | public abstract class ReqlGeo extends ReqlAst { 16 | 17 | public static final String MAPPING_CLASS_KEY = "__geoClassName"; 18 | 19 | @JsonProperty(MAPPING_CLASS_KEY) 20 | private final String geoClassName; 21 | 22 | protected ReqlGeo(TermType termType, Arguments args, OptArgs optargs) { 23 | super(termType, args, optargs); 24 | geoClassName = getClass().getName(); 25 | } 26 | 27 | static double parseLongitude(Map data) { 28 | if (data.containsKey("longitude")) 29 | return ReqlGeo.convertCoordinate(data.get("longitude")); 30 | else 31 | return ReqlGeo.convertCoordinate(((List) data.get("coordinates")).get(0)); 32 | } 33 | 34 | static double parseLatitude(Map data) { 35 | if (data.containsKey("latitude")) 36 | return ReqlGeo.convertCoordinate(data.get("latitude")); 37 | else 38 | return ReqlGeo.convertCoordinate(((List) data.get("coordinates")).get(1)); 39 | } 40 | 41 | static List parsePoints(Map data) { 42 | List points = (List) data.get("coordinates"); 43 | 44 | if (points.isEmpty()) { 45 | return new ArrayList<>(); 46 | } else if (points.get(0) instanceof Map) { 47 | // Deserialize Jackson serialization 48 | return points.stream() 49 | .map(p -> toPoint((Map) p)) 50 | .collect(Collectors.toList()); 51 | } else if (points.get(0) instanceof List) { 52 | // Deserialize ReQL result 53 | if(((List) points.get(0)).get(0) instanceof List) { 54 | // The data structure used by polygon has one more cascaded list 55 | // points[][] = array(longitude, latitude) 56 | points = (List) points.get(0); 57 | } 58 | return points.stream() 59 | .map(p -> parseReqlCoordinate((List) p)) 60 | .collect(Collectors.toList()); 61 | } else { 62 | throw new IllegalArgumentException("Can't handle" + points.get(0).getClass() + " as coordinate data type."); 63 | } 64 | } 65 | 66 | @SuppressWarnings("unchecked") 67 | private static ReqlPoint toPoint(Map map) { 68 | return new ReqlPoint(map); 69 | } 70 | 71 | private static ReqlPoint parseReqlCoordinate(List data) { 72 | return new ReqlPoint(convertCoordinate(data.get(0)), convertCoordinate(data.get(1))); 73 | } 74 | 75 | private static double convertCoordinate(Object longitude) { 76 | if (longitude instanceof Long) { 77 | return (long) longitude; 78 | } else { 79 | return (double) longitude; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/geo/ReqlLine.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model.geo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.google.common.base.Joiner; 5 | import com.rethinkdb.gen.exc.ReqlCompileError; 6 | import com.rethinkdb.gen.proto.TermType; 7 | 8 | import java.util.*; 9 | 10 | 11 | public class ReqlLine extends ReqlGeo { 12 | 13 | private final List coordinates; 14 | 15 | 16 | // Constructor and parsing used to deserialize from RethinkDB response or jackson map structure. 17 | @JsonCreator 18 | ReqlLine(Map data) { 19 | this(parsePoints(data)); 20 | } 21 | 22 | public ReqlLine(ReqlPoint... coordinates) { 23 | this(Arrays.asList(coordinates)); 24 | } 25 | 26 | public ReqlLine(List coordinates) { 27 | super(TermType.LINE, null, null); 28 | 29 | if (coordinates.size() < 2) { 30 | throw new ReqlCompileError("Line must contain at least 2 coordinates"); 31 | } 32 | 33 | coordinates.forEach(args::coerceAndAdd); 34 | 35 | this.coordinates = new ArrayList<>(coordinates); 36 | } 37 | 38 | public List getCoordinates() { 39 | return Collections.unmodifiableList(coordinates); 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "[ " + Joiner.on(", ").join(coordinates) + " ]"; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object o) { 49 | if (this == o) return true; 50 | if (o == null || getClass() != o.getClass()) return false; 51 | ReqlLine reQLLine = (ReqlLine) o; 52 | return Objects.equals(coordinates, reQLLine.coordinates); 53 | } 54 | 55 | @Override 56 | public int hashCode() { 57 | return Objects.hash(coordinates); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/geo/ReqlPoint.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model.geo; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.rethinkdb.gen.proto.TermType; 7 | 8 | import java.util.Map; 9 | import java.util.Objects; 10 | 11 | public class ReqlPoint extends ReqlGeo { 12 | 13 | private final double longitude; 14 | private final double latitude; 15 | 16 | // Constructor and parsing used to deserialize from RethinkDB response or jackson map structure. 17 | @JsonCreator 18 | ReqlPoint(Map data) { 19 | this(parseLongitude(data), 20 | parseLatitude(data)); 21 | } 22 | 23 | public ReqlPoint(double longitude, double latitude) { 24 | super(TermType.POINT, null, null); 25 | args.coerceAndAdd(longitude); 26 | args.coerceAndAdd(latitude); 27 | this.longitude = longitude; 28 | this.latitude = latitude; 29 | } 30 | 31 | @JsonProperty() 32 | public double getLongitude() { 33 | return longitude; 34 | } 35 | 36 | public double getLatitude() { 37 | return latitude; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "{ longitude: " + longitude + ", latitude: " + latitude + " }"; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (o == null || getClass() != o.getClass()) return false; 49 | ReqlPoint reQLPoint = (ReqlPoint) o; 50 | return Double.compare(reQLPoint.latitude, latitude) == 0 && 51 | Double.compare(reQLPoint.longitude, longitude) == 0; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | return Objects.hash(latitude, longitude); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/main/java/de/jakobjarosch/rethinkdb/orm/model/geo/ReqlPolygon.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.model.geo; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.google.common.base.Joiner; 6 | import com.rethinkdb.gen.exc.ReqlCompileError; 7 | import com.rethinkdb.gen.proto.TermType; 8 | 9 | import java.util.*; 10 | 11 | public class ReqlPolygon extends ReqlGeo { 12 | 13 | private final List coordinates; 14 | 15 | // Constructor and parsing used to deserialize from RethinkDB response or jackson map structure. 16 | @JsonCreator 17 | ReqlPolygon(Map data) { 18 | this(parsePoints(data)); 19 | } 20 | 21 | public ReqlPolygon(ReqlPoint... coordinates) { 22 | this(Arrays.asList(coordinates)); 23 | } 24 | 25 | public ReqlPolygon(List coordinates) { 26 | super(TermType.POLYGON, null, null); 27 | 28 | if (coordinates.size() < 3) { 29 | throw new ReqlCompileError("Polygon must contain at least 3 coordinates"); 30 | } 31 | 32 | coordinates.forEach(args::coerceAndAdd); 33 | 34 | this.coordinates = new ArrayList<>(coordinates); 35 | } 36 | 37 | public List getCoordinates() { 38 | return Collections.unmodifiableList(coordinates); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "[ " + Joiner.on(", ").join(coordinates) + " ]"; 44 | } 45 | 46 | @Override 47 | public boolean equals(Object o) { 48 | if (this == o) return true; 49 | if (o == null || getClass() != o.getClass()) return false; 50 | ReqlPolygon that = (ReqlPolygon) o; 51 | return Objects.equals(coordinates, that.coordinates); 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | return Objects.hash(coordinates); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rethinkdb-orm/src/test/java/de/jakobjarosch/rethinkdb/orm/dao/GenericDAOTest.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.dao; 2 | 3 | import com.rethinkdb.RethinkDB; 4 | import com.rethinkdb.net.Connection; 5 | import de.jakobjarosch.rethinkdb.orm.model.ChangeFeedElement; 6 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlLine; 7 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlPoint; 8 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlPolygon; 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import rx.Subscription; 13 | import rx.schedulers.Schedulers; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Iterator; 17 | import java.util.List; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | public class GenericDAOTest { 22 | 23 | static class TestModel { 24 | static class SubModel { 25 | public String name; 26 | } 27 | 28 | TestModel() { 29 | } 30 | 31 | TestModel(String id) { 32 | this.id = id; 33 | } 34 | 35 | public String id; 36 | public Integer rank; 37 | public ReqlPoint point; 38 | public ReqlLine line; 39 | public ReqlPolygon polygon; 40 | public SubModel sub; 41 | } 42 | 43 | private static RethinkDB r = RethinkDB.r; 44 | private GenericDAO dao; 45 | 46 | @Before 47 | public void setup() { 48 | dao = new GenericDAO<>(() -> createConnection(), TestModel.class, "integration_test", "id"); 49 | dao.addIndex(true, "point"); 50 | dao.addIndex(true, "line"); 51 | dao.addIndex(true, "polygon"); 52 | dao.initTable(); 53 | } 54 | 55 | @After 56 | public void teardown() { 57 | r.tableDrop("integration_test").run(createConnection()); 58 | } 59 | 60 | @Test 61 | public void create() { 62 | TestModel model = new TestModel("1"); 63 | dao.create(model); 64 | 65 | assertThat(dao.read("1")).isPresent(); 66 | } 67 | 68 | @Test 69 | public void update() { 70 | TestModel model = new TestModel("1"); 71 | dao.create(model); 72 | 73 | model.sub = new TestModel.SubModel(); 74 | model.sub.name = "test"; 75 | dao.update(model.id, model); 76 | 77 | assertThat(dao.read("1").get().sub.name).isEqualTo(model.sub.name); 78 | } 79 | 80 | @Test 81 | public void delete() { 82 | TestModel model = new TestModel("1"); 83 | dao.create(model); 84 | 85 | dao.delete("1"); 86 | 87 | assertThat(dao.read("1")).isNotPresent(); 88 | } 89 | 90 | @Test 91 | public void read_geoModelCorrectlyReturned() { 92 | TestModel model = new TestModel("1"); 93 | model.point = new ReqlPoint(10, 20.1); 94 | model.line = new ReqlLine(new ReqlPoint(1, 2), new ReqlPoint(3, 4)); 95 | model.polygon = new ReqlPolygon(new ReqlPoint(5, 6), new ReqlPoint(7, 8), new ReqlPoint(9, 10), new ReqlPoint(5, 6)); 96 | dao.create(model); 97 | 98 | TestModel dbModel = dao.read("1").get(); 99 | 100 | assertThat(dbModel.id).isEqualTo(model.id); 101 | assertThat(dbModel.point).isEqualTo(model.point); 102 | assertThat(dbModel.line).isEqualTo(model.line); 103 | assertThat(dbModel.polygon).isEqualTo(model.polygon); 104 | } 105 | 106 | @Test 107 | public void read_returnsAll() { 108 | dao.create(new TestModel("1")); 109 | dao.create(new TestModel("2")); 110 | 111 | Iterator iterator = dao.read(); 112 | List result = new ArrayList<>(); 113 | iterator.forEachRemaining(result::add); 114 | 115 | assertThat(result).hasSize(2); 116 | } 117 | 118 | @Test 119 | public void read_filter() { 120 | TestModel model1 = new TestModel("1"); 121 | model1.rank = 10; 122 | dao.create(model1); 123 | 124 | TestModel model2 = new TestModel("2"); 125 | model2.rank = 20; 126 | dao.create(model2); 127 | 128 | Iterator iterator = dao.read(t -> t.filter(r -> r.g("rank").gt(10))); 129 | List result = new ArrayList<>(); 130 | iterator.forEachRemaining(result::add); 131 | 132 | assertThat(result).hasSize(1); 133 | assertThat(result.get(0).id).isEqualTo(model2.id); 134 | } 135 | 136 | @Test 137 | public void changes_filtered() throws InterruptedException { 138 | List> changes = new ArrayList<>(); 139 | Subscription subscription = dao.changes(t -> t.filter(r -> r.g("rank").gt(10))) 140 | .subscribeOn(Schedulers.newThread()) 141 | .subscribe(changes::add); 142 | 143 | dao.create(new TestModel("1")); 144 | final TestModel model2 = new TestModel("2"); 145 | model2.rank = 20; 146 | dao.create(model2); 147 | model2.rank = 25; 148 | dao.update(model2.id, model2); 149 | dao.delete("2"); 150 | 151 | Thread.sleep(500); 152 | 153 | subscription.unsubscribe(); 154 | 155 | // Check if create, update and delete was received. 156 | assertThat(changes).hasSize(3); 157 | 158 | assertThat(changes.get(0).getOldValue()).isNotPresent(); 159 | assertThat(changes.get(0).getNewValue()).isPresent(); 160 | assertThat(changes.get(0).getNewValue().get().id).isEqualTo("2"); 161 | 162 | assertThat(changes.get(1).getOldValue()).isPresent(); 163 | assertThat(changes.get(1).getNewValue()).isPresent(); 164 | assertThat(changes.get(1).getOldValue().get().rank).isEqualTo(20); 165 | assertThat(changes.get(1).getNewValue().get().rank).isEqualTo(25); 166 | 167 | assertThat(changes.get(2).getOldValue()).isPresent(); 168 | assertThat(changes.get(2).getNewValue()).isNotPresent(); 169 | assertThat(changes.get(2).getOldValue().get().id).isEqualTo("2"); 170 | } 171 | 172 | private static Connection createConnection() { 173 | return r.connection().hostname("127.0.0.1").user("admin", "").db("test").connect(); 174 | } 175 | } -------------------------------------------------------------------------------- /rethinkdb-orm/src/test/java/de/jakobjarosch/rethinkdb/orm/dao/ModelMapperTest.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.orm.dao; 2 | 3 | 4 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlGeo; 5 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlLine; 6 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlPoint; 7 | import de.jakobjarosch.rethinkdb.orm.model.geo.ReqlPolygon; 8 | import org.assertj.core.api.Assertions; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | import java.util.Map; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | public class ModelMapperTest { 17 | 18 | public static class TestModel { 19 | public String id; 20 | public ReqlGeo geo; 21 | } 22 | 23 | private ModelMapper modelMapper; 24 | 25 | @Before 26 | public void setup() { 27 | modelMapper = new ModelMapper(); 28 | } 29 | 30 | @Test 31 | public void modelToMap_ReqlPointPreserved() { 32 | final TestModel model = new TestModel(); 33 | model.id = "test"; 34 | model.geo = new ReqlPoint(20.1, 30.0); 35 | 36 | Map result = modelMapper.map(model); 37 | 38 | assertThat(result.get("geo")).isEqualTo(model.geo); 39 | } 40 | 41 | @Test 42 | public void modelToMap_ReqlLinePreserved() { 43 | final TestModel model = new TestModel(); 44 | model.id = "test"; 45 | model.geo = new ReqlLine(new ReqlPoint(20.1, 30.0), new ReqlPoint(22.1, 33.0)); 46 | 47 | Map result = modelMapper.map(model); 48 | 49 | assertThat(result.get("geo")).isEqualTo(model.geo); 50 | } 51 | 52 | @Test 53 | public void modelToMap_ReqlPolygonPreserved() { 54 | final TestModel model = new TestModel(); 55 | model.id = "test"; 56 | model.geo = new ReqlPolygon(new ReqlPoint(20.1, 30.0), new ReqlPoint(22.1, 33.0), new ReqlPoint(24.1, 35.0)); 57 | 58 | Map result = modelMapper.map(model); 59 | 60 | assertThat(result.get("geo")).isEqualTo(model.geo); 61 | } 62 | } -------------------------------------------------------------------------------- /rethinkdb-pool/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'idea' 4 | id 'net.ltgt.apt' version '0.6' 5 | } 6 | 7 | sourceCompatibility = 1.8 8 | targetCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compile 'com.rethinkdb:rethinkdb-driver:2.3.0' 16 | compile 'org.apache.commons:commons-pool2:2.4.2' 17 | compile 'javax.inject:javax.inject:1' 18 | 19 | testCompile 'org.mockito:mockito-all:2.0.2-beta' 20 | testCompile 'org.assertj:assertj-core:3.4.1' 21 | 22 | compile 'com.google.auto.factory:auto-factory:1.0-beta3' 23 | apt 'com.google.auto.factory:auto-factory:1.0-beta3' 24 | } 25 | -------------------------------------------------------------------------------- /rethinkdb-pool/src/main/java/de/jakobjarosch/rethinkdb/pool/ConnectionFactory.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.pool; 2 | 3 | import com.rethinkdb.RethinkDB; 4 | import com.rethinkdb.net.Connection; 5 | import org.apache.commons.pool2.BasePooledObjectFactory; 6 | import org.apache.commons.pool2.PooledObject; 7 | import org.apache.commons.pool2.impl.DefaultPooledObject; 8 | 9 | import javax.inject.Inject; 10 | 11 | public class ConnectionFactory extends BasePooledObjectFactory { 12 | 13 | 14 | private final String hostname; 15 | private final int port; 16 | private final String username; 17 | private final String password; 18 | private final String database; 19 | 20 | @Inject 21 | public ConnectionFactory(String hostname, int port, String username, String password, String database) { 22 | this.hostname = hostname; 23 | this.port = port; 24 | this.username = username; 25 | this.password = password; 26 | this.database = database; 27 | } 28 | 29 | @Override 30 | public Connection create() throws Exception { 31 | return RethinkDB.r.connection() 32 | .hostname(hostname) 33 | .port(port) 34 | .user(username, password) 35 | .db(database) 36 | .connect(); 37 | } 38 | 39 | @Override 40 | public PooledObject wrap(Connection connection) { 41 | return new DefaultPooledObject<>(connection); 42 | } 43 | 44 | @Override 45 | public void destroyObject(PooledObject connection) throws Exception { 46 | connection.getObject().close(); 47 | } 48 | 49 | @Override 50 | public boolean validateObject(PooledObject connection) { 51 | return connection.getObject().isOpen(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rethinkdb-pool/src/main/java/de/jakobjarosch/rethinkdb/pool/ConnectionPoolMetrics.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.pool; 2 | 3 | 4 | public class ConnectionPoolMetrics { 5 | 6 | public enum PoolHealth { 7 | /** 8 | * Everything fine. 9 | */ 10 | HEALTHY, 11 | /** 12 | * No free connections left, and maxConnections reached. 13 | */ 14 | FULL, 15 | STOPPED 16 | } 17 | 18 | private final int connections; 19 | private final int freeConnections; 20 | private final int maxConnections; 21 | private final double connectionsPerSecond; 22 | private final double averageWaitTime; 23 | private final PoolHealth poolHealth; 24 | 25 | ConnectionPoolMetrics(int connections, 26 | int freeConnections, 27 | int maxConnections, 28 | double connectionsPerSecond, 29 | double averageWaitTime, 30 | PoolHealth poolHealth) { 31 | this.connections = connections; 32 | this.freeConnections = freeConnections; 33 | this.maxConnections = maxConnections; 34 | this.connectionsPerSecond = connectionsPerSecond; 35 | this.averageWaitTime = averageWaitTime; 36 | this.poolHealth = poolHealth; 37 | } 38 | 39 | @SuppressWarnings("unused") 40 | public int getConnections() { 41 | return connections; 42 | } 43 | 44 | @SuppressWarnings("unused") 45 | public int getFreeConnections() { 46 | return freeConnections; 47 | } 48 | 49 | @SuppressWarnings("unused") 50 | public int getMaxConnections() { 51 | return maxConnections; 52 | } 53 | 54 | /** 55 | * @return The average connection used per second in the last 5 minutes. 56 | */ 57 | @SuppressWarnings("unused") 58 | public double getConnectionsPerSecond() { 59 | return connectionsPerSecond; 60 | } 61 | 62 | /** 63 | * @return The average wait time to obtain a connection in the last 5 minutes. Unit is milliseconds. 64 | */ 65 | @SuppressWarnings("unused") 66 | public double getAverageWaitTime() { 67 | return averageWaitTime; 68 | } 69 | 70 | @SuppressWarnings("unused") 71 | public PoolHealth getPoolHealth() { 72 | return poolHealth; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /rethinkdb-pool/src/main/java/de/jakobjarosch/rethinkdb/pool/ConnectionStats.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.pool; 2 | 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | class ConnectionStats { 8 | 9 | private static class Metric { 10 | long duration; 11 | } 12 | 13 | private static final long TIMEOUT = 5 * 60 * 1_000; // 5 minutes 14 | 15 | private final Map stats = new HashMap<>(); 16 | private final long startTime = System.currentTimeMillis(); 17 | 18 | private long lastCleanup = 0; 19 | 20 | 21 | void add(long duration) { 22 | final Metric metric = new Metric(); 23 | metric.duration = duration; 24 | stats.put(metric, System.currentTimeMillis()); 25 | cleanup(); 26 | } 27 | 28 | double getAverage() { 29 | return stats.keySet().stream().mapToLong(m -> m.duration).average().orElse(0); 30 | } 31 | 32 | double getConnectionsPerSecond() { 33 | long timeWindow = Math.min(System.currentTimeMillis() - startTime, TIMEOUT); 34 | return (double) stats.size() / timeWindow; 35 | } 36 | 37 | private void cleanup() { 38 | if (lastCleanup + 1000 < System.currentTimeMillis()) { 39 | lastCleanup = System.currentTimeMillis(); 40 | stats.entrySet().removeIf(e -> e.getValue() < System.currentTimeMillis() - TIMEOUT); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rethinkdb-pool/src/main/java/de/jakobjarosch/rethinkdb/pool/PersistentConnection.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.pool; 2 | 3 | 4 | import com.rethinkdb.ast.ReqlAst; 5 | import com.rethinkdb.gen.exc.ReqlDriverError; 6 | import com.rethinkdb.model.OptArgs; 7 | import com.rethinkdb.net.Connection; 8 | 9 | import java.util.Optional; 10 | import java.util.concurrent.TimeoutException; 11 | 12 | public class PersistentConnection extends Connection { 13 | 14 | @FunctionalInterface 15 | public interface CloseCallback { 16 | void free(); 17 | } 18 | 19 | private final Connection wrapped; 20 | private final CloseCallback closeCallback; 21 | private boolean closed = false; 22 | 23 | public PersistentConnection(Connection wrapped) { 24 | this(wrapped, () -> { 25 | }); 26 | } 27 | 28 | public PersistentConnection(Connection wrapped, CloseCallback closeCallback) { 29 | super(new Builder()); 30 | this.wrapped = wrapped; 31 | this.closeCallback = closeCallback; 32 | } 33 | 34 | @Override 35 | public void close() { 36 | this.closed = true; 37 | closeCallback.free(); 38 | } 39 | 40 | @Override 41 | public void close(boolean shouldNoreplyWait) { 42 | this.closed = true; 43 | closeCallback.free(); 44 | } 45 | 46 | private void checkOpen() { 47 | if (closed) { 48 | throw new ReqlDriverError("Connection already returned to pool."); 49 | } 50 | } 51 | 52 | @Override 53 | public Optional db() { 54 | checkOpen(); 55 | return wrapped.db(); 56 | } 57 | 58 | @Override 59 | public void connect() throws TimeoutException { 60 | checkOpen(); 61 | throw new ReqlDriverError("Connect is not supported on an already connected pool connection."); 62 | } 63 | 64 | @Override 65 | public Connection reconnect() { 66 | throw new ReqlDriverError("Reconnect not supported, try to get a new connection from pool."); 67 | } 68 | 69 | @Override 70 | public Connection reconnect(boolean noreplyWait, Optional timeout) throws TimeoutException { 71 | throw new ReqlDriverError("Reconnect not supported, try to get a new connection from pool."); 72 | } 73 | 74 | @Override 75 | public boolean isOpen() { 76 | return !closed && wrapped.isOpen(); 77 | } 78 | 79 | @Override 80 | public void use(String db) { 81 | throw new ReqlDriverError("Switching database is not supported on a pool connection."); 82 | } 83 | 84 | @Override 85 | public Optional timeout() { 86 | return wrapped.timeout(); 87 | } 88 | 89 | @Override 90 | public void noreplyWait() { 91 | checkOpen(); 92 | wrapped.noreplyWait(); 93 | } 94 | 95 | @Override 96 | public T run(ReqlAst term, OptArgs globalOpts, Optional> pojoClass) { 97 | checkOpen(); 98 | return wrapped.run(term, globalOpts, pojoClass); 99 | } 100 | 101 | @Override 102 | public T run(ReqlAst term, OptArgs globalOpts, Optional> pojoClass, Optional timeout) { 103 | checkOpen(); 104 | return wrapped.run(term, globalOpts, pojoClass, timeout); 105 | } 106 | 107 | @Override 108 | public void runNoReply(ReqlAst term, OptArgs globalOpts) { 109 | checkOpen(); 110 | wrapped.runNoReply(term, globalOpts); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /rethinkdb-pool/src/main/java/de/jakobjarosch/rethinkdb/pool/RethinkDBPool.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.pool; 2 | 3 | 4 | import com.rethinkdb.gen.exc.ReqlDriverError; 5 | import com.rethinkdb.net.Connection; 6 | import org.apache.commons.pool2.impl.GenericObjectPool; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * The {@link RethinkDBPool} is able to serve RethinkDB connection in a multi-threaded environment. 12 | * Connections can be retrieved by calling {@link #getConnection()}. 13 | * When a connection should be returned to the pool, it is enough to call {@link Connection#close()}, 14 | * or use a try-resource scope. 15 | */ 16 | public class RethinkDBPool { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(RethinkDBPool.class); 19 | 20 | private final GenericObjectPool pool; 21 | private final int defaultTimeout; 22 | 23 | private final ConnectionStats stats = new ConnectionStats(); 24 | 25 | RethinkDBPool(GenericObjectPool pool, 26 | int defaultTimeout) { 27 | this.pool = pool; 28 | this.defaultTimeout = defaultTimeout; 29 | } 30 | 31 | @SuppressWarnings("unused") 32 | public void shutdown() { 33 | pool.close(); 34 | pool.setTimeBetweenEvictionRunsMillis(-1); 35 | } 36 | 37 | /** 38 | * @return Returns a free connection within the default timeout. 39 | * @throws ReqlDriverError Throws error when no free connection is available within the default timeout. 40 | */ 41 | @SuppressWarnings("unused") 42 | public Connection getConnection() { 43 | return getConnection(defaultTimeout); 44 | } 45 | 46 | /** 47 | * @param timeout Timeout in seconds. 48 | * @return Returns a free connection within the specified timeout. 49 | * @throws ReqlDriverError Throws error when no free connection is available within specified timeout. 50 | */ 51 | @SuppressWarnings("unused") 52 | public Connection getConnection(int timeout) { 53 | if (pool.isClosed()) { 54 | throw new ReqlDriverError("Pool is not started."); 55 | } 56 | 57 | try { 58 | final long startRetrieve = System.currentTimeMillis(); 59 | final Connection connection = pool.borrowObject(timeout * 1000); 60 | stats.add(System.currentTimeMillis() - startRetrieve); 61 | 62 | return new PersistentConnection(connection, () -> pool.returnObject(connection)); 63 | } catch (Exception e) { 64 | LOGGER.error("Failed to retrieve connection from pool.", e); 65 | throw new ReqlDriverError("Failed to retrieve connection", e); 66 | } 67 | } 68 | 69 | @SuppressWarnings("unused") 70 | public ConnectionPoolMetrics getMetrics() { 71 | return new ConnectionPoolMetrics(pool.getNumActive(), 72 | pool.getNumIdle(), 73 | pool.getMaxTotal(), 74 | stats.getConnectionsPerSecond(), 75 | stats.getAverage(), 76 | getHealth()); 77 | } 78 | 79 | private ConnectionPoolMetrics.PoolHealth getHealth() { 80 | if (pool.isClosed()) { 81 | return ConnectionPoolMetrics.PoolHealth.STOPPED; 82 | } else if (pool.getNumActive() >= pool.getMaxTotal()) { 83 | return ConnectionPoolMetrics.PoolHealth.FULL; 84 | } else { 85 | return ConnectionPoolMetrics.PoolHealth.HEALTHY; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /rethinkdb-pool/src/main/java/de/jakobjarosch/rethinkdb/pool/RethinkDBPoolBuilder.java: -------------------------------------------------------------------------------- 1 | package de.jakobjarosch.rethinkdb.pool; 2 | 3 | 4 | import com.rethinkdb.gen.exc.ReqlUserError; 5 | import com.rethinkdb.net.Connection; 6 | import org.apache.commons.pool2.impl.GenericObjectPool; 7 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 8 | 9 | public class RethinkDBPoolBuilder { 10 | 11 | private String hostname = "127.0.0.1"; 12 | private int port = 28015; 13 | private String username = "admin"; 14 | private String password = ""; 15 | private String database = "test"; 16 | 17 | private int maxConnections = 10; 18 | private int minFreeConnections = 1; 19 | private int maxFreeConnections = 5; 20 | private int timeout = 60; 21 | 22 | private GenericObjectPoolConfig config; 23 | 24 | @SuppressWarnings("unused") 25 | public RethinkDBPoolBuilder hostname(String hostname) { 26 | this.hostname = hostname; 27 | return this; 28 | } 29 | 30 | @SuppressWarnings("unused") 31 | public RethinkDBPoolBuilder port(int port) { 32 | if (port < 1 || port > 65535) 33 | throw new ReqlUserError("Constraint violated: 1 <= port <= 65535"); 34 | this.port = port; 35 | return this; 36 | } 37 | 38 | @SuppressWarnings("unused") 39 | public RethinkDBPoolBuilder username(String username) { 40 | this.username = username; 41 | return this; 42 | } 43 | 44 | @SuppressWarnings("unused") 45 | public RethinkDBPoolBuilder password(String password) { 46 | this.password = password; 47 | return this; 48 | } 49 | 50 | @SuppressWarnings("unused") 51 | public RethinkDBPoolBuilder database(String database) { 52 | this.database = database; 53 | return this; 54 | } 55 | 56 | @SuppressWarnings("unused") 57 | public RethinkDBPoolBuilder maxConnections(int maxConnections) { 58 | checkConnectionConstraints(maxConnections, this.minFreeConnections, this.maxFreeConnections); 59 | this.maxConnections = maxConnections; 60 | return this; 61 | } 62 | 63 | @SuppressWarnings("unused") 64 | public RethinkDBPoolBuilder minFreeConnections(int minFreeConnections) { 65 | checkConnectionConstraints(this.maxConnections, minFreeConnections, this.maxFreeConnections); 66 | this.minFreeConnections = minFreeConnections; 67 | return this; 68 | } 69 | 70 | @SuppressWarnings("unused") 71 | public RethinkDBPoolBuilder maxFreeConnections(int maxFreeConnections) { 72 | checkConnectionConstraints(this.maxConnections, this.minFreeConnections, maxFreeConnections); 73 | this.maxFreeConnections = maxFreeConnections; 74 | return this; 75 | } 76 | 77 | /** 78 | * When setting a custom config the basic configuration is ignored and the values of this config is used instead. 79 | * 80 | * @param config The config which should be applied to the pool. 81 | * @return Returns the builder 82 | */ 83 | @SuppressWarnings("unused") 84 | public RethinkDBPoolBuilder customConfig(GenericObjectPoolConfig config) { 85 | this.config = config; 86 | return this; 87 | } 88 | 89 | @SuppressWarnings("unused") 90 | public RethinkDBPoolBuilder timeout(int timeout) { 91 | if (timeout < 1) 92 | throw new ReqlUserError("Timeout must be at least 1 second"); 93 | this.timeout = timeout; 94 | return this; 95 | } 96 | 97 | @SuppressWarnings("unused") 98 | public RethinkDBPool build() { 99 | ConnectionFactory factory = new ConnectionFactory(hostname, port, username, password, database); 100 | 101 | 102 | GenericObjectPoolConfig config = this.config; 103 | if (config == null) { 104 | config = new GenericObjectPoolConfig(); 105 | config.setMaxTotal(maxConnections); 106 | config.setMinIdle(minFreeConnections); 107 | config.setMaxIdle(maxFreeConnections); 108 | } 109 | 110 | GenericObjectPool pool = new GenericObjectPool<>(factory, config); 111 | return new RethinkDBPool(pool, timeout); 112 | } 113 | 114 | private void checkConnectionConstraints(int maxConnections, int minFreeConnections, int maxFreeConnections) { 115 | if (maxConnections < minFreeConnections) 116 | throw new ReqlUserError("Constraint violated: maxConnections >= minFreeConnections."); 117 | if (maxConnections < maxFreeConnections) 118 | throw new ReqlUserError("Constraint violated: maxConnections >= maxFreeConnections"); 119 | if (maxFreeConnections < minFreeConnections) 120 | throw new ReqlUserError("Constraint violated: maxFreeConnections >= minFreeConnections"); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':rethinkdb-pool' 2 | include ':rethinkdb-orm-annotations' 3 | include ':rethinkdb-orm' 4 | include ':rethinkdb-orm-samples' 5 | --------------------------------------------------------------------------------