├── .github
└── workflows
│ ├── build-and-test.yml
│ ├── startup
│ └── 01_createUser.sql
│ └── test.sh
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.txt
├── NOTICE.txt
├── README.md
├── SECURITY.md
├── THIRD_PARTY_LICENSES.txt
├── pom.xml
├── sample
├── README.md
├── example-config.properties
├── pom.xml
└── src
│ └── main
│ └── java
│ └── oracle
│ └── r2dbc
│ └── samples
│ ├── DatabaseConfig.java
│ ├── DescriptorURL.java
│ ├── JdbcToR2dbc.java
│ └── TcpsConnectDemo.java
└── src
├── main
├── java
│ ├── module-info.java
│ └── oracle
│ │ └── r2dbc
│ │ ├── OracleR2dbcObject.java
│ │ ├── OracleR2dbcObjectMetadata.java
│ │ ├── OracleR2dbcOptions.java
│ │ ├── OracleR2dbcTypes.java
│ │ ├── OracleR2dbcWarning.java
│ │ ├── impl
│ │ ├── AsyncLock.java
│ │ ├── AsyncLockImpl.java
│ │ ├── DependentCounter.java
│ │ ├── Main.java
│ │ ├── NoOpAsyncLock.java
│ │ ├── OracleBatchImpl.java
│ │ ├── OracleConnectionFactoryImpl.java
│ │ ├── OracleConnectionFactoryMetadataImpl.java
│ │ ├── OracleConnectionFactoryProviderImpl.java
│ │ ├── OracleConnectionImpl.java
│ │ ├── OracleConnectionMetadataImpl.java
│ │ ├── OracleLargeObjects.java
│ │ ├── OracleR2dbcExceptions.java
│ │ ├── OracleReactiveJdbcAdapter.java
│ │ ├── OracleReadableImpl.java
│ │ ├── OracleReadableMetadataImpl.java
│ │ ├── OracleResultImpl.java
│ │ ├── OracleStatementImpl.java
│ │ ├── Publishers.java
│ │ ├── ReactiveJdbcAdapter.java
│ │ ├── ReadablesMetadata.java
│ │ ├── SqlParameterParser.java
│ │ ├── SqlTypeMap.java
│ │ ├── SuppliedOptionConnectionFactory.java
│ │ └── package-info.java
│ │ └── package-info.java
└── resources
│ └── META-INF
│ └── services
│ └── io.r2dbc.spi.ConnectionFactoryProvider
└── test
├── java
└── oracle
│ └── r2dbc
│ ├── impl
│ ├── OracleBatchImplTest.java
│ ├── OracleConnectionFactoryImplTest.java
│ ├── OracleConnectionFactoryProviderImplTest.java
│ ├── OracleConnectionImplTest.java
│ ├── OracleLargeObjectsTest.java
│ ├── OracleR2dbcExceptionsTest.java
│ ├── OracleReactiveJdbcAdapterTest.java
│ ├── OracleReadableImplTest.java
│ ├── OracleReadableMetadataImplTest.java
│ ├── OracleResultImplTest.java
│ ├── OracleRowMetadataImplTest.java
│ ├── OracleStatementImplTest.java
│ ├── SqlParameterParserTest.java
│ └── TypeMappingTest.java
│ ├── test
│ ├── DatabaseConfig.java
│ ├── OracleTestKit.java
│ └── TestUtils.java
│ └── util
│ ├── Awaits.java
│ ├── SharedConnectionFactory.java
│ └── TestContextFactory.java
└── resources
└── example-config.properties
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020, 2021, Oracle and/or its affiliates.
2 | #
3 | # This software is dual-licensed to you under the Universal Permissive License
4 | # (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
5 | # 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
6 | # either license.
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License");
9 | # you may not use this file except in compliance with the License.
10 | # You may obtain a copy of the License at
11 | #
12 | # https://www.apache.org/licenses/LICENSE-2.0
13 | #
14 | # Unless required by applicable law or agreed to in writing, software
15 | # distributed under the License is distributed on an "AS IS" BASIS,
16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | # See the License for the specific language governing permissions and
18 | # limitations under the License.
19 |
20 | # This script will start an Oracle Database inside a Docker container and then
21 | # execute the Oracle R2DBC test suite with a configuration that has it connect
22 | # to that database.
23 | #
24 | # This script makes no attempt to clean up. The docker container is left
25 | # running, and the database retains the test user and any other modifications
26 | # that the test suite may have performed.
27 | # It is assumed that the Github Runner will clean up any state this script
28 | # leaves behind.
29 |
30 | name: Build and Test Oracle R2DBC
31 |
32 | on:
33 | push:
34 | branches:
35 | - main
36 | - development
37 | pull_request:
38 | branches:
39 | - main
40 | - development
41 |
42 | jobs:
43 | # Builds the Oracle R2DBC Driver using Maven
44 | build:
45 | runs-on: ubuntu-latest
46 | steps:
47 | - uses: actions/checkout@v2
48 | - name: Set up JDK 11
49 | uses: actions/setup-java@v1
50 | with:
51 | java-version: 11
52 | - name: Build with Maven
53 | run: mvn -B package --file pom.xml -DskipTests=true
54 | # Tests the Oracle R2DBC Driver with an Oracle Database
55 | test:
56 | needs: build
57 | runs-on: ubuntu-latest
58 | steps:
59 | - uses: actions/checkout@v2
60 | - name: Test with Oracle Database
61 | run: cd $GITHUB_WORKSPACE/.github/workflows && bash test.sh
62 |
--------------------------------------------------------------------------------
/.github/workflows/startup/01_createUser.sql:
--------------------------------------------------------------------------------
1 | -- Copyright (c) 2020, 2021, Oracle and/or its affiliates.
2 | --
3 | -- This software is dual-licensed to you under the Universal Permissive License
4 | -- (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
5 | -- 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
6 | -- either license.
7 | --
8 | -- Licensed under the Apache License, Version 2.0 (the "License");
9 | -- you may not use this file except in compliance with the License.
10 | -- You may obtain a copy of the License at
11 | --
12 | -- https://www.apache.org/licenses/LICENSE-2.0
13 | --
14 | -- Unless required by applicable law or agreed to in writing, software
15 | -- distributed under the License is distributed on an "AS IS" BASIS,
16 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | -- See the License for the specific language governing permissions and
18 | -- limitations under the License.
19 |
20 | -- This script will start an Oracle Database inside a Docker container and then
21 | -- execute the Oracle R2DBC test suite with a configuration that has it connect
22 | -- to that database.
23 | --
24 | -- This script makes no attempt to clean up. The docker container is left
25 | -- running, and the database retains the test user and any other modifications
26 | -- that the test suite may have performed.
27 | -- It is assumed that the Github Runner will clean up any state this script
28 | -- leaves behind.
29 |
30 |
31 | -- This script will be run as sysdba connected to the container database. This
32 | -- script will create a test user in the xepdb1 pluggable database. The test
33 | -- user is granted permission to connect to the database, create/query/modify
34 | -- tables, and to query some V$ views:
35 | -- v$open_cursor (to verify if cursors are being closed).
36 | -- v$transaction (to verify if TransactionDefinitions are applied).
37 | -- v$session (to verify if VSESSION_* Options are applied).
38 | ALTER SESSION SET CONTAINER=xepdb1;
39 | CREATE ROLE r2dbc_test_role;
40 | GRANT SELECT ON v_$open_cursor TO r2dbc_test_role;
41 | GRANT SELECT ON v_$transaction TO r2dbc_test_role;
42 | GRANT SELECT ON v_$session TO r2dbc_test_role;
43 | GRANT CREATE VIEW TO r2dbc_test_role;
44 |
45 | CREATE USER test IDENTIFIED BY test;
46 | GRANT connect, resource, unlimited tablespace, r2dbc_test_role TO test;
47 | ALTER USER test DEFAULT TABLESPACE users;
48 | ALTER USER test TEMPORARY TABLESPACE temp;
49 |
--------------------------------------------------------------------------------
/.github/workflows/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright (c) 2020, 2021, Oracle and/or its affiliates.
4 | #
5 | # This software is dual-licensed to you under the Universal Permissive License
6 | # (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
7 | # 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
8 | # either license.
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 |
22 | # This script will start an Oracle Database inside a Docker container and then
23 | # execute the Oracle R2DBC test suite with a configuration that has it connect
24 | # to that database.
25 | #
26 | # This script makes no attempt to clean up. The docker container is left
27 | # running, and the database retains the test user and any other modifications
28 | # that the test suite may have performed.
29 | # It is assumed that the Github Runner will clean up any state this script
30 | # leaves behind.
31 |
32 |
33 | # The startup directory is mounted as a volume inside the docker container.
34 | # The container's entry point script will execute any .sh and .sql scripts
35 | # it finds under /opt/oracle/scripts/startup. The startup scripts are run
36 | # after the database instance is active.A numeric prefix on the script name
37 | # determines the order in which scripts are run. The final script, prefixed
38 | # with "99_" will create a file named "done" in the mounted volumn. When the
39 | # "done" file exists, this signals that the database is active and that all
40 | # startup scripts have completed.
41 | startUpScripts=$PWD/startup
42 | startUpMount=/opt/oracle/scripts/startup
43 | echo "touch $startUpMount/done" > $startUpScripts/99_done.sh
44 |
45 |
46 | # The oracle/docker-images repo is cloned. This repo provides Dockerfiles along
47 | # with a handy script to build images of Oracle Database. For now, this script
48 | # is just going to build an 18.4.0 XE image, because this can be done in an
49 | # automated fashion, without having to accept license agreements required by
50 | # newer versions like 19 and 21.
51 | # TODO: Also test with newer database versions
52 | git clone https://github.com/oracle/docker-images.git
53 | cd docker-images/OracleDatabase/SingleInstance/dockerfiles/
54 | ./buildContainerImage.sh -v 18.4.0 -x
55 |
56 | # Run the image in a detached container
57 | # The startup directory is mounted. It contains a createUser.sql script that
58 | # creates a test user. The docker container will run this script once the
59 | # database has started.
60 | # The database port number, 1521, is mapped to the host system. The Oracle
61 | # R2DBC test suite is configured to connect with this port.
62 | docker run --name test_db --detach --rm -p 1521:1521 -v $startUpScripts:$startUpMount oracle/database:18.4.0-xe
63 |
64 | # Wait for the database instance to start. The final startup script will create
65 | # a file named "done" in the startup directory. When that file exists, it means
66 | # the database is ready for testing.
67 | echo "Waiting for database to start..."
68 | until [ -f $startUpScripts/done ]
69 | do
70 | docker logs --since 3s test_db
71 | sleep 3
72 | done
73 |
74 | # Create a configuration file and run the tests. The service name, "xepdb1",
75 | # is always created for the 18.4.0 XE database, but it would probably change
76 | # for other database versions (TODO). The test user is created by the
77 | # startup/01_createUser.sql script
78 | cd $GITHUB_WORKSPACE
79 | echo "DATABASE=xepdb1" > src/test/resources/config.properties
80 | echo "HOST=localhost" >> src/test/resources/config.properties
81 | echo "PORT=1521" >> src/test/resources/config.properties
82 | echo "USER=test" >> src/test/resources/config.properties
83 | echo "PASSWORD=test" >> src/test/resources/config.properties
84 | echo "CONNECT_TIMEOUT=240" >> src/test/resources/config.properties
85 | echo "SQL_TIMEOUT=240" >> src/test/resources/config.properties
86 | mvn clean compile test
87 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | .DS_Store
3 | *.iml
4 | /src/test/resources/config.properties
5 | /sample/target/
6 | /sample/config.properties
7 | .github/workflows/startup/99_done.sh
8 | .github/workflows/startup/done
9 | .idea/
10 | *.swp
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this repository
2 |
3 | We welcome your contributions! There are multiple ways to contribute.
4 |
5 | ## Opening issues
6 |
7 | For bugs or enhancement requests, please file a GitHub issue unless it's
8 | security related. When filing a bug remember that the better written the bug is,
9 | the more likely it is to be fixed. If you think you've found a security
10 | vulnerability, do not raise a GitHub issue and follow the instructions in our
11 | [security policy](./SECURITY.md).
12 |
13 | ## Contributing code
14 |
15 | We welcome your code contributions. Before submitting code via a pull request,
16 | you will need to have signed the [Oracle Contributor Agreement][OCA] (OCA) and
17 | your commits need to include the following line using the name and e-mail
18 | address you used to sign the OCA:
19 |
20 | ```text
21 | Signed-off-by: Your Name
22 | ```
23 |
24 | This can be automatically added to pull requests by committing with `--sign-off`
25 | or `-s`, e.g.
26 |
27 | ```text
28 | git commit --signoff
29 | ```
30 |
31 | Only pull requests from committers that can be verified as having signed the OCA
32 | can be accepted.
33 |
34 | ## Pull request process
35 |
36 | 1. Ensure there is an issue created to track and discuss the fix or enhancement
37 | you intend to submit.
38 | 1. Fork this repository.
39 | 1. Create a branch in your fork to implement the changes. We recommend using
40 | the issue number as part of your branch name, e.g. `1234-fixes`.
41 | 1. Ensure that any documentation is updated with the changes that are required
42 | by your change.
43 | 1. Ensure that any samples are updated if the base image has been changed.
44 | 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly
45 | what your changes are meant to do and provide simple steps on how to validate.
46 | your changes. Ensure that you reference the issue you created as well.
47 | 1. We will assign the pull request to 2-3 people for review before it is merged.
48 |
49 | ## Code of conduct
50 |
51 | Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd
52 | like more specific guidelines, see the [Contributor Covenant Code of Conduct][COC].
53 |
54 | [OCA]: https://oca.opensource.oracle.com
55 | [COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/
56 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
2 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting security vulnerabilities
2 |
3 | Oracle values the independent security research community and believes that
4 | responsible disclosure of security vulnerabilities helps us ensure the security
5 | and privacy of all our users.
6 |
7 | Please do NOT raise a GitHub Issue to report a security vulnerability. If you
8 | believe you have found a security vulnerability, please submit a report to
9 | [secalert_us@oracle.com][1] preferably with a proof of concept. Please review
10 | some additional information on [how to report security vulnerabilities to Oracle][2].
11 | We encourage people who contact Oracle Security to use email encryption using
12 | [our encryption key][3].
13 |
14 | We ask that you do not use other channels or contact the project maintainers
15 | directly.
16 |
17 | Non-vulnerability related security issues including ideas for new or improved
18 | security features are welcome on GitHub Issues.
19 |
20 | ## Security updates, alerts and bulletins
21 |
22 | Security updates will be released on a regular cadence. Many of our projects
23 | will typically release security fixes in conjunction with the
24 | Oracle Critical Patch Update program. Additional
25 | information, including past advisories, is available on our [security alerts][4]
26 | page.
27 |
28 | ## Security-related information
29 |
30 | We will provide security related information such as a threat model, considerations
31 | for secure use, or any known security issues in our documentation. Please note
32 | that labs and sample code are intended to demonstrate a concept and may not be
33 | sufficiently hardened for production use.
34 |
35 | [1]: mailto:secalert_us@oracle.com
36 | [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html
37 | [3]: https://www.oracle.com/security-alerts/encryptionkey.html
38 | [4]: https://www.oracle.com/security-alerts/
39 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
25 | 4.0.0
26 |
27 | com.oracle.database.r2dbc
28 | oracle-r2dbc
29 | 1.3.0
30 | oracle-r2dbc
31 |
32 | Oracle R2DBC Driver implementing version 1.0.0 of the R2DBC SPI for Oracle Database.
33 |
34 |
35 | https://github.com/oracle/oracle-r2dbc
36 |
37 | 2019
38 |
39 |
40 | Universal Permissive License v1.0
41 | https://opensource.org/licenses/UPL
42 |
43 |
44 | Apache License, Version 2.0
45 | https://www.apache.org/licenses/LICENSE-2.0
46 |
47 |
48 |
49 |
50 | Oracle America, Inc.
51 | http://www.oracle.com
52 |
53 |
54 |
55 | https://github.com/oracle/oracle-r2dbc.git
56 |
57 | scm:git:https://github.com/oracle/oracle-r2dbc.git
58 |
59 | scm:git:git@github.com:oracle/oracle-r2dbc.git
60 |
61 |
62 | GitHub
63 | https://github.com/oracle/oracle-r2dbc/issues
64 |
65 |
66 |
67 | 11
68 | 23.6.0.24.10
69 | 1.0.0.RELEASE
70 | 3.6.11
71 | 1.0.3
72 | 5.9.1
73 | 5.3.19
74 | UTF-8
75 |
76 |
77 |
78 |
79 |
80 | org.apache.maven.plugins
81 | maven-compiler-plugin
82 | 3.8.1
83 |
84 |
85 | -Xlint:all
86 | -Xlint:-options
87 | -Xlint:-processing
88 | -Xlint:-serial
89 |
90 | --add-modules
91 | java.naming
92 |
93 | true
94 | ${java.version}
95 |
96 |
97 |
98 | org.apache.maven.plugins
99 | maven-jar-plugin
100 | 3.2.0
101 |
102 |
103 |
104 | Oracle R2DBC
105 |
106 | ${project.version}
107 |
108 | Oracle Corporation
109 |
110 | R2DBC - Reactive Relational Database Connectivity
111 |
112 | ${r2dbc.version}
113 |
114 | Pivotal Software, Inc
115 |
116 |
117 | Oracle R2DBC ${project.version} compiled with JDK ${java.vm.version} from ${java.vm.vendor} on ${maven.build.timestamp}
118 |
119 |
120 | oracle.r2dbc.impl.Main
121 |
122 |
123 |
124 |
125 |
126 |
127 | org.apache.maven.plugins
128 | maven-source-plugin
129 | 3.2.1
130 |
131 |
132 | org.apache.maven.plugins
133 | maven-deploy-plugin
134 | 3.0.0-M1
135 |
136 |
137 | org.apache.maven.plugins
138 | maven-install-plugin
139 | 3.0.0-M1
140 |
141 |
142 | org.apache.maven.plugins
143 | maven-clean-plugin
144 | 3.1.0
145 |
146 |
147 | org.apache.maven.plugins
148 | maven-resources-plugin
149 | 3.2.0
150 |
151 |
152 | org.apache.maven.plugins
153 | maven-surefire-plugin
154 | 3.0.0-M5
155 |
156 |
157 |
158 | **/*Test.java
159 | **/*TestKit.java
160 |
161 |
162 | --add-reads com.oracle.database.r2dbc=java.naming
163 |
164 |
165 |
166 | org.apache.maven.plugins
167 | maven-javadoc-plugin
168 | 3.2.0
169 |
170 | Oracle R2DBC ${project.version}
171 |
174 | package
175 |
181 | src/main/java
182 |
183 | https://r2dbc.io/spec/${r2dbc.version}/api/
184 | https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/
185 | https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/
186 |
187 |
188 |
189 | implNote
190 | a
191 | Implementation Note:
192 |
193 |
194 | implSpec
195 | a
196 | Implementation Requirements:
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 | ${project.basedir}
205 |
206 | NOTICE.txt
207 | LICENSE.txt
208 | THIRD_PARTY_LICENSES.txt
209 |
210 | META-INF
211 |
212 |
213 | ${project.basedir}/src/main/resources/META-INF/services/
214 |
215 | io.r2dbc.spi.ConnectionFactoryProvider
216 |
217 | META-INF/services/
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 | io.r2dbc
227 | r2dbc-spi
228 | ${r2dbc.version}
229 |
230 |
231 | com.oracle.database.jdbc
232 | ojdbc11
233 | ${ojdbc.version}
234 |
235 |
236 | io.projectreactor
237 | reactor-core
238 | ${reactor.version}
239 |
240 |
241 | org.reactivestreams
242 | reactive-streams
243 | ${reactive-streams.version}
244 |
245 |
246 |
247 |
248 | io.r2dbc
249 | r2dbc-spi-test
250 | ${r2dbc.version}
251 | test
252 |
253 |
254 | org.junit.jupiter
255 | junit-jupiter-api
256 | ${junit.version}
257 | test
258 |
259 |
260 | org.junit.jupiter
261 | junit-jupiter-engine
262 | ${junit.version}
263 | test
264 |
265 |
266 | org.springframework
267 | spring-jdbc
268 | ${spring-jdbc.version}
269 | test
270 |
271 |
272 |
273 |
274 |
275 | publication
276 |
277 |
278 | release
279 |
280 |
281 |
282 |
283 |
284 | org.apache.maven.plugins
285 | maven-javadoc-plugin
286 |
287 |
288 | attach-javadocs
289 |
290 | jar
291 |
292 |
293 | true
294 |
295 |
296 |
297 |
298 |
299 | org.apache.maven.plugins
300 | maven-source-plugin
301 |
302 |
303 | attach-sources
304 |
305 | jar
306 |
307 |
308 | true
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
326 |
--------------------------------------------------------------------------------
/sample/README.md:
--------------------------------------------------------------------------------
1 | # Oracle R2DBC code examples
2 | A simple R2DBC program that connects to Autonomous Database and executes a query
3 |
4 | # Building manually
5 |
6 | | | Task | Command |
7 | | ------ | ----- | ------------------------------------------------ |
8 | | Maven | Build | `mvn clean install` |
9 | | | Run | `mvn exec:java` |
10 | | Gradle | Build | `gradle build` |
11 | | | Run | `gradle run` |
12 |
13 |
--------------------------------------------------------------------------------
/sample/example-config.properties:
--------------------------------------------------------------------------------
1 | # Values in this properties file configure how sample code connects to a # database. # This file contains example values. Create a copy named config.properties in
2 | # /sample and change the example values to actual values for your test database.
3 |
4 | # Host name of a test database
5 | HOST=db.host.example.com
6 |
7 | # Port number of a test database
8 | PORT=1521
9 |
10 | # Service name of a test database
11 | DATABASE=db.service.name
12 |
13 | # User name authenticated by a test database
14 | USER=db_user
15 |
16 | # Password authenticated by a test database
17 | PASSWORD=db_password
18 |
19 | # File system path to a directory containing an Oracle Wallet file
20 | WALLET_LOCATION=/path/to/wallet
21 |
--------------------------------------------------------------------------------
/sample/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
25 | 4.0.0
26 | com.oracle.database.r2dbc
27 | oracle-r2dbc-samples
28 | 1.2.0
29 | oracle-r2dbc-samples
30 |
31 | Code examples for the Oracle R2DBC Driver
32 |
33 |
34 | https://github.com/oracle/oracle-r2dbc
35 |
36 | 2019
37 |
38 |
39 | Universal Permissive License v1.0
40 | https://opensource.org/licenses/UPL
41 |
42 |
43 | Apache License, Version 2.0
44 | https://www.apache.org/licenses/LICENSE-2.0
45 |
46 |
47 |
48 |
49 | Oracle America, Inc.
50 | http://www.oracle.com
51 |
52 |
53 |
54 | https://github.com/oracle/oracle-r2dbc.git
55 |
56 | scm:git:https://github.com/oracle/oracle-r2dbc.git
57 |
58 | scm:git:git@github.com:oracle/oracle-r2dbc.git
59 |
60 |
61 | GitHub
62 | https://github.com/oracle/oracle-r2dbc/issues
63 |
64 |
65 |
66 | 11
67 | 1.2.0
68 | 3.5.11
69 |
70 |
71 |
72 |
73 |
74 | org.apache.maven.plugins
75 | maven-compiler-plugin
76 | 3.8.1
77 |
78 |
79 | -Xlint:all
80 | -Xlint:-options
81 | -Xlint:-processing
82 | -Xlint:-serial
83 |
84 | true
85 | ${java.version}
86 |
87 |
88 |
89 | org.apache.maven.plugins
90 | maven-clean-plugin
91 | 3.1.0
92 |
93 |
94 |
95 |
96 |
97 |
98 | com.oracle.database.r2dbc
99 | oracle-r2dbc
100 | ${oracle-r2dbc.version}
101 |
102 |
103 | io.projectreactor
104 | reactor-test
105 | ${reactor.version}
106 | test
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/sample/src/main/java/oracle/r2dbc/samples/DatabaseConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.samples;
23 |
24 | import java.io.IOException;
25 | import java.io.UncheckedIOException;
26 | import java.nio.file.Files;
27 | import java.nio.file.Path;
28 | import java.util.Properties;
29 |
30 | /**
31 | *
32 | * Configuration for connecting code samples to an Oracle Database instance.
33 | *
34 | * The configuration is read from a properties file in the current directory
35 | * by default, or from a file specified as
36 | * -DCONFIG_FILE=/path/to/your/config.properties
37 | *
38 | */
39 | public class DatabaseConfig {
40 |
41 | /** Path to a configuration file: config.properties */
42 | private static final Path CONFIG_PATH =
43 | Path.of(System.getProperty("CONFIG_FILE", "config.properties"));
44 |
45 | /** Configuration that is read from a file at {@link #CONFIG_PATH} */
46 | private static final Properties CONFIG;
47 | static {
48 | try (var fileStream = Files.newInputStream(CONFIG_PATH)) {
49 | CONFIG = new Properties();
50 | CONFIG.load(fileStream);
51 | }
52 | catch (IOException readFailure) {
53 | throw new UncheckedIOException(readFailure);
54 | }
55 | }
56 |
57 | /** Host name where an Oracle Database instance is running */
58 | static final String HOST = CONFIG.getProperty("HOST");
59 |
60 | /** Port number where an Oracle Database instance is listening */
61 | static final int PORT = Integer.parseInt(CONFIG.getProperty("PORT"));
62 |
63 | /** Service name of an Oracle Database */
64 | static final String SERVICE_NAME = CONFIG.getProperty("DATABASE");
65 |
66 | /** User name that connects to an Oracle Database */
67 | static final String USER = CONFIG.getProperty("USER");
68 |
69 | /** Password of the user that connects to an Oracle Database */
70 | static final String PASSWORD = CONFIG.getProperty("PASSWORD");
71 |
72 | /** The file system path of a wallet directory */
73 | static final String WALLET_LOCATION =
74 | CONFIG.getProperty("WALLET_LOCATION");
75 | }
76 |
--------------------------------------------------------------------------------
/sample/src/main/java/oracle/r2dbc/samples/DescriptorURL.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.samples;
23 |
24 | import io.r2dbc.spi.ConnectionFactories;
25 | import io.r2dbc.spi.ConnectionFactoryOptions;
26 | import oracle.r2dbc.OracleR2dbcOptions;
27 | import reactor.core.publisher.Mono;
28 |
29 | import static oracle.r2dbc.samples.DatabaseConfig.HOST;
30 | import static oracle.r2dbc.samples.DatabaseConfig.PORT;
31 | import static oracle.r2dbc.samples.DatabaseConfig.SERVICE_NAME;
32 | import static oracle.r2dbc.samples.DatabaseConfig.USER;
33 | import static oracle.r2dbc.samples.DatabaseConfig.PASSWORD;
34 |
35 | /**
36 | * This code example shows how to use TNS descriptor URLs with Oracle R2DBC.
37 | * The TNS descriptor has the form:
38 | *
39 | * (DESCRIPTION=...)
40 | *
41 | * The full syntax of the TNS descriptor is described in the
42 | *
43 | * Oracle Net Services Administrator's Guide
44 | *
45 | */
46 | public class DescriptorURL {
47 |
48 | /**
49 | * A TNS descriptor specifying the HOST, PORT, and SERVICE_NAME read from
50 | * {@link DatabaseConfig}. These values can be configured in a
51 | * config.properties file of the current directory.
52 | */
53 | private static final String DESCRIPTOR = "(DESCRIPTION=" +
54 | "(ADDRESS=(HOST="+HOST+")(PORT="+PORT+")(PROTOCOL=tcp))" +
55 | "(CONNECT_DATA=(SERVICE_NAME="+SERVICE_NAME+")))";
56 |
57 | public static void main(String[] args) {
58 | // A descriptor may appear in the query section of an R2DBC URL:
59 | String r2dbcUrl = "r2dbc:oracle://?oracle.r2dbc.descriptor="+DESCRIPTOR;
60 | Mono.from(ConnectionFactories.get(ConnectionFactoryOptions.parse(r2dbcUrl)
61 | .mutate()
62 | .option(ConnectionFactoryOptions.USER, USER)
63 | .option(ConnectionFactoryOptions.PASSWORD, PASSWORD)
64 | .build())
65 | .create())
66 | .flatMapMany(connection ->
67 | Mono.from(connection.createStatement(
68 | "SELECT 'Connected with TNS descriptor' FROM sys.dual")
69 | .execute())
70 | .flatMapMany(result ->
71 | result.map(row -> row.get(0, String.class)))
72 | .concatWith(Mono.from(connection.close()).cast(String.class)))
73 | .toStream()
74 | .forEach(System.out::println);
75 |
76 | // A descriptor may also be specified as an Option
77 | Mono.from(ConnectionFactories.get(ConnectionFactoryOptions.builder()
78 | .option(ConnectionFactoryOptions.DRIVER, "oracle")
79 | .option(OracleR2dbcOptions.DESCRIPTOR, DESCRIPTOR)
80 | .option(ConnectionFactoryOptions.USER, USER)
81 | .option(ConnectionFactoryOptions.PASSWORD, PASSWORD)
82 | .build())
83 | .create())
84 | .flatMapMany(connection ->
85 | Mono.from(connection.createStatement(
86 | "SELECT 'Connected with TNS descriptor' FROM sys.dual")
87 | .execute())
88 | .flatMapMany(result ->
89 | result.map((row, metadata) -> row.get(0, String.class)))
90 | .concatWith(Mono.from(connection.close()).cast(String.class)))
91 | .toStream()
92 | .forEach(System.out::println);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/sample/src/main/java/oracle/r2dbc/samples/TcpsConnectDemo.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.samples;
23 |
24 | import io.r2dbc.spi.ConnectionFactories;
25 | import io.r2dbc.spi.ConnectionFactory;
26 | import io.r2dbc.spi.ConnectionFactoryOptions;
27 | import io.r2dbc.spi.Option;
28 | import oracle.jdbc.OracleConnection;
29 | import org.reactivestreams.Publisher;
30 | import reactor.core.publisher.Flux;
31 | import reactor.core.publisher.Mono;
32 |
33 | import java.net.URI;
34 | import java.net.URISyntaxException;
35 | import java.time.Duration;
36 |
37 | import static oracle.r2dbc.samples.DatabaseConfig.HOST;
38 | import static oracle.r2dbc.samples.DatabaseConfig.PASSWORD;
39 | import static oracle.r2dbc.samples.DatabaseConfig.PORT;
40 | import static oracle.r2dbc.samples.DatabaseConfig.SERVICE_NAME;
41 | import static oracle.r2dbc.samples.DatabaseConfig.USER;
42 | import static oracle.r2dbc.samples.DatabaseConfig.WALLET_LOCATION;
43 |
44 | /**
45 | * Sample code that uses an Oracle Wallet to authenticate with an Oracle
46 | * Database instance. To read the wallet, the Oracle PKI library must be on the
47 | * JVM classpath when this demo is run. The maven coordinates for Oracle PKI
48 | * are:
49 | *
66 | *
67 | * Database connection configuration, including the wallet location, must be
68 | * written to a file named "config.properties" that exists in the current
69 | * directory when this demo is run. There is an example config.properties
70 | * file in the /sample directory (relative to the root this repository).
71 | */
72 | public class TcpsConnectDemo {
73 |
74 |
75 | public static void main(String[] args) throws URISyntaxException {
76 |
77 | // The R2DBC URL format can configure a TCPS/SSL/TLS enabled connection
78 | // by specifying the "r2dbcs" schema (rather than "r2dbc"). The path to a
79 | // wallet file is specified in the query section of the URL as the value of
80 | // an Oracle JDBC connection property: "oracle.net.wallet_location"
81 | //
82 | // Special characters that appear in an R2DBC URL must be escaped using
83 | // percent encoding. Characters that don't require an escape are limited
84 | // to a-z, A-Z, 0-9, and a few other symbols. All other characters must be
85 | // encoded as a percent sign (%) followed by the hexadecimal digits of
86 | // the character's byte value. Multi-byte characters are escaped as a
87 | // sequence percent encodings, each representing one byte.
88 | //
89 | // Fortunately, the java.net.URI class implements percent encoding, so it
90 | // is used by the code below to generate an R2DBC URL.
91 | String r2dbcsUrl = new URI("r2dbcs:oracle", // schema
92 | USER + ":" + PASSWORD, // userInfo
93 | HOST, // host
94 | PORT, // port
95 | "/" + SERVICE_NAME, // path
96 | "oracle.net.wallet_location=" + WALLET_LOCATION
97 | + "&oracle.jdbc.fanEnabled=false", // query
98 | null) // fragment
99 | .toASCIIString();
100 |
101 | // The URI.toASCIIString() call above returns a URL like this:
102 | // r2dbcs:oracle:user:p%40ssword!:host.example.com:1522/service.name?oracle.net.wallet_location=/path/to/wallet/&oracle.jdbc.fanEnabled=false
103 |
104 | // With the r2dbcs URL, open a connection and execute SQL
105 | Flux.from(sayHello(ConnectionFactories.get(r2dbcsUrl)))
106 | .doOnNext(System.out::println)
107 | .blockLast(Duration.ofSeconds(60));
108 |
109 | // As an alternative to the URL, a ConnectionFactoryOptions.Builder
110 | // offers a programmatic API to configure a ConnectionFactory. If the
111 | // wallet is password protected, then the Builder API must be used to
112 | // configure the password. Passwords should never be configured in the URL.
113 | ConnectionFactoryOptions options =
114 | ConnectionFactoryOptions.builder()
115 | .option(ConnectionFactoryOptions.DRIVER, "oracle")
116 | .option(ConnectionFactoryOptions.HOST, HOST)
117 | .option(ConnectionFactoryOptions.PORT, PORT)
118 | .option(ConnectionFactoryOptions.DATABASE, SERVICE_NAME)
119 | .option(ConnectionFactoryOptions.USER, USER)
120 | .option(ConnectionFactoryOptions.PASSWORD, PASSWORD)
121 | // To configure a TCPS/SSL/TLS enabled ConnectionFactory, set the SSL
122 | // option to true, and then specify the path to a wallet location...
123 | .option(ConnectionFactoryOptions.SSL, true)
124 | // The Oracle JDBC wallet location connection property can be
125 | // configured as an R2DBC Option. The connection property name,
126 | // "oracle.net.wallet_location", is the argument to the
127 | // Option.valueOf(String) factory method.
128 | // This is an extended option; It is only recognized by the Oracle
129 | // R2DBC Driver. Other R2DBC drivers might use a different Option to
130 | // configure their TLS certificates.
131 | .option(Option.valueOf(
132 | OracleConnection.CONNECTION_PROPERTY_WALLET_LOCATION),
133 | WALLET_LOCATION)
134 | .option(Option.valueOf(
135 | OracleConnection.CONNECTION_PROPERTY_FAN_ENABLED),
136 | "false")
137 | // To set a wallet password, the Option.sensitiveValueOf(String)
138 | // factory method is used. Any property that stores a password in
139 | // clear text needs to be handled carefully; This factory method is
140 | // used for Options that are configured with sensitive information.
141 | // .option(Option.sensitiveValueOf(
142 | // OracleConnection.CONNECTION_PROPERTY_WALLET_PASSWORD),
143 | // readPasswordSecurely()) // TODO: Prompt user to type password?
144 | .build();
145 |
146 | // Open a connection and execute SQL
147 | Flux.from(sayHello(
148 | ConnectionFactories.get(options)))
149 | .doOnNext(System.out::println)
150 | .blockLast(Duration.ofSeconds(60));
151 | }
152 |
153 | /**
154 | * Publishes the result of opening a connection and executing a SQL query
155 | * that returns a greeting message.
156 | * @param connectionFactory Factory configured to open connections
157 | * @return A publisher that emits a greeting
158 | */
159 | static Publisher sayHello(ConnectionFactory connectionFactory) {
160 | return Mono.from(connectionFactory.create())
161 | .flatMapMany(connection ->
162 | Flux.from(connection.createStatement(
163 | "SELECT 'Hello, Oracle' FROM sys.dual")
164 | .execute())
165 | .flatMap(result ->
166 | result.map((row, metadata) -> row.get(0, String.class)))
167 | .concatWith(Mono.from(
168 | connection.close()).cast(String.class)));
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | /**
22 | * Implements the R2DBC SPI for Oracle Database.
23 | *
24 | * @provides io.r2dbc.spi.ConnectionFactoryProvider
25 | * @since 0.1.0
26 | */
27 | module com.oracle.database.r2dbc {
28 |
29 | provides io.r2dbc.spi.ConnectionFactoryProvider
30 | with oracle.r2dbc.impl.OracleConnectionFactoryProviderImpl;
31 |
32 | requires java.sql;
33 | requires com.oracle.database.jdbc;
34 | requires reactor.core;
35 | requires transitive org.reactivestreams;
36 | requires transitive r2dbc.spi;
37 |
38 | exports oracle.r2dbc;
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/OracleR2dbcObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2022, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | package oracle.r2dbc;
22 |
23 | /**
24 | *
25 | * A {@link io.r2dbc.spi.Readable} that represents an instance of a user
26 | * defined OBJECT type.
27 | *
28 | * An OBJECT returned by a {@link io.r2dbc.spi.Result} may be mapped to an
29 | * {@code OracleR2dbcObject}:
30 | *
45 | * As seen in the example above, the values of an OBJECT's attributes may be
46 | * accessed by name with {@link #get(String)} or {@link #get(String, Class)}.
47 | * Alternatively, attribute values may be accessed by index with {@link #get(int)} or
48 | * {@link #get(int, Class)}. The {@code get} methods support all standard
49 | * SQL-to-Java type mappings defined by the
50 | *
51 | * R2DBC Specification.
52 | *
53 | *
54 | * Instances of {@code OracleR2dbcObject} may be set as a bind value when
55 | * passed to {@link io.r2dbc.spi.Statement#bind(int, Object)} or
56 | * {@link io.r2dbc.spi.Statement#bind(String, Object)}:
57 | *
69 | */
70 | public interface OracleR2dbcObject extends io.r2dbc.spi.Readable {
71 |
72 | /**
73 | * Returns metadata for the attributes of this OBJECT.
74 | * @return The metadata of this OBJECT's attributes. Not null.
75 | */
76 | OracleR2dbcObjectMetadata getMetadata();
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/OracleR2dbcObjectMetadata.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2022, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | package oracle.r2dbc;
22 |
23 | import io.r2dbc.spi.ReadableMetadata;
24 |
25 | import java.util.List;
26 | import java.util.NoSuchElementException;
27 |
28 | /**
29 | * Represents the metadata for attributes of an OBJECT. Metadata for attributes
30 | * can either be retrieved by index or by name. Attribute indexes are
31 | * {@code 0}-based. Retrieval by attribute name is case-insensitive.
32 | */
33 | public interface OracleR2dbcObjectMetadata {
34 |
35 | /**
36 | * Returns the type of the OBJECT which metadata is provided for.
37 | * @return The type of the OBJECT. Not null.
38 | */
39 | OracleR2dbcTypes.ObjectType getObjectType();
40 |
41 | /**
42 | * Returns the {@link ReadableMetadata} for one attribute.
43 | *
44 | * @param index the attribute index starting at 0
45 | * @return the {@link ReadableMetadata} for one attribute. Not null.
46 | * @throws IndexOutOfBoundsException if {@code index} is out of range
47 | * (negative or equals/exceeds {@code getParameterMetadatas().size()})
48 | */
49 | ReadableMetadata getAttributeMetadata(int index);
50 |
51 | /**
52 | * Returns the {@link ReadableMetadata} for one attribute.
53 | *
54 | * @param name the name of the attribute. Not null. Parameter names are
55 | * case-insensitive.
56 | * @return the {@link ReadableMetadata} for one attribute. Not null.
57 | * @throws IllegalArgumentException if {@code name} is {@code null}
58 | * @throws NoSuchElementException if there is no attribute with the
59 | * {@code name}
60 | */
61 | ReadableMetadata getAttributeMetadata(String name);
62 |
63 | /**
64 | * Returns the {@link ReadableMetadata} for all attributes.
65 | *
66 | * @return the {@link ReadableMetadata} for all attributes. Not null.
67 | */
68 | List extends ReadableMetadata> getAttributeMetadatas();
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/OracleR2dbcTypes.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | package oracle.r2dbc;
22 |
23 | import io.r2dbc.spi.Parameter;
24 | import io.r2dbc.spi.R2dbcType;
25 | import io.r2dbc.spi.Result;
26 | import io.r2dbc.spi.Statement;
27 | import io.r2dbc.spi.Type;
28 | import oracle.sql.json.OracleJsonObject;
29 |
30 | import java.nio.ByteBuffer;
31 | import java.sql.RowId;
32 | import java.time.Duration;
33 | import java.time.LocalDateTime;
34 | import java.time.Period;
35 | import java.util.Objects;
36 |
37 | /**
38 | * SQL types supported by Oracle Database that are not defined as standard types
39 | * by {@link io.r2dbc.spi.R2dbcType}.
40 | */
41 | public final class OracleR2dbcTypes {
42 |
43 | private OracleR2dbcTypes() {}
44 |
45 | /**
46 | * A 64-bit, double-precision floating-point number data type.
47 | */
48 | public static final Type BINARY_DOUBLE =
49 | new TypeImpl(Double.class, "BINARY_DOUBLE");
50 |
51 | /**
52 | * A 32-bit, single-precision floating-point number data type.
53 | */
54 | public static final Type BINARY_FLOAT =
55 | new TypeImpl(Float.class, "BINARY_FLOAT");
56 |
57 | /**
58 | * Stores a period of time in days, hours, minutes, and seconds.
59 | */
60 | public static final Type INTERVAL_DAY_TO_SECOND =
61 | new TypeImpl(Duration.class, "INTERVAL DAY TO SECOND");
62 |
63 | /**
64 | * Stores a period of time in years and months.
65 | */
66 | public static final Type INTERVAL_YEAR_TO_MONTH =
67 | new TypeImpl(Period.class, "INTERVAL YEAR TO MONTH");
68 |
69 | /**
70 | * Stores a JSON value.
71 | */
72 | public static final Type JSON =
73 | new TypeImpl(OracleJsonObject.class, "JSON");
74 |
75 | /**
76 | * Character data of variable length up to 2 gigabytes.
77 | */
78 | public static final Type LONG =
79 | new TypeImpl(String.class, "LONG");
80 |
81 | /**
82 | * Raw binary data of variable length up to 2 gigabytes.
83 | */
84 | public static final Type LONG_RAW =
85 | new TypeImpl(ByteBuffer.class, "LONG RAW");
86 |
87 | /**
88 | * Base 64 string representing the unique address of a row in its table.
89 | */
90 | public static final Type ROWID =
91 | new TypeImpl(RowId.class, "ROWID");
92 |
93 | /**
94 | * Timestamp that is converted to the database's timezone when stored, and
95 | * converted to the local timezone (the session timezone) when retrieved.
96 | */
97 | public static final Type TIMESTAMP_WITH_LOCAL_TIME_ZONE =
98 | new TypeImpl(LocalDateTime.class, "TIMESTAMP WITH LOCAL TIME ZONE");
99 |
100 | /**
101 | * A cursor that is returned by a procedural call.
102 | */
103 | public static final Type REF_CURSOR =
104 | new TypeImpl(Result.class, "SYS_REFCURSOR");
105 |
106 | /**
107 | * A vector of 64-bit floating point numbers, 32-bit floating point numbers,
108 | * or 8-bit signed integers. Maps to double[] by default, as a
109 | * double can store all the possible number formats without
110 | * losing information.
111 | */
112 | public static final Type VECTOR =
113 | new TypeImpl(oracle.sql.VECTOR.class, "VECTOR");
114 |
115 | /**
116 | *
117 | * Creates an {@link ArrayType} representing a user defined {@code ARRAY}
118 | * type. The {@code name} passed to this method must identify the name of a
119 | * user defined {@code ARRAY} type.
120 | *
121 | * Typically, the name passed to this method should be UPPER CASE, unless the
122 | * {@code CREATE TYPE} command that created the type used an "enquoted" type
123 | * name.
124 | *
125 | * The {@code ArrayType} object returned by this method may be used to create
126 | * a {@link Parameter} that binds an array value to a {@link Statement}:
127 | *
{@code
128 | * Publisher arrayBindExample(Connection connection) {
129 | * Statement statement =
130 | * connection.createStatement("INSERT INTO example VALUES (:array_bind)");
131 | *
132 | * // Use the name defined for an ARRAY type:
133 | * // CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
134 | * ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
135 | * Integer[] arrayValues = {1, 2, 3};
136 | * statement.bind("arrayBind", Parameters.in(arrayType, arrayValues));
137 | *
138 | * return statement.execute();
139 | * }
140 | * }
141 | * @param name Name of a user defined ARRAY type. Not null.
142 | * @return A {@code Type} object representing the user defined ARRAY type. Not
143 | * null.
144 | */
145 | public static ArrayType arrayType(String name) {
146 | return new ArrayTypeImpl(Objects.requireNonNull(name, "name is null"));
147 | }
148 |
149 | /**
150 | *
151 | * Creates an {@link ObjectType} representing a user defined {@code OBJECT}
152 | * type. The {@code name} passed to this method must identify the name of a
153 | * user defined {@code OBJECT} type.
154 | *
155 | * Typically, the name passed to this method should be UPPER CASE, unless the
156 | * {@code CREATE TYPE} command that created the type used an "enquoted" type
157 | * name.
158 | *
159 | * The {@code ObjectType} object returned by this method may be used to create
160 | * a {@link Parameter} that binds an OBJECT value to a {@link Statement}:
161 | *
178 | * @param name Name of a user defined OBJECT type. Not null.
179 | * @return A {@code Type} object representing the user defined OBJECT type.
180 | * Not null.
181 | */
182 | public static ObjectType objectType(String name) {
183 | return new ObjectTypeImpl(Objects.requireNonNull(name, "name is null"));
184 | }
185 |
186 | /**
187 | * Extension of the standard {@link Type} interface used to represent user
188 | * defined ARRAY types. An instance of {@code ArrayType} must be used when
189 | * binding an array value to a {@link Statement} created by the Oracle R2DBC
190 | * Driver.
191 | *
192 | * Oracle Database does not support an anonymous {@code ARRAY} type, which is
193 | * what the standard {@link R2dbcType#COLLECTION} type represents. Oracle
194 | * Database only supports {@code ARRAY} types which are declared as a user
195 | * defined type, as in:
196 | *
{@code
197 | * CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
198 | * }
199 | * In order to bind an array, the name of a user defined ARRAY type must
200 | * be known to Oracle R2DBC. Instances of {@code ArrayType} retain the name
201 | * that is provided to the {@link #arrayType(String)} factory method.
202 | */
203 | public interface ArrayType extends Type {
204 |
205 | /**
206 | * {@inheritDoc}
207 | * Returns {@code Object[].class}, which is the standard mapping for
208 | * {@link R2dbcType#COLLECTION}. The true default type mapping is the array
209 | * variant of the default mapping for the element type of the {@code ARRAY}.
210 | * For instance, an {@code ARRAY} of {@code VARCHAR} maps to a
211 | * {@code String[]} by default.
212 | */
213 | @Override
214 | Class> getJavaType();
215 |
216 | /**
217 | * {@inheritDoc}
218 | * Returns the name of this user defined {@code ARRAY} type. For instance,
219 | * this method returns "MY_ARRAY" if the type is declared as:
220 | *
{@code
221 | * CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
222 | * }
223 | */
224 | @Override
225 | String getName();
226 | }
227 |
228 | /**
229 | * Extension of the standard {@link Type} interface used to represent user
230 | * defined OBJECT types. An instance of {@code ObjectType} must be used when
231 | * binding an OBJECT value to a {@link Statement} created by the Oracle R2DBC
232 | * Driver.
233 | */
234 | public interface ObjectType extends Type {
235 |
236 | /**
237 | * {@inheritDoc}
238 | * Returns the class of {@link OracleR2dbcObject}, which is the default mapping
239 | * of OBJECT types returned by Oracle R2DBC.
240 | */
241 | @Override
242 | Class> getJavaType();
243 |
244 | /**
245 | * {@inheritDoc}
246 | * Returns the name of this user defined {@code OBJECT} type. For instance,
247 | * this method returns "PET" if the type is declared as:
248 | *
{@code
249 | * CREATE TYPE PET AS OBJECT(
250 | * name VARCHAR(128),
251 | * species VARCHAR(128),
252 | * weight NUMBER,
253 | * birthday DATE)
254 | * }
255 | */
256 | @Override
257 | String getName();
258 | }
259 |
260 | /** Concrete implementation of the {@code ArrayType} interface */
261 | private static final class ArrayTypeImpl
262 | extends TypeImpl implements ArrayType {
263 |
264 | /**
265 | * Constructs an ARRAY type with the given {@code name}. {@code Object[]} is
266 | * the default mapping of the constructed type. This is consistent with the
267 | * standard {@link R2dbcType#COLLECTION} type.
268 | * @param name User defined name of the type. Not null.
269 | */
270 | ArrayTypeImpl(String name) {
271 | super(Object[].class, name);
272 | }
273 | }
274 |
275 | /** Concrete implementation of the {@code ObjectType} interface */
276 | private static final class ObjectTypeImpl
277 | extends TypeImpl implements ObjectType {
278 |
279 | /**
280 | * Constructs an OBJECT type with the given {@code name}.
281 | * {@code OracleR2dbcObject} is the default mapping of the constructed type.
282 | * @param name User defined name of the type. Not null.
283 | */
284 | ObjectTypeImpl(String name) {
285 | super(OracleR2dbcObject.class, name);
286 | }
287 | }
288 |
289 | /**
290 | * Implementation of the {@link Type} SPI.
291 | */
292 | private static class TypeImpl implements Type {
293 |
294 | /**
295 | * The Java Language mapping of this SQL type.
296 | */
297 | private final Class> javaType;
298 |
299 | /**
300 | * The name of this SQL type, as it would appear in a DDL expression.
301 | */
302 | private final String sqlName;
303 |
304 | /**
305 | * Constructs a {@code Type} having a {@code javaType} mapping and
306 | * {@code sqlName}.
307 | * @param javaType Java type
308 | * @param sqlName SQL type name
309 | */
310 | TypeImpl(Class> javaType, String sqlName) {
311 | this.javaType = javaType;
312 | this.sqlName = sqlName;
313 | }
314 |
315 | /**
316 | * {@inheritDoc}
317 | *
318 | * Implements the R2DBC SPI method by returning the default Java type
319 | * mapping for values of this SQL type. The Java type returned by this
320 | * method is the type of {@code Object} returned by {@code Row.get
321 | * (String/int)} when accessing a value of this SQL type.
322 | *
332 | * Implements the R2DBC SPI method by returning the name of this SQL type.
333 | * The name returned by this method is recognized in expressions of a SQL
334 | * command, for instance: A column definition of a {@code CREATE TABLE}
335 | * command.
336 | *
337 | *
338 | * @return
339 | */
340 | @Override
341 | public String getName() {
342 | return sqlName;
343 | }
344 |
345 | /**
346 | * Returns the name of this type.
347 | * @return Type name
348 | */
349 | @Override
350 | public String toString() {
351 | return getName();
352 | }
353 |
354 | @Override
355 | public boolean equals(Object other) {
356 | if (! (other instanceof Type))
357 | return false;
358 |
359 | return sqlName.equals(((Type)other).getName());
360 | }
361 |
362 | @Override
363 | public int hashCode() {
364 | return sqlName.hashCode();
365 | }
366 | }
367 |
368 | }
369 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/OracleR2dbcWarning.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2022, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | package oracle.r2dbc;
22 |
23 | import io.r2dbc.spi.Result;
24 |
25 | import java.util.function.Function;
26 | import java.util.function.Predicate;
27 |
28 | /**
29 | *
30 | * A subtype of {@link Result.Message} that provides information on warnings
31 | * raised by Oracle Database.
32 | *
33 | * When a SQL command results in a warning, Oracle R2DBC emits a {@link Result}
34 | * with an {@code OracleR2dbcWarning} segment in addition to any other segments
35 | * that resulted from the SQL command. For example, if a SQL {@code SELECT}
36 | * command results in a warning, then an {@code OracleR2dbcWarning} segment is
37 | * included with the result, along with any {@link Result.RowSegment}s returned
38 | * by the {@code SELECT}.
39 | *
40 | * R2DBC drivers typically emit {@code onError} signals for {@code Message}
41 | * segments that are not consumed by {@link Result#filter(Predicate)} or
42 | * {@link Result#flatMap(Function)}. Oracle R2DBC does not apply this behavior
43 | * for warning messages. If an {@code OracleR2dbcWarning}
44 | * segment is not consumed by the {@code filter} or {@code flatMap} methods of
45 | * a {@code Result}, then the warning is discarded and the result may be
46 | * consumed as normal with with the {@code map} or {@code getRowsUpdated}
47 | * methods.
48 | *
49 | * Warning messages may be consumed with {@link Result#flatMap(Function)}:
50 | *
73 | * @since 1.1.0
74 | */
75 | public interface OracleR2dbcWarning extends Result.Message {
76 |
77 | }
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/AsyncLock.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2022, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | package oracle.r2dbc.impl;
22 |
23 | import oracle.r2dbc.impl.OracleR2dbcExceptions.JdbcRunnable;
24 | import oracle.r2dbc.impl.OracleR2dbcExceptions.JdbcSupplier;
25 | import org.reactivestreams.Publisher;
26 | import org.reactivestreams.Subscriber;
27 | import org.reactivestreams.Subscription;
28 |
29 | /**
30 | *
31 | * A lock that is acquired and unlocked asynchronously. An instance of this lock
32 | * is used to guard access to the Oracle JDBC Connection, without blocking
33 | * threads that contend for it.
34 | *
35 | * Since the 23.1 release, Oracle JDBC no longer blocks threads during
36 | * asynchronous calls. The remainder of this JavaDoc describes behavior which
37 | * was present in 21.x releases of Oracle JDBC. If a 23.1 or newer version
38 | * of Oracle JDBC is installed, then Oracle R2DBC will use the
39 | * {@link NoOpAsyncLock} rather than {@link AsyncLockImpl}.
40 | *
41 | * Any time Oracle R2DBC invokes a synchronous API of Oracle JDBC, it will
42 | * acquire an instance of this lock before doing so. Synchronous method calls
43 | * will block a thread if JDBC has a database call in progress, and this can
44 | * lead a starvation of the thread pool. If no pooled threads are available,
45 | * then JDBC can not execute callbacks that complete the database call, and
46 | * so JDBC will never release it's blocking lock.
47 | *
48 | * Any time Oracle R2DBC creates a publisher that is implemented by the
49 | * Oracle JDBC driver, it will acquire an instance of this lock before
50 | * subscribing to that publisher. The lock is only released if it is known
51 | * that neither onNext nor onSubscribe signals are pending. If these signals
52 | * are not pending, then the driver should not be executing any database
53 | * call; As long as no database call is in progress, JDBC should not block
54 | * threads that call any method of its API.
55 | *
56 | *
Locking for Asynchronous JDBC Calls
57 | *
58 | * Wrapping a JDBC Publisher with {@link #lock(Publisher)} will have signals
59 | * from a downstream subscriber proxied, such that the lock is held whenever
60 | * {@code onSubscribe} or {@code onNext} signals are pending.
61 | *
62 | * Invoking {@link #lock(Runnable)} will have a {@code Runnable} executed
63 | * after the lock becomes available. The {@code Runnable} is executed
64 | * immediately, before {@code lock(Runnable)} returns if the lock is
65 | * available. Otherwise, the {@code Runnable} is executed asynchronously
66 | * after the lock becomes available.
67 | *
68 | *
Locking for Synchronous JDBC Calls
69 | *
70 | * If the lock simply needs to be acquired before making a synchronous call,
71 | * and then released after that call returns, then methods
72 | * {@link #run(JdbcRunnable)}, {@link #get(JdbcSupplier)}, or
73 | * {@link #flatMap(JdbcSupplier)} may be used. These methods return a
74 | * {@code Publisher} that completes after the lock is acquired and the provided
75 | * task has been run. These methods will automatically release the lock after
76 | * the provided task has run.
77 | *
78 | * Rather than invoke the {@code get/run} methods for each and every JDBC
79 | * method call, it is preferable to invoke them with a single task that
80 | * performs many synchronous JDBC API calls. This will reduce the computational
81 | * and memory costs of creating and subscribing to publishers. It will also
82 | * allow most of the code base to be written in a synchronous style, with the
83 | * assumption that it is executing within a task provided to the {@code get/run}
84 | * methods.
85 | *
86 | */
87 | interface AsyncLock {
88 |
89 | /**
90 | * Acquires this lock, executes a {@code callback}, and then releases this
91 | * lock. The {@code callback} may be executed before this method returns if
92 | * this lock is currently available. Otherwise, the {@code callback} is
93 | * executed asynchronously after this lock becomes available.
94 | *
95 | * @param callback Task to be executed with lock ownership. Not null.
96 | */
97 | void lock(Runnable callback);
98 |
99 | /**
100 | * Returns a {@code Publisher} that acquires this lock and executes a
101 | * {@code jdbcRunnable} when a subscriber subscribes. The {@code Publisher}
102 | * emits {@code onComplete} if the runnable completes normally, or emits
103 | * {@code onError} if the runnable throws an exception.
104 | *
105 | * @param jdbcRunnable Runnable to execute. Not null.
106 | * @return A publisher that emits the result of the {@code jdbcRunnable}.
107 | */
108 | Publisher run(JdbcRunnable jdbcRunnable);
109 |
110 | /**
111 | * Returns a {@code Publisher} that acquires this lock and executes a
112 | * {@code jdbcSupplier} when a subscriber subscribes. The {@code Publisher}
113 | * emits {@code onNext} if the supplier returns a non-null value, and then
114 | * emits {@code onComplete}. Or, the {@code Publisher} emits {@code onError}
115 | * with any {@code Throwable} thrown by the supplier.
116 | *
117 | * @param The class of object output by the {@code jdbcSupplier}
118 | * @param jdbcSupplier Supplier to execute. Not null.
119 | * @return A publisher that emits the result of the {@code jdbcSupplier}.
120 | */
121 | Publisher get(JdbcSupplier jdbcSupplier);
122 |
123 | /**
124 | * Returns a {@code Publisher} that acquires this lock and executes a
125 | * {@code publisherSupplier} when a subscriber subscribes. The
126 | * {@code Publisher} output by the {@code publisherSupplier} is flat mapped
127 | * into the {@code Publisher} returned by this method. If the supplier outputs
128 | * {@code null}, the returned publisher just emits {@code onComplete}. If the
129 | * supplier throws an error, the returned publisher emits that as
130 | * {@code onError}.
131 | *
132 | * @param The class of object emitted by the supplied publisher
133 | * @param publisherSupplier Supplier to execute. Not null.
134 | * @return A flat-mapping of the publisher output by the
135 | * {@code publisherSupplier}.
136 | */
137 | Publisher flatMap(JdbcSupplier> publisherSupplier);
138 |
139 | /**
140 | * Returns a {@code Publisher} that proxies signals to and from a
141 | * provided {@code publisher} in order to guard access to the JDBC
142 | * {@code Connection} associated with this adapter. Invocations of
143 | * {@link Publisher#subscribe(Subscriber)} and
144 | * {@link Subscription#request(long)} will only occur when the JDBC connection
145 | * is not being used by another thread or another publisher.
146 | *
147 | * @param The class of object emitted by the {@code publisher}
148 | * @param publisher A publisher that uses the JDBC connection
149 | * @return A Publisher that
150 | */
151 | Publisher lock(Publisher publisher);
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/DependentCounter.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2022, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 | package oracle.r2dbc.impl;
22 |
23 | import org.reactivestreams.Publisher;
24 | import reactor.core.publisher.Mono;
25 |
26 | import java.util.concurrent.atomic.AtomicInteger;
27 |
28 | /**
29 | *
30 | * A count of resources that depend on another resource to remain open. A
31 | * dependent resource registers itself by incrementing the count, and
32 | * deregisters itself by decrementing the count. The last dependent to
33 | * deregister has the responsibility of subscribing to a {@code Publisher} that
34 | * closes the resource it depended upon.
35 | *
36 | * This class is conceptually similar to a {@code java.util.concurrent.Phaser}.
37 | * Parties register by calling {@link #increment()}, and deregister by calling
38 | * {@link #decrement()}. Asynchronous "phase advancement" is then handled by
39 | * the {@code Publisher} which {@code decrement} returns.
40 | *
41 | * This class offers a solution for tracking the consumption of
42 | * {@link io.r2dbc.spi.Result} objects that depend on a JDBC statement to remain
43 | * open until each result is consumed. Further explanations can be found in the
44 | * JavaDocs of {@link OracleStatementImpl} and {@link OracleResultImpl}.
45 | *
46 | */
47 | class DependentCounter {
48 |
49 | /** Count of dependents */
50 | private final AtomicInteger count = new AtomicInteger(0);
51 |
52 | /** Publisher that closes the depended upon resource */
53 | private final Publisher closePublisher;
54 |
55 | /**
56 | * Constructs a new counter that returns a resource closing publisher to the
57 | * last dependent which unregisters. The counter is initialized with a count
58 | * of zero.
59 | * @param closePublisher Publisher that closes a resource. Not null.
60 | */
61 | DependentCounter(Publisher closePublisher) {
62 | this.closePublisher = closePublisher;
63 | }
64 |
65 | /**
66 | * Increments the count of dependents by one.
67 | *
68 | * A corresponding call to {@link #decrement()} MUST occur by the dependent
69 | * which has called {@code increment()}
70 | *
71 | */
72 | void increment() {
73 | count.incrementAndGet();
74 | }
75 |
76 | /**
77 | *
78 | * Returns a publisher that decrements the count of dependents by one when
79 | * subscribed to.
80 | *
81 | * A corresponding call to {@link #increment()} MUST have previously occurred
82 | * by the dependent which has called {@code decrement()}
83 | *
84 | *
85 | * The dependent which has called this method MUST subscribe to the returned
86 | * published. If the dependent that calls this method is the last dependent to
87 | * do so, then the returned publisher will close the depended upon resource.
88 | * Otherwise, if more dependents remain, the returned publisher does nothing.
89 | * The caller of this method has no way to tell which is the case, so it must
90 | * subscribe to be safe.
91 | *
92 | * @return A publisher that closes the depended upon resource after no
93 | * dependents remain. Not null.
94 | */
95 | Publisher decrement() {
96 | return Mono.defer(() ->
97 | count.decrementAndGet() == 0
98 | ? Mono.from(closePublisher)
99 | : Mono.empty());
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/Main.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import java.io.IOException;
25 | import java.io.InputStream;
26 | import java.util.Objects;
27 | import java.util.jar.Manifest;
28 |
29 | /**
30 | *
31 | * A public class implementing a main method that may be executed from a
32 | * command line. This class is specified as the Main-Class attribute in
33 | * the META-INF/MANIFEST.MF of the Oracle R2DBC jar.
34 | *
35 | * The behavior implemented by this class may change between minor and patch
36 | * version release updates.
37 | *
38 | * The following command, in
39 | * which "x.y.z" is the semantic version number of the jar,
40 | * executes the main method of this class:
41 | *
42 | * java -jar oracle-r2dbc-x.y.z.jar
43 | *
44 | * Since version 0.1.1, the main method is implemented to exit after printing
45 | * a message to the standard output stream. The message includes the version
46 | * numbers of the Oracle R2DBC Driver along with the JDK that compiled it. A
47 | * timestamp captured at the moment when the jar was compiled is also included.
48 | *
49 | *
50 | * @since 0.1.1
51 | * @author Michael-A-McMahon
52 | */
53 | public final class Main {
54 |
55 | private Main() {/*This class has no instance fields*/}
56 |
57 | /**
58 | * Prints information about this build of Oracle R2DBC. This method attempts
59 | * to read a "Build-Info" attribute from META-INF/MANIFEST.MF. If the
60 | * manifest is not available to the class loader, or if the manifest does
61 | * not contain a Build-Info attribute, then this method prints a message
62 | * indicating that build information can not be located.
63 | * @param args ignored
64 | * @throws IOException If the META-INF/MANIFEST.MF resource can not be read.
65 | */
66 | public static void main(String[] args) throws IOException {
67 |
68 | InputStream manifestStream =
69 | Main.class.getModule().getResourceAsStream("META-INF/MANIFEST.MF");
70 |
71 | if (manifestStream == null) {
72 | System.out.println("META-INF/MANIFEST.MF not found");
73 | return;
74 | }
75 |
76 | try (manifestStream) {
77 | System.out.println(Objects.requireNonNullElse(
78 | new Manifest(manifestStream)
79 | .getMainAttributes()
80 | .getValue("Build-Info"),
81 | "Build-Info is missing from the manifest"));
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/NoOpAsyncLock.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import org.reactivestreams.Publisher;
25 | import reactor.core.publisher.Flux;
26 | import reactor.core.publisher.Mono;
27 |
28 | /**
29 | * A no-op implementation of {@link AsyncLock} for use with 23.1 and newer
30 | * versions of Oracle JDBC. All methods are implemented by immediately executing
31 | * operations without acquiring a lock.
32 | */
33 | final class NoOpAsyncLock implements AsyncLock {
34 |
35 | @Override
36 | public void lock(Runnable callback) {
37 | callback.run();
38 | }
39 |
40 | @Override
41 | public Publisher run(OracleR2dbcExceptions.JdbcRunnable jdbcRunnable) {
42 | return Mono.fromRunnable(jdbcRunnable);
43 | }
44 |
45 | @Override
46 | public Publisher get(
47 | OracleR2dbcExceptions.JdbcSupplier jdbcSupplier) {
48 | return Mono.fromSupplier(jdbcSupplier);
49 | }
50 |
51 | @Override
52 | public Publisher flatMap(
53 | OracleR2dbcExceptions.JdbcSupplier> publisherSupplier) {
54 | return Flux.defer(publisherSupplier);
55 | }
56 |
57 | @Override
58 | public Publisher lock(Publisher publisher) {
59 | return publisher;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/OracleBatchImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import io.r2dbc.spi.Batch;
25 | import io.r2dbc.spi.R2dbcException;
26 | import org.reactivestreams.Publisher;
27 | import reactor.core.publisher.Flux;
28 |
29 | import java.sql.Connection;
30 | import java.time.Duration;
31 | import java.util.LinkedList;
32 | import java.util.Queue;
33 |
34 | import static oracle.r2dbc.impl.OracleR2dbcExceptions.requireNonNull;
35 | import static oracle.r2dbc.impl.OracleR2dbcExceptions.requireOpenConnection;
36 |
37 | /**
38 | *
39 | * Implementation of the {@link Batch} SPI for Oracle Database. This SPI
40 | * implementation executes an ordered sequence of arbitrary SQL statements
41 | * using a JDBC connection. JDBC API calls are adapted into Reactive Streams
42 | * APIs using a {@link ReactiveJdbcAdapter}.
43 | *
44 | * Oracle Database supports batch execution of parameterized DML statements,
45 | * but does not support batch execution of arbitrary SQL statements. This
46 | * implementation reflects the capabilities of Oracle Database; It does not
47 | * offer any performance benefit compared to individually executing each
48 | * statement in the batch.
49 | *
50 | *
51 | * @author harayuanwang, michael-a-mcmahon
52 | * @since 0.1.0
53 | */
54 | final class OracleBatchImpl implements Batch {
55 |
56 | /** The OracleConnectionImpl that created this Batch */
57 | private final OracleConnectionImpl r2dbcConnection;
58 |
59 | /**
60 | * JDBC connection to an Oracle Database which executes this batch.
61 | */
62 | private final Connection jdbcConnection;
63 |
64 | /**
65 | * Timeout applied to each statement this {@code Batch} executes;
66 | */
67 | private final Duration timeout;
68 |
69 | /**
70 | * Ordered sequence of SQL commands that have been added to this batch. May
71 | * be empty.
72 | */
73 | private Queue statements = new LinkedList<>();
74 |
75 | /**
76 | * Constructs a new batch that uses the specified {@code adapter} to execute
77 | * SQL statements with a {@code jdbcConnection}.
78 | * @param timeout Timeout applied to each statement this batch executes.
79 | * Not null. Not negative.
80 | * @param r2dbcConnection R2DBC connection that created this batch. Not null.
81 | */
82 | OracleBatchImpl(Duration timeout, OracleConnectionImpl r2dbcConnection) {
83 | this.timeout = timeout;
84 | this.r2dbcConnection = r2dbcConnection;
85 | this.jdbcConnection = r2dbcConnection.jdbcConnection();
86 | }
87 |
88 | /**
89 | * {@inheritDoc}
90 | *
91 | * Implements the R2DBC SPI method by adding a {@code sql} command to the
92 | * end of the command sequence of the current batch.
93 | *
107 | * Implements the R2DBC SPI method by executing the SQL statements that have
108 | * been added to the current batch since the previous execution. Statements
109 | * are executed in the order they were added. Calling this method clears all
110 | * statements that have been added to the current batch.
111 | *
112 | * If the execution of any statement in the sequence results in a failure,
113 | * then the returned publisher emits {@code onError} with an
114 | * {@link R2dbcException} that describes the failure, and all subsequent
115 | * statements in the sequence are not executed.
116 | *
117 | * The returned publisher begins executing the batch after a
118 | * subscriber subscribes, before the subscriber emits a {@code
119 | * request} signal. The returned publisher does not support multiple
120 | * subscribers. After one subscriber has subscribed, the returned publisher
121 | * signals {@code onError} with {@code IllegalStateException} to any
122 | * subsequent subscribers.
123 | *
124 | */
125 | @Override
126 | public Publisher execute() {
127 | requireOpenConnection(jdbcConnection);
128 | Queue currentStatements = statements;
129 | statements = new LinkedList<>();
130 | return Flux.fromIterable(currentStatements)
131 | .flatMapSequential(OracleStatementImpl::execute);
132 | }
133 |
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/OracleConnectionFactoryImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import io.r2dbc.spi.Connection;
25 | import io.r2dbc.spi.ConnectionFactory;
26 | import io.r2dbc.spi.ConnectionFactoryMetadata;
27 | import io.r2dbc.spi.ConnectionFactoryOptions;
28 | import io.r2dbc.spi.IsolationLevel;
29 | import io.r2dbc.spi.R2dbcException;
30 | import io.r2dbc.spi.Statement;
31 | import oracle.r2dbc.OracleR2dbcOptions;
32 | import org.reactivestreams.Publisher;
33 | import reactor.core.publisher.Mono;
34 |
35 | import javax.sql.DataSource;
36 | import java.time.Duration;
37 | import java.util.Optional;
38 | import java.util.concurrent.Executor;
39 | import java.util.concurrent.ForkJoinPool;
40 |
41 | /**
42 | *
43 | * Implementation of the {@link ConnectionFactory} SPI for Oracle Database.
44 | *
45 | * Instances of this class open database connections using a JDBC
46 | * {@link javax.sql.DataSource}. JDBC API calls are adapted into Reactive
47 | * Streams APIs using a {@link ReactiveJdbcAdapter}.
48 | *
49 | * {@code Connections} created by this factory are initially
50 | * configured with {@linkplain Connection#isAutoCommit() auto-commit} mode
51 | * enabled and have a {@linkplain Connection#getTransactionIsolationLevel()
52 | * transaction isolation level} of {@linkplain IsolationLevel#READ_COMMITTED
53 | * READ_COMMITTED}.
54 | *
55 | *
Required Options
56 | * This implementation requires the following options for connection creation:
57 | *
58 | *
{@link ConnectionFactoryOptions#DRIVER}
59 | *
Must have the value "oracle"
60 | *
{@link ConnectionFactoryOptions#HOST}
61 | *
IP address or hostname of an Oracle Database
62 | *
63 | *
Supported Options
64 | * This implementation supports the following options for connection creation:
65 | *
66 | *
{@link ConnectionFactoryOptions#PORT}
67 | *
Port number of an Oracle Database
68 | *
{@link ConnectionFactoryOptions#DATABASE}
69 | *
Service name (not an SID) of an Oracle Database
70 | *
{@link ConnectionFactoryOptions#USER}
71 | *
Name of an Oracle Database user
72 | *
{@link ConnectionFactoryOptions#PASSWORD}
73 | *
74 | * Password of an Oracle Database user. The value may be an instance
75 | * of a mutable {@link CharSequence}, such {@link java.nio.CharBuffer},
76 | * that may be cleared after creating an instance of
77 | * {@code OracleConnectionFactoryImpl}.
78 | *
79 | *
{@link ConnectionFactoryOptions#CONNECT_TIMEOUT}
80 | *
81 | * Maximum wait time when requesting a {@code Connection}. If the
82 | * duration expires, a {@code Connection} {@code Subscriber} receives
83 | * {@code onError} with an {@link io.r2dbc.spi.R2dbcTimeoutException}.
84 | * The duration is rounded up to the nearest whole second. The query
85 | * section of an R2DBC URL may provide a value in the format specified by
86 | * {@link Duration#parse(CharSequence)}.
87 | *
88 | *
{@link ConnectionFactoryOptions#SSL}
89 | *
90 | * If set to {@code true}, the driver connects to Oracle Database using
91 | * TCPS (ie: SSL/TLS).
92 | *
93 | *
94 | *
Supported Options
95 | * This implementation supports extended options having the name of a
96 | * subset of Oracle JDBC connection properties. The list of supported
97 | * connection properties is specified by {@link OracleReactiveJdbcAdapter}.
98 | *
107 | * The default executor when {@link OracleR2dbcOptions#EXECUTOR} is not
108 | * configured. It will use the common {@code ForkJoinPool}, unless it has
109 | * a maximum pool size of 0. See:
110 | * https://github.com/oracle/oracle-r2dbc/issues/129
111 | *
112 | */
113 | private static final Executor DEFAULT_EXECUTOR =
114 | "0".equals(System.getProperty(
115 | "java.util.concurrent.ForkJoinPool.common.parallelism"))
116 | ? new ForkJoinPool(1)
117 | : ForkJoinPool.commonPool();
118 |
119 | /** JDBC data source that this factory uses to open connections */
120 | private final DataSource dataSource;
121 |
122 | /**
123 | * Executor configured by {@link oracle.r2dbc.OracleR2dbcOptions#EXECUTOR},
124 | * or a default one if none was configured.
125 | */
126 | private final Executor executor;
127 |
128 | /**
129 | *
130 | * Timeout applied to the execution of {@link Statement}s created by
131 | * {@link Connection}s created by this {@code ConnectionFactory}.
132 | *
133 | * The {@link #dataSource} is not configured with this value because Oracle
134 | * JDBC does not have a connection property to set a statement execution
135 | * timeout. This value is retained by an instance of
136 | * {@code OracleConnectionFactoryImpl} so that it may be applied to each
137 | * {@code Connection} it creates.
138 | *
144 | * Constructs a new factory that applies the values specified by the {@code
145 | * options} parameter when opening a database connection. This constructor
146 | * fails if any required options
147 | * are not specified by the {@code options} parameter.
148 | *
149 | * Where curly brackets {enclose} the name of a required {@code Option},
150 | * and angle brackets [enclose] the name of an optional {@code Option}, the
151 | * values specified by the {@code options} parameter are used to compose a
152 | * JDBC URL for database connectivity as:
153 | *
156 | * Note that the syntax used is {@code /{DATABASE}} and not
157 | * {@code :{DATABASE}}. This forward slash syntax has the {@code DATABASE}
158 | * option interpreted as a service name. The {@code DATABASE} option is not
159 | * interpreted as a system ID (SID) using the colon syntax.
160 | *
161 | * Traditional database authentication is configured by option values for
162 | * {@link ConnectionFactoryOptions#USER} with
163 | * {@link ConnectionFactoryOptions#PASSWORD}. These options are not
164 | * required because Oracle Database supports alternative methods of
165 | * authentication that do not require a user name and password, such as
166 | * trusted TLS certificates.
167 | *
168 | * Well-known options {@link ConnectionFactoryOptions#CONNECT_TIMEOUT} and
169 | * {@link ConnectionFactoryOptions#SSL} are supported.
170 | *
171 | * Any extended options are applied as Oracle JDBC connection properties.
172 | * An extended option is any option that is not declared by
173 | * {@link ConnectionFactoryOptions}. See
174 | * {@link OracleReactiveJdbcAdapter#createDataSource(ConnectionFactoryOptions)}
175 | * for a list of Oracle JDBC connection properties that are supported.
176 | *
177 | *
178 | * @param options Options applied when opening a connection to a database.
179 | *
180 | * @throws IllegalArgumentException If the value of a required option is
181 | * null.
182 | *
183 | * @throws IllegalStateException If the value of a required option is not
184 | * specified.
185 | *
186 | * @throws IllegalArgumentException If the {@code oracleNetDescriptor}
187 | * {@code Option} is provided with any other options that might have
188 | * conflicting values, such as {@link ConnectionFactoryOptions#HOST}.
189 | *
190 | * @throws IllegalArgumentException If the
191 | * {@link ConnectionFactoryOptions#STATEMENT_TIMEOUT} {@code Option} specifies
192 | * a negative {@code Duration}
193 | */
194 | OracleConnectionFactoryImpl(ConnectionFactoryOptions options) {
195 | OracleR2dbcExceptions.requireNonNull(options, "options is null.");
196 | dataSource = ReactiveJdbcAdapter.getOracleAdapter()
197 | .createDataSource(options);
198 |
199 | // Handle any Options that Oracle JDBC doesn't
200 | if (options.hasOption(ConnectionFactoryOptions.LOCK_WAIT_TIMEOUT)) {
201 | throw new UnsupportedOperationException(
202 | "Unsupported Option: "
203 | + ConnectionFactoryOptions.LOCK_WAIT_TIMEOUT.name()
204 | + ". Oracle Database does not support a lock wait timeout session " +
205 | "parameter.");
206 | }
207 |
208 | statementTimeout = Optional.ofNullable(
209 | options.getValue(ConnectionFactoryOptions.STATEMENT_TIMEOUT))
210 | .map(timeout -> (timeout instanceof Duration)
211 | ? (Duration)timeout
212 | : Duration.parse(timeout.toString()))
213 | .orElse(Duration.ZERO);
214 |
215 | Object executor = options.getValue(OracleR2dbcOptions.EXECUTOR);
216 | if (executor == null) {
217 | this.executor = DEFAULT_EXECUTOR;
218 | }
219 | else if (executor instanceof Executor) {
220 | this.executor = (Executor) executor;
221 | }
222 | else {
223 | throw new IllegalArgumentException(
224 | "Value of " + OracleR2dbcOptions.EXECUTOR
225 | + " is not an instance of Executor: " + executor.getClass());
226 | }
227 |
228 | }
229 |
230 | /**
231 | * {@inheritDoc}
232 | *
233 | * Implements the R2DBC SPI method by opening a database connection using
234 | * the JDBC {@link DataSource} that this factory initialized when it was
235 | * constructed.
236 | *
237 | * The returned publisher does not attempt to open a JDBC connection until
238 | * a subscriber has signalled demand, and either emits a single connection or
239 | * emits {@code onError} with an {@link R2dbcException}. The returned
240 | * publisher releases any resources allocated to a JDBC connection if a
241 | * subscriber cancels it's subscription before the returned
242 | * publisher has emitted a connection. Subscribers MUST eventually
243 | * {@linkplain Connection#close() close} any connection that is emitted by
244 | * the returned publisher, so that the database can reclaim the resources
245 | * allocated for that connection.
246 | *
247 | * The returned publisher supports multiple subscribers. One {@code
248 | * Connection} is emitted to each subscriber that subscribes and signals
249 | * demand.
250 | *
274 | * Implements the R2DBC SPI method by returning an implementation of the
275 | * {@code ConnectionFactoryMetaData} SPI that names "Oracle Database" as the
276 | * database product that this factory's JDBC datasource can open
277 | * connections to.
278 | *
279 | */
280 | @Override
281 | public ConnectionFactoryMetadata getMetadata() {
282 | return OracleConnectionFactoryMetadataImpl.INSTANCE;
283 | }
284 |
285 | }
286 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/OracleConnectionFactoryMetadataImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import io.r2dbc.spi.ConnectionFactoryMetadata;
25 |
26 | /**
27 | * Implementation of {@code ConnectionFactoryMetaData} which names
28 | * "Oracle Database" as the database product that a
29 | * {@link io.r2dbc.spi.ConnectionFactory} connects to.
30 | */
31 | final class OracleConnectionFactoryMetadataImpl
32 | implements ConnectionFactoryMetadata {
33 |
34 | static final OracleConnectionFactoryMetadataImpl INSTANCE =
35 | new OracleConnectionFactoryMetadataImpl();
36 |
37 | private OracleConnectionFactoryMetadataImpl() {}
38 |
39 | @Override
40 | public String getName() {
41 | return "Oracle Database";
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/OracleConnectionFactoryProviderImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import io.r2dbc.spi.ConnectionFactory;
25 | import io.r2dbc.spi.ConnectionFactoryOptions;
26 | import io.r2dbc.spi.ConnectionFactoryProvider;
27 |
28 | import java.util.ServiceLoader;
29 |
30 | import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER;
31 | import static oracle.r2dbc.impl.OracleR2dbcExceptions.requireNonNull;
32 |
33 | /**
34 | *
35 | * Implementation of the {@link ConnectionFactoryProvider} SPI for the Oracle
36 | * Database. This provider
37 | * {@linkplain #supports(ConnectionFactoryOptions) supports}
38 | * {@link ConnectionFactoryOptions} that specify {@code DRIVER} as the value
39 | * {@code "oracle"}.
40 | *
41 | * The Oracle R2DBC Driver JAR identifies this class as a service provider for
42 | * the R2DBC SPI. Identification is specified with a
43 | * provider-configuration-file included under {@code META-INF/services}. A
44 | * {@link ServiceLoader} uses the provider-configuration-file to locate this
45 | * class.
46 | *
47 | *
48 | * @author harayuanwang, michael-a-mcmahon
49 | * @since 0.1.0
50 | */
51 | public final class OracleConnectionFactoryProviderImpl
52 | implements ConnectionFactoryProvider {
53 |
54 | /**
55 | * Identifier of the Oracle R2DBC Driver that can appear in a R2DBC URL or
56 | * connection factory options.
57 | */
58 | private static final String DRIVER_IDENTIFIER = "oracle";
59 |
60 | /**
61 | *
62 | * Constructs a new connection factory provider.
63 | *
70 | *
71 | * This constructor is not supported for general application programming.
72 | *
73 | * Application programmers should use the
74 | * {@link io.r2dbc.spi.ConnectionFactories} APIs to obtain an instance of
75 | * the Oracle R2DBC {@link ConnectionFactory}.
76 | *
83 | * Implements the R2DBC SPI method by returning a new
84 | * {@code ConnectionFactory} that applies values specified by the {@code
85 | * options} parameter, when opening connections to an Oracle Database.
86 | *
87 | *
88 | * @throws IllegalStateException If any option required by
89 | * {@link OracleConnectionFactoryImpl} is not specified by {@code options}.
90 | *
91 | * @throws IllegalArgumentException If the {@code oracleNetDescriptor}
92 | * {@code Option} is provided with any other options that might have
93 | * conflicting values, such as {@link ConnectionFactoryOptions#HOST}.
94 | */
95 | @Override
96 | public ConnectionFactory create(ConnectionFactoryOptions options) {
97 | assert supports(options) : "Options are not supported: " + options;
98 | requireNonNull(options, "options must not be null.");
99 |
100 | if (SuppliedOptionConnectionFactory.containsSuppliedValue(options))
101 | return new SuppliedOptionConnectionFactory(options);
102 | else
103 | return new OracleConnectionFactoryImpl(options);
104 | }
105 |
106 | /**
107 | * {@inheritDoc}
108 | *
109 | * Implements the R2DBC SPI method by returning {@code true} if the {@code
110 | * options} parameter specifies the value of {@code DRIVER} as "oracle".
111 | *
112 | */
113 | @Override
114 | public boolean supports(ConnectionFactoryOptions options) {
115 | requireNonNull(options, "options must not be null.");
116 | return DRIVER_IDENTIFIER.equals(options.getValue(DRIVER));
117 | }
118 |
119 | /**
120 | * {@inheritDoc}
121 | *
122 | * Implements the R2DBC SPI method by returning the identifier of the Oracle
123 | * R2DBC Driver, which is "oracle".
124 | *
125 | */
126 | @Override
127 | public String getDriver() {
128 | return DRIVER_IDENTIFIER;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/OracleConnectionMetadataImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import java.sql.DatabaseMetaData;
25 | import io.r2dbc.spi.ConnectionMetadata;
26 |
27 | import static oracle.r2dbc.impl.OracleR2dbcExceptions.fromJdbc;
28 |
29 |
30 | /**
31 | *
32 | * Implementation of the {@link ConnectionMetadata} SPI for Oracle Database.
33 | *
34 | * Instances of this class supply metadata values of a JDBC
35 | * {@link DatabaseMetaData} object.
36 | *
37 | *
38 | * @author harayuanwang
39 | * @since 0.1.0
40 | */
41 | final class OracleConnectionMetadataImpl implements ConnectionMetadata {
42 |
43 | /** Metadata from a JDBC connection */
44 | private final DatabaseMetaData dbMetaData;
45 |
46 | /**
47 | * Constructs a new instance that supplies metadata from the specified
48 | * {@code dbMetaData}.
49 | * @param dbMetaData Metadata to supply.
50 | */
51 | OracleConnectionMetadataImpl(DatabaseMetaData dbMetaData) {
52 | this.dbMetaData = dbMetaData;
53 | }
54 |
55 | /**
56 | * {@inheritDoc}
57 | *
58 | * Implements the R2DBC SPI method by returning the database product name
59 | * supplied by the JDBC {@code DatabaseMetaData}.
60 | *
70 | * Implements the R2DBC SPI method by returning the database product version
71 | * supplied by the JDBC {@code DatabaseMetaData}.
72 | *
73 | */
74 | @Override
75 | public String getDatabaseVersion() {
76 | return fromJdbc(dbMetaData::getDatabaseProductVersion);
77 | }
78 | }
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/OracleLargeObjects.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2021, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import io.r2dbc.spi.Blob;
25 | import io.r2dbc.spi.Clob;
26 | import org.reactivestreams.Publisher;
27 | import reactor.core.publisher.Flux;
28 | import reactor.core.publisher.Mono;
29 |
30 | import java.nio.ByteBuffer;
31 | import java.util.Objects;
32 | import java.util.concurrent.atomic.AtomicBoolean;
33 |
34 | /**
35 | * Factory for implementations of the R2DBC {@link Blob} and {@link Clob}
36 | * SPIs. The Oracle R2DBC Driver uses this factory to produce column mappings
37 | * for the BLOB and CLOB database types.
38 | * @since 0.1.0
39 | * @author michael-a-mcmahon
40 | */
41 | final class OracleLargeObjects {
42 |
43 | /**
44 | * This class has no instance methods, so this constructor should never be
45 | * called.
46 | */
47 | private OracleLargeObjects(){ }
48 |
49 | /**
50 | * Creates a new {@code Blob} that streams binary data from a
51 | * {@code contentPublisher} and subscribes to a {@code releasePublisher},
52 | * when the {@code Blob} is discarded. The {@code contentPublisher} is
53 | * subscribed to one or zero times. The {@code releasePublisher} must
54 | * support multiple subscribers, and must publish the same result to each
55 | * subscriber.
56 | * @param contentPublisher Publishes the contents of a BLOB
57 | * @param releasePublisher Publishes the result of releasing a BLOB.
58 | * @return A new {@code Blob}
59 | */
60 | static Blob createBlob(
61 | Publisher contentPublisher, Publisher releasePublisher) {
62 |
63 | Publisher streamPublisher =
64 | createStreamPublisher(contentPublisher, releasePublisher);
65 |
66 | return new Blob() {
67 | @Override
68 | public Publisher stream() {
69 | return streamPublisher;
70 | }
71 |
72 | @Override
73 | public Publisher discard() {
74 | return releasePublisher;
75 | }
76 | };
77 | }
78 |
79 | /**
80 | * Creates a new {@code Clob} that streams character data from a
81 | * {@code contentPublisher} and subscribes to a {@code releasePublisher},
82 | * when the {@code Clob} is discarded. The {@code contentPublisher} is
83 | * subscribed to one or zero times. The {@code releasePublisher} must
84 | * support multiple subscribers, and must publish the same result to each
85 | * subscriber.
86 | * @param contentPublisher Publishes the contents of a CLOB
87 | * @param releasePublisher Publishes the result of releasing a CLOB.
88 | * @return A new {@code Clob}
89 | */
90 | static Clob createClob(
91 | Publisher extends CharSequence> contentPublisher,
92 | Publisher releasePublisher) {
93 |
94 | Publisher streamPublisher =
95 | createStreamPublisher(contentPublisher, releasePublisher);
96 |
97 | return new Clob() {
98 | @Override
99 | public Publisher stream() {
100 | return streamPublisher;
101 | }
102 |
103 | @Override
104 | public Publisher discard() {
105 | return releasePublisher;
106 | }
107 | };
108 | }
109 |
110 | /**
111 | *
112 | * Returns a publisher of LOB content that implements the behavior specified
113 | * for {@link Blob#stream()} and {@link Clob#stream()} R2DBC SPIs.
114 | *
115 | * This publisher will publish LOB content to the first subscriber that
116 | * subscribes, and will throw an {@link IllegalStateException} if
117 | * subscribed to more than once.
118 | *
119 | * If a subscription to this publisher is cancelled, this publisher will
120 | * initiate the release of LOB resources.
121 | *
122 | * If LOB content publishing terminates with an {@code onComplete} or
123 | * {@code onError}, this publisher will initiate the release of LOB resources.
124 | * This behavior is specified in the R2DBC 0.8.1 Specification,
125 | * Section 12.2.5:
126 | *
127 | * Applications may release Blob and Clob by either consuming the
128 | * content stream or disposing of resources by calling the discard()
129 | * method.
130 | *
131 | *
132 | * When, upon it's termination, the returned publisher subscribes to the
133 | * {@code releasePublisher}, the result emitted by the release publisher is
134 | * not emitted to the subscriber of the returned publisher. To process signals
135 | * that result from releasing the LOB, the {@code releasePublisher} must
136 | * emit the same result to subscribers of {@link Blob#discard()} or
137 | * {@link Clob#discard()}.
138 | *
139 | * @param contentPublisher Publishes a LOB's contents
140 | * @param releasePublisher Publishes the result of releasing a LOB.
141 | * @param The type of published content
142 | * @return A LOB content publisher that releases resources upon it's
143 | * termination.
144 | */
145 | private static Publisher createStreamPublisher(
146 | Publisher extends T> contentPublisher, Publisher releasePublisher) {
147 |
148 | AtomicBoolean isSubscribed = new AtomicBoolean(false);
149 |
150 | return subscriber -> {
151 | Objects.requireNonNull(subscriber, "Subscriber is null");
152 |
153 | if (isSubscribed.compareAndSet(false, true)) {
154 | Flux.from(contentPublisher)
155 | // Call to free the LOB should happen *after* the LOB content
156 | // publisher terminates, so that calls to Blob/Clob.freeAsyncOracle
157 | // do not block.
158 | .doFinally(signalType -> Mono.from(releasePublisher).subscribe())
159 | .subscribe(subscriber);
160 | }
161 | else {
162 | throw new IllegalStateException(
163 | "A content stream can not be consumed more than once");
164 | }
165 | };
166 | }
167 | }
--------------------------------------------------------------------------------
/src/main/java/oracle/r2dbc/impl/Publishers.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2020, 2022, Oracle and/or its affiliates.
3 |
4 | This software is dual-licensed to you under the Universal Permissive License
5 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
6 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7 | either license.
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | https://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | */
21 |
22 | package oracle.r2dbc.impl;
23 |
24 | import org.reactivestreams.Publisher;
25 | import reactor.core.publisher.Flux;
26 | import reactor.core.publisher.Mono;
27 |
28 | /**
29 | * Factory methods that create a {@code Publisher}. These methods cover special
30 | * cases which are not already supported by Project Reactor.
31 | */
32 | class Publishers {
33 |
34 | private Publishers() {}
35 |
36 | /**
37 | * A publisher that immediately emits onNext and onComplete to subscribers
38 | */
39 | private static final Publisher