├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ ├── maven-wrapper.properties
│ └── MavenWrapperDownloader.java
├── src
├── test
│ ├── java
│ │ └── io
│ │ │ └── r2dbc
│ │ │ └── adba
│ │ │ ├── mock
│ │ │ ├── package-info.java
│ │ │ ├── SqlAware.java
│ │ │ ├── MockTransaction.java
│ │ │ ├── SqlAwareMockOperation.java
│ │ │ ├── MockSubmission.java
│ │ │ ├── ParameterBinding.java
│ │ │ ├── ResultBuilder.java
│ │ │ ├── MockOperation.java
│ │ │ ├── Parameters.java
│ │ │ ├── MockColumn.java
│ │ │ ├── MockParameterizedRowCountOperation.java
│ │ │ ├── MockRowColumn.java
│ │ │ ├── MockParameterizedRowOperation.java
│ │ │ ├── MockDataSource.java
│ │ │ └── MockSession.java
│ │ │ ├── AdbaConnectionFactoryUnitTests.java
│ │ │ ├── MockSessionFactoryTests.java
│ │ │ ├── BindingUnitTests.java
│ │ │ ├── MockOperationTests.java
│ │ │ ├── AdbaUtilsUnitTests.java
│ │ │ └── AdbaConnectionUnitTests.java
│ └── resources
│ │ └── logback-test.xml
└── main
│ └── java
│ └── io
│ └── r2dbc
│ └── adba
│ ├── package-info.java
│ ├── AdbaColumnMetadata.java
│ ├── Bindings.java
│ ├── SimpleAdbaColumnMetadata.java
│ ├── AdbaAdapter.java
│ ├── AdbaConnectionFactory.java
│ ├── AdbaException.java
│ ├── AdbaConnection.java
│ ├── AdbaUtils.java
│ ├── AdbaRow.java
│ ├── Binding.java
│ ├── AdbaStatement.java
│ └── Assert.java
├── ci
├── release.yml
├── build.yml
├── release.sh
├── create-release.sh
└── build.sh
├── NOTICE
├── README.md
├── mvnw.cmd
├── mvnw
├── LICENSE
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.iml
3 | target
4 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r2dbc/r2dbc-over-adba/HEAD/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Mock implementations of ADBA objects..
3 | * This package contains {@link io.r2dbc.adba.mock.MockDataSource} and
4 | * {@link io.r2dbc.adba.mock.MockSession} along with mock operations.
5 | */
6 | @reactor.util.annotation.NonNullApi
7 | package io.r2dbc.adba.mock;
8 |
--------------------------------------------------------------------------------
/ci/release.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: registry-image
6 | source:
7 | repository: openjdk
8 | tag: 11-jdk
9 |
10 | inputs:
11 | - name: r2dbc-over-adba
12 |
13 | outputs:
14 | - name: r2dbc-over-adba-artifactory
15 |
16 | caches:
17 | - path: maven
18 |
19 | run:
20 | path: r2dbc-over-adba/ci/release.sh
21 |
--------------------------------------------------------------------------------
/ci/build.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: registry-image
6 | source:
7 | repository: openjdk
8 | tag: 11-jdk
9 |
10 | inputs:
11 | - name: r2dbc-over-adba
12 | - name: r2dbc-spi-artifactory
13 |
14 | outputs:
15 | - name: r2dbc-over-adba-artifactory
16 |
17 | caches:
18 | - path: maven
19 |
20 | run:
21 | path: r2dbc-over-adba/ci/build.sh
22 |
--------------------------------------------------------------------------------
/ci/release.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euo pipefail
4 |
5 | [[ -d $PWD/maven && ! -d $HOME/.m2 ]] && ln -s $PWD/maven $HOME/.m2
6 |
7 | r2dbc_over_adba_artifactory=$(pwd)/r2dbc-over-adba-artifactory
8 |
9 | rm -rf $HOME/.m2/repository/io/r2dbc 2> /dev/null || :
10 |
11 | cd r2dbc-over-adba
12 | ./mvnw deploy \
13 | -DaltDeploymentRepository=distribution::default::file://${r2dbc_over_adba_artifactory}
14 |
--------------------------------------------------------------------------------
/ci/create-release.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euo pipefail
4 |
5 | RELEASE=$1
6 | SNAPSHOT=$2
7 |
8 | ./mvnw versions:set -DnewVersion=$RELEASE -DgenerateBackupPoms=false
9 | git add .
10 | git commit --message "v$RELEASE Release"
11 | git tag -s v$RELEASE -m "v$RELEASE"
12 |
13 | git reset --hard HEAD^1
14 | ./mvnw versions:set -DnewVersion=$SNAPSHOT -DgenerateBackupPoms=false
15 | git add .
16 | git commit --message "v$SNAPSHOT Development"
17 |
--------------------------------------------------------------------------------
/ci/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euo pipefail
4 |
5 | [[ -d $PWD/maven && ! -d $HOME/.m2 ]] && ln -s $PWD/maven $HOME/.m2
6 |
7 | r2dbc_over_adba_artifactory=$(pwd)/r2dbc-over-adba-artifactory
8 | r2dbc_spi_artifactory=$(pwd)/r2dbc-spi-artifactory
9 |
10 | rm -rf $HOME/.m2/repository/io/r2dbc 2> /dev/null || :
11 |
12 | cd r2dbc-over-adba
13 | ./mvnw deploy \
14 | -DaltDeploymentRepository=distribution::default::file://${r2dbc_over_adba_artifactory} \
15 | -Dr2dbcSpiArtifactory=file://${r2dbc_spi_artifactory}
16 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * An implementation of the Reactive Relational Database Connection API over ADBA-based database drivers. This package contains
3 | * {@link io.r2dbc.adba.AdbaAdapter} as entry-point and supporting implementation classes to use
4 | * ADBA implementations through R2DBC's {@link io.r2dbc.spi.ConnectionFactory}.
5 | *
6 | *
7 | * jdk.incubator.sql2.DataSource dataSource = …;
8 | * ConnectionFactory connectionFactory = AdbaAdapter.fromDataSource(dataSource);
9 | *
10 | */
11 | @reactor.util.annotation.NonNullApi
12 | package io.r2dbc.adba;
13 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Reactive Relational Database Connectivity over ADBA
2 |
3 | Copyright 2017-2019 the original author or authors.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | https://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaColumnMetadata.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.ColumnMetadata;
19 |
20 | /**
21 | * ADBA-specific extension to {@link ColumnMetadata}.
22 | *
23 | * @author Mark Paluch
24 | */
25 | public interface AdbaColumnMetadata extends ColumnMetadata {
26 |
27 | /**
28 | * @return the column index. Indexes are 1-based.
29 | */
30 | int getIndex();
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 | %date{HH:mm:ss.SSS} %-18thread %-55logger %msg%n
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/SqlAware.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | /**
19 | * Minimal interface for exposing and setting the SQL behind an operation.
20 | *
21 | * Implemented by {@link jdk.dynalink.Operation} objects.
22 | *
23 | * @author Mark Paluch
24 | */
25 | public interface SqlAware {
26 |
27 | /**
28 | * Set the SQL that is associated with this object.
29 | *
30 | * @param sql the SQL string.
31 | */
32 | void setSql(String sql);
33 |
34 | /**
35 | * @return the SQL string associated with this object, can be {@literal null}.
36 | */
37 | String getSql();
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockTransaction.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.TransactionCompletion;
19 |
20 | /**
21 | * Mock implementation of {@link Transaction}.
22 | *
23 | * @author Mark Paluch
24 | */
25 | public class MockTransaction implements TransactionCompletion {
26 |
27 | private boolean rollbackOnly;
28 |
29 | @Override
30 | public boolean setRollbackOnly() {
31 |
32 | if (rollbackOnly) {
33 | return false;
34 | }
35 | return rollbackOnly = true;
36 | }
37 |
38 | @Override
39 | public boolean isRollbackOnly() {
40 | return rollbackOnly;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/SqlAwareMockOperation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | /**
19 | * {@link SqlAware} extension to {@link MockOperation}. Not used directly but rather as base-class for higher level operations.
20 | *
21 | * @author Mark Paluch
22 | * @see MockParameterizedRowCountOperation
23 | * @see MockParameterizedRowOperation
24 | */
25 | public class SqlAwareMockOperation extends MockOperation implements SqlAware {
26 |
27 | private String sql;
28 |
29 | @Override
30 | public void setSql(String sql) {
31 | this.sql = sql;
32 | }
33 |
34 | @Override
35 | public String getSql() {
36 | return sql;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/AdbaConnectionFactoryUnitTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.adba.mock.MockDataSource;
19 | import io.r2dbc.spi.ConnectionFactory;
20 | import io.r2dbc.spi.ConnectionFactoryMetadata;
21 | import org.junit.jupiter.api.Test;
22 |
23 | import static org.assertj.core.api.Assertions.assertThat;
24 |
25 | /**
26 | * Unit tests for {@link AdbaConnectionFactory}.
27 | *
28 | * @author Mark Paluch
29 | */
30 | class AdbaConnectionFactoryUnitTests {
31 |
32 | @Test
33 | void shouldReportMetadata() {
34 |
35 | ConnectionFactory connectionFactory = AdbaAdapter.fromDataSource(MockDataSource.newMockBuilder().build());
36 | ConnectionFactoryMetadata metadata = connectionFactory.getMetadata();
37 |
38 | assertThat(metadata.getName()).isEqualTo("ADBA Adapter");
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/MockSessionFactoryTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.adba.mock.MockDataSource;
19 | import io.r2dbc.adba.mock.MockSession;
20 | import jdk.incubator.sql2.Session;
21 | import org.junit.jupiter.api.Test;
22 | import reactor.test.StepVerifier;
23 |
24 | import static org.assertj.core.api.Assertions.assertThat;
25 |
26 | /**
27 | * Tests for {@link io.r2dbc.spi.ConnectionFactory} using {@link io.r2dbc.adba.mock.MockDataSource}.
28 | *
29 | * @author Mark Paluch
30 | */
31 | class MockSessionFactoryTests {
32 |
33 | @Test
34 | void shouldConnectSuccessfully() {
35 |
36 |
37 | MockDataSource dataSource = MockDataSource.newSingletonMock();
38 | MockSession connection = dataSource.getSession();
39 |
40 | StepVerifier.create(AdbaAdapter.fromDataSource(dataSource).create()).expectNextCount(1).verifyComplete();
41 |
42 | assertThat(connection.getSessionLifecycle()).isEqualTo(Session.Lifecycle.ATTACHED);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/Bindings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.Statement;
19 | import reactor.util.annotation.Nullable;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 | import java.util.stream.Stream;
24 |
25 | /**
26 | * Holds one or more parameter bindings for a {@link Statement}.
27 | */
28 | class Bindings {
29 |
30 | private final List bindings = new ArrayList<>();
31 | private @Nullable
32 | Binding current;
33 |
34 | @Override
35 | public String toString() {
36 | return "Bindings{" + "bindings=" + this.bindings + ", current=" + this.current + '}';
37 | }
38 |
39 | void finish() {
40 | this.current = null;
41 | }
42 |
43 | Binding getCurrent() {
44 | if (this.current == null) {
45 | this.current = new Binding();
46 | this.bindings.add(this.current);
47 | }
48 |
49 | return this.current;
50 | }
51 |
52 | Stream stream() {
53 | return this.bindings.stream();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reactive Relational Database Connectivity over ADBA Experiment
2 |
3 | :warning: **This project is no longer being actively maintained as the ADBA efforts were terminated.** :warning:
4 |
5 | We might reconsider reviving this project once ADBA has made significant progress.
6 |
7 | This project contains the [ADBA][a] Adapter of the [R2DBC SPI][r] that allows usage of ADBA drivers by using the R2DBC SPI. This implementation is not intended to be used directly, but rather to be used as the backing implementation for a humane client library to delegate to.
8 |
9 | [a]: https://github.com/pull-vert/adba-mirror
10 | [r]: https://github.com/r2dbc/r2dbc-spi
11 |
12 | ## Maven
13 | Both milestone and snapshot artifacts (library, source, and javadoc) can be found in Maven repositories.
14 |
15 | ```xml
16 |
17 | io.r2dbc
18 | r2dbc-over-adba
19 | 1.0.0.BUILD-SNAPSHOT
20 |
21 | ```
22 |
23 | Artifacts can bound found at the following repositories.
24 |
25 | ### Repositories
26 | ```xml
27 |
28 | spring-snapshots
29 | Spring Snapshots
30 | https://repo.spring.io/snapshot
31 |
32 | true
33 |
34 |
35 | ```
36 |
37 | ```xml
38 |
39 | spring-milestones
40 | Spring Milestones
41 | https://repo.spring.io/milestone
42 |
43 | false
44 |
45 |
46 | ```
47 |
48 | ## License
49 | This project is released under version 2.0 of the [Apache License][l].
50 |
51 | [l]: https://www.apache.org/licenses/LICENSE-2.0
52 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockSubmission.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.Submission;
19 |
20 | import java.util.concurrent.CompletableFuture;
21 | import java.util.concurrent.CompletionStage;
22 |
23 | /**
24 | * Mock implementation of {@link Submission}.
25 | *
26 | * @author Mark Paluch
27 | */
28 | public class MockSubmission implements Submission {
29 |
30 | private boolean canceled = false;
31 | private CompletionStage completionStage;
32 |
33 | public MockSubmission(CompletionStage completionStage) {
34 | this.completionStage = completionStage;
35 | }
36 |
37 | @Override
38 | public CompletionStage cancel() {
39 | return CompletableFuture.completedFuture(true);
40 | }
41 |
42 | @Override
43 | public CompletionStage getCompletionStage() {
44 | return completionStage;
45 | }
46 |
47 | /**
48 | * @return {@literal true} if {@link #cancel()} was called.
49 | */
50 | public boolean isCanceled() {
51 | return canceled;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/ParameterBinding.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.SqlType;
19 | import reactor.util.annotation.Nullable;
20 |
21 | /**
22 | * Value object representing a parameter binding. Used by {@link Parameters}.
23 | *
24 | * @author Mark Paluch
25 | */
26 | public class ParameterBinding {
27 |
28 | private final @Nullable
29 | Object value;
30 | private final @Nullable
31 | SqlType type;
32 |
33 | private ParameterBinding(@Nullable Object value, @Nullable SqlType type) {
34 |
35 | this.value = value;
36 | this.type = type;
37 | }
38 |
39 | /**
40 | * Creates a new {@link ParameterBinding} given {@code value} and its {@link SqlType}.
41 | *
42 | * @param value the value. Can be {@literal null}.
43 | * @param type the value {@link SqlType type}.
44 | * @return a new {@link ParameterBinding}.
45 | */
46 | public static ParameterBinding create(@Nullable Object value, SqlType type) {
47 | return new ParameterBinding(value, type);
48 | }
49 |
50 | /**
51 | * Creates a new {@link ParameterBinding} given {@code value}.
52 | *
53 | * @param value the value. Can be {@literal null}.
54 | * @return a new {@link ParameterBinding}.
55 | */
56 | public static ParameterBinding create(@Nullable Object value) {
57 | return new ParameterBinding(value, null);
58 | }
59 |
60 | /**
61 | * @return the bound value, can be {@literal null}.
62 | */
63 | @Nullable
64 | public Object getValue() {
65 | return value;
66 | }
67 |
68 | @Nullable
69 | public SqlType getType() {
70 | return type;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/SimpleAdbaColumnMetadata.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.ColumnMetadata;
19 | import jdk.incubator.sql2.Result;
20 | import reactor.util.annotation.Nullable;
21 |
22 | import java.util.Optional;
23 |
24 | /**
25 | * Simple implementation of {@link ColumnMetadata}.
26 | *
27 | * @author Mark Paluch
28 | */
29 | class SimpleAdbaColumnMetadata implements AdbaColumnMetadata {
30 |
31 | private final @Nullable
32 | String name;
33 | private final int index;
34 | private final Optional precision;
35 | private final Integer type;
36 |
37 | private SimpleAdbaColumnMetadata(String name, int index, Optional precision, @Nullable Integer type) {
38 | this.name = name;
39 | this.index = index;
40 | this.precision = precision;
41 | this.type = type;
42 | }
43 |
44 | /**
45 | * Create {@link SimpleAdbaColumnMetadata} from {@link jdk.incubator.sql2.Result.Column}.
46 | *
47 | * @param column must not be {@literal null}.
48 | * @return {@link SimpleAdbaColumnMetadata} for {@link jdk.incubator.sql2.Result.Column}.
49 | */
50 | static SimpleAdbaColumnMetadata from(Result.Column column) {
51 | return new SimpleAdbaColumnMetadata(column.identifier(), column.index(), Optional.empty(), null);
52 | }
53 |
54 | @Override
55 | public String getName() {
56 | return name;
57 | }
58 |
59 | @Override
60 | public Optional getPrecision() {
61 | return precision;
62 | }
63 |
64 | @Override
65 | public Integer getType() {
66 | return type;
67 | }
68 |
69 | @Override
70 | public int getIndex() {
71 | return index;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.ConnectionFactory;
19 | import io.r2dbc.spi.Result;
20 | import jdk.incubator.sql2.DataSource;
21 |
22 | import java.util.function.BiFunction;
23 |
24 | /**
25 | * This class is the entry-point ADBA implementations through R2DBC's {@link ConnectionFactory}.
26 | *
27 | *
28 | * jdk.incubator.sql2.DataSource dataSource = …;
29 | * ConnectionFactory connectionFactory = AdbaAdapter.fromDataSource(dataSource);
30 | *
31 | *
32 | * State R2DBC supports a broader feature-set than ADBA which leaves certain operations unsupported.
33 | *
34 | * Supported operations:
35 | *
36 | * {@link jdk.incubator.sql2.RowCountOperation} through {@link Result#getRowsUpdated()}
37 | * {@link jdk.incubator.sql2.ParameterizedRowOperation} through {@link Result#map(BiFunction)}
38 | * Subset of {@link io.r2dbc.spi.RowMetadata} based on result set column identifiers
39 | *
40 | * Unsupported operations:
41 | *
42 | * Savepoints
43 | * Setting of Transaction Mutability
44 | * Setting of Transaction Isolation Levels
45 | *
46 | *
47 | * @author Mark Paluch
48 | * @see jdk.incubator.sql2.DataSource
49 | */
50 | public final class AdbaAdapter {
51 |
52 | /**
53 | * Create a {@link ConnectionFactory} given an {@link DataSource ADBA DataSource}.
54 | *
55 | * @param dataSource must not be {@literal null}.
56 | * @return the {@link ConnectionFactory} adapter for {@link DataSource}.
57 | */
58 | public static ConnectionFactory fromDataSource(DataSource dataSource) {
59 |
60 | Assert.notNull(dataSource, "DataSource must not be null!");
61 | return AdbaConnectionFactory.create(dataSource);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaConnectionFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.ConnectionFactory;
19 | import io.r2dbc.spi.ConnectionFactoryMetadata;
20 | import jdk.incubator.sql2.DataSource;
21 | import jdk.incubator.sql2.Session;
22 | import reactor.core.publisher.Mono;
23 |
24 | /**
25 | * R2DBC wrapper for a {@link jdk.incubator.sql2.DataSource ADBA DataSource}.
26 | *
27 | * @author Mark Paluch
28 | */
29 | class AdbaConnectionFactory implements ConnectionFactory {
30 |
31 | private final DataSource dataSource;
32 |
33 | /**
34 | * Creates a new {@link AdbaConnectionFactory} given {@link DataSource}.
35 | *
36 | * @param dataSource must not be {@literal null}.
37 | */
38 | private AdbaConnectionFactory(DataSource dataSource) {
39 | this.dataSource = dataSource;
40 | }
41 |
42 | /**
43 | * Creates a new {@link AdbaConnectionFactory} given {@link DataSource}.
44 | *
45 | * @param dataSource must not be {@literal null}.
46 | * @return the {@link AdbaConnectionFactory} for {@link DataSource}.
47 | */
48 | static AdbaConnectionFactory create(DataSource dataSource) {
49 | return new AdbaConnectionFactory(dataSource);
50 | }
51 |
52 | /**
53 | * Create a {@link Mono} from the {@link jdk.incubator.sql2.Operation ConnectOperation}.
54 | *
55 | * @return
56 | * @see jdk.incubator.sql2.Session#connectOperation
57 | */
58 | @Override
59 | public Mono create() {
60 |
61 | return Mono.defer(() -> {
62 |
63 | Session session = dataSource.builder().build();
64 | session.attachOperation().submit();
65 |
66 | return Mono.just(AdbaConnection.create(session));
67 | }).onErrorMap(AdbaUtils.exceptionMapper());
68 | }
69 |
70 | @Override
71 | public AdbaConnectionFactoryMetadata getMetadata() {
72 | return AdbaConnectionFactoryMetadata.INSTANCE;
73 | }
74 |
75 | /**
76 | * Static {@link ConnectionFactoryMetadata} for the ADBA adapter.
77 | */
78 | enum AdbaConnectionFactoryMetadata implements ConnectionFactoryMetadata {
79 |
80 | INSTANCE;
81 |
82 | @Override
83 | public String getName() {
84 | return "ADBA Adapter";
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.R2dbcException;
19 | import reactor.util.annotation.Nullable;
20 |
21 | /**
22 | * An exception that represents an ADBA error. This exception is a direct translation of the
23 | * {@link jdk.incubator.sql2.SqlException}.
24 | *
25 | * @author Mark Paluch
26 | */
27 | public final class AdbaException extends R2dbcException {
28 |
29 | private @Nullable
30 | final String sqlString;
31 | private final int position;
32 |
33 | /**
34 | * Create a new {@link AdbaException}.
35 | *
36 | * @param reason exception message.
37 | * @param sqlState vendor-specific SQL state.
38 | * @param errorCode vendor-specific error code.
39 | * @param sqlString causing SQL string.
40 | * @param position position within the causing SQL string.
41 | * @param cause the cause.
42 | */
43 | public AdbaException(@Nullable String reason, @Nullable String sqlState, int errorCode, @Nullable String sqlString,
44 | int position, @Nullable Throwable cause) {
45 |
46 | super(reason, sqlState, errorCode, cause);
47 |
48 | this.sqlString = sqlString;
49 | this.position = position;
50 | }
51 |
52 | /**
53 | * Create a new {@link AdbaException}.
54 | *
55 | * @param cause the cause.
56 | */
57 | public AdbaException(Throwable cause) {
58 |
59 | super(cause);
60 |
61 | this.sqlString = null;
62 | this.position = -1;
63 | }
64 |
65 | /**
66 | * The causing SQL string.
67 | *
68 | * @return causing SQL string.
69 | */
70 | @Nullable
71 | public String getSqlString() {
72 | return sqlString;
73 | }
74 |
75 | /**
76 | * The position within the causing SQL string.
77 | *
78 | * @return position within the causing SQL string.
79 | */
80 | public int getPosition() {
81 | return position;
82 | }
83 |
84 | @Override
85 | public String toString() {
86 | String s = getClass().getName();
87 | String message = getLocalizedMessage();
88 | return (message != null) ? (s + ": " + message) : s;
89 | }
90 |
91 | @Override
92 | public String getLocalizedMessage() {
93 |
94 | String message = super.getLocalizedMessage();
95 | return String.format("%s; SQLSTATE=%s; ERROR=%d; SQL=%s; POSITION=%d", message, getSqlState(), getErrorCode(), getSqlString(), getPosition());
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/BindingUnitTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import jdk.incubator.sql2.*;
19 | import org.junit.jupiter.api.Test;
20 | import org.junit.jupiter.api.extension.ExtendWith;
21 | import org.mockito.Answers;
22 | import org.mockito.Mock;
23 | import org.mockito.junit.jupiter.MockitoExtension;
24 |
25 | import java.time.*;
26 | import java.util.LinkedHashMap;
27 | import java.util.Map;
28 | import java.util.Optional;
29 |
30 | import static org.assertj.core.api.Assertions.assertThat;
31 | import static org.mockito.Mockito.mock;
32 | import static org.mockito.Mockito.verify;
33 |
34 | /**
35 | * Unit tests for {@link Binding}.
36 | *
37 | * @author Mark Paluch
38 | */
39 | @ExtendWith(MockitoExtension.class)
40 | class BindingUnitTests {
41 |
42 | @Mock(answer = Answers.RETURNS_MOCKS)
43 | ParameterizedOperation operation;
44 |
45 | @Test
46 | void shouldBindByName() {
47 |
48 | Binding binding = new Binding();
49 | binding.add("foo", Optional.of("bar"));
50 |
51 | binding.bind(operation);
52 |
53 | verify(operation).set("foo", "bar", AdbaType.VARCHAR);
54 | }
55 |
56 | @Test
57 | void shouldBindByIndex() {
58 |
59 | Binding binding = new Binding();
60 | binding.add(2, Optional.of("bar"));
61 |
62 | binding.bind(operation);
63 |
64 | verify(operation).set("2", "bar", AdbaType.VARCHAR);
65 | }
66 |
67 | @Test
68 | void shouldCorrectlyMapTypes() {
69 |
70 | Map expectation = new LinkedHashMap<>();
71 |
72 | expectation.put(AdbaType.NULL, null);
73 | expectation.put(AdbaType.INTEGER, 1);
74 | expectation.put(AdbaType.BIGINT, 1L);
75 | expectation.put(AdbaType.REAL, 1F);
76 | expectation.put(AdbaType.DOUBLE, 1D);
77 | expectation.put(AdbaType.VARCHAR, "foo");
78 | expectation.put(AdbaType.CLOB, mock(SqlClob.class));
79 | expectation.put(AdbaType.BINARY, new byte[0]);
80 | expectation.put(AdbaType.BOOLEAN, true);
81 | expectation.put(AdbaType.SMALLINT, Short.valueOf("1"));
82 | expectation.put(AdbaType.TINYINT, Byte.valueOf("1"));
83 | expectation.put(AdbaType.TIMESTAMP, LocalDateTime.now());
84 | expectation.put(AdbaType.DATE, LocalDate.now());
85 | expectation.put(AdbaType.TIME_WITH_TIME_ZONE, OffsetTime.now());
86 | expectation.put(AdbaType.TIMESTAMP_WITH_TIME_ZONE, OffsetDateTime.now());
87 | expectation.put(AdbaType.TIME, LocalTime.now());
88 | expectation.put(AdbaType.BLOB, mock(SqlBlob.class));
89 | expectation.put(AdbaType.REF, mock(SqlRef.class));
90 | expectation.put(AdbaType.OTHER, new Object());
91 |
92 | expectation.forEach((expected, value) -> {
93 | assertThat(Binding.determineType(value)).describedAs("Type for " + value).isEqualTo(expected);
94 | });
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaConnection.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.Batch;
19 | import io.r2dbc.spi.Connection;
20 | import io.r2dbc.spi.IsolationLevel;
21 | import reactor.core.publisher.Mono;
22 |
23 | /**
24 | * R2DBC wrapper for a {@link jdk.incubator.sql2.Session ADBA Connection}.
25 | *
26 | * @author Mark Paluch
27 | * @see jdk.incubator.sql2.Session
28 | */
29 | class AdbaConnection implements Connection {
30 |
31 | private final jdk.incubator.sql2.Session delegate;
32 |
33 | /**
34 | * Create a new {@link AdbaConnection} for an {@link jdk.incubator.sql2.Session ADBA Connection}.
35 | *
36 | * @param delegate must not be {@literal null}.
37 | */
38 | private AdbaConnection(jdk.incubator.sql2.Session delegate) {
39 | this.delegate = delegate;
40 | }
41 |
42 | /**
43 | * Create a new {@link AdbaConnection} for an {@link jdk.incubator.sql2.Session ADBA Connection}.
44 | *
45 | * @param delegate must not be {@literal null}.
46 | * @return {@link AdbaConnection} for the {@link jdk.incubator.sql2.Session ADBA Connection}.
47 | */
48 | public static AdbaConnection create(jdk.incubator.sql2.Session delegate) {
49 |
50 | Assert.notNull(delegate, "Connection must not be null!");
51 |
52 | return new AdbaConnection(delegate);
53 | }
54 |
55 | @Override
56 | public Mono beginTransaction() {
57 | return Mono.empty();
58 | }
59 |
60 | @Override
61 | public Mono close() {
62 | return AdbaUtils.submitLater(delegate::closeOperation);
63 | }
64 |
65 | @Override
66 | public Mono commitTransaction() {
67 | return AdbaUtils.executeLater(() -> delegate.commitMaybeRollback(delegate.transactionCompletion())).then();
68 | }
69 |
70 | @Override
71 | public Batch createBatch() {
72 | throw new UnsupportedOperationException();
73 | }
74 |
75 | @Override
76 | public Mono createSavepoint(String name) {
77 | throw new UnsupportedOperationException();
78 | }
79 |
80 | @Override
81 | public AdbaStatement createStatement(String sql) {
82 | return AdbaStatement.create(delegate, sql);
83 | }
84 |
85 | @Override
86 | public Mono releaseSavepoint(String name) {
87 | throw new UnsupportedOperationException();
88 | }
89 |
90 | @Override
91 | public Mono rollbackTransaction() {
92 | return AdbaUtils.executeLater(delegate::rollback).then();
93 | }
94 |
95 | @Override
96 | public Mono rollbackTransactionToSavepoint(String name) {
97 | throw new UnsupportedOperationException();
98 | }
99 |
100 | @Override
101 | public Mono setTransactionIsolationLevel(IsolationLevel isolationLevel) {
102 | throw new UnsupportedOperationException();
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/MockOperationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.adba.mock.*;
19 | import io.r2dbc.spi.Result;
20 | import jdk.incubator.sql2.AdbaType;
21 | import org.junit.jupiter.api.Test;
22 | import reactor.core.publisher.Flux;
23 | import reactor.core.publisher.Mono;
24 | import reactor.test.StepVerifier;
25 |
26 | import java.util.List;
27 |
28 | import static org.assertj.core.api.Assertions.assertThat;
29 |
30 | /**
31 | * Tests for operation execution using {@link MockSession} and operation mocks.
32 | *
33 | * @author Mark Paluch
34 | */
35 | class MockOperationTests {
36 |
37 | @Test
38 | void shouldExecuteRowOperation() {
39 |
40 | MockDataSource dataSource = MockDataSource.newSingletonMock();
41 | MockSession session = dataSource.getSession();
42 |
43 | List resultset = ResultBuilder.builder() //
44 | .withColumn("col", AdbaType.VARCHAR) //
45 | .andResult() //
46 | .withRow("foo").withRow("bar") //
47 | .build();
48 |
49 | session.registerOnCreate(MockParameterizedRowOperation.class, (String sql, MockParameterizedRowOperation op) -> {
50 | op.completeWith(resultset);
51 | });
52 |
53 | Mono> result = Mono.from(AdbaAdapter.fromDataSource(dataSource).create()) //
54 | .flatMapMany(it -> it.createStatement("SELECT * FROM foo").execute()) //
55 | .flatMap(it -> it.map((r, md) -> r.get("col", String.class))) //
56 | .collectList();
57 |
58 | result //
59 | .as(StepVerifier::create) //
60 | .consumeNextWith(actual -> {
61 | assertThat(actual).contains("foo", "bar");
62 | }).verifyComplete();
63 | }
64 |
65 | @Test
66 | void shouldExecuteCountOperation() {
67 |
68 | MockDataSource dataSource = MockDataSource.newSingletonMock();
69 | MockSession session = dataSource.getSession();
70 |
71 | session.registerOnCreate(MockParameterizedRowCountOperation.class, (sql, op) -> {
72 | op.setRowCount(100);
73 | });
74 |
75 | Mono result = Mono.from(AdbaAdapter.fromDataSource(dataSource).create()) //
76 | .flatMapMany(it -> it.createStatement("UPDATE foo").execute()) //
77 | .flatMap(Result::getRowsUpdated) //
78 | .next();
79 |
80 | result //
81 | .as(StepVerifier::create) //
82 | .expectNext(100).verifyComplete();
83 | }
84 |
85 | @Test
86 | void shouldNotSupportGeneratedKeys() {
87 |
88 | MockDataSource dataSource = MockDataSource.newSingletonMock();
89 |
90 | Flux extends Result> result = Mono.from(AdbaAdapter.fromDataSource(dataSource).create()) //
91 | .flatMapMany(it -> it.createStatement("SELECT * FROM foo").returnGeneratedValues().execute());
92 |
93 | result //
94 | .as(StepVerifier::create) //
95 | .expectError(UnsupportedOperationException.class) //
96 | .verify();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/ResultBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.SqlType;
19 |
20 | import java.util.*;
21 |
22 | /**
23 | * Builder for a operation result emitting {@link jdk.incubator.sql2.Result.RowColumn}.
24 | *
25 | * @author Mark Paluch
26 | */
27 | public class ResultBuilder {
28 |
29 | private final Map columnDefs;
30 | private final List> values = new ArrayList<>();
31 |
32 | private ResultBuilder(Map columnDefs) {
33 | this.columnDefs = new LinkedHashMap<>(columnDefs);
34 | }
35 |
36 | /**
37 | * Create a new {@link ColumnDefBuilder} to build a result set.
38 | *
39 | * @return a new {@link ColumnDefBuilder}.
40 | */
41 | public static ColumnDefBuilder builder() {
42 | return new ColumnDefBuilder();
43 | }
44 |
45 | /**
46 | * Configure a new row containing {@code values}.
47 | *
48 | * @param values the values for the row. Value count must match the previously configured column count. May contain {@literal null} values.
49 | * @return {@literal this} {@link ResultBuilder}.
50 | */
51 | public ResultBuilder withRow(Object... values) {
52 |
53 | if (columnDefs.size() != values.length) {
54 | throw new IllegalArgumentException(String.format("Value count %d does not match column count %d!", values.length, columnDefs.size()));
55 | }
56 |
57 | this.values.add(Arrays.asList(values));
58 | return this;
59 | }
60 |
61 | public List build() {
62 |
63 |
64 | List result = new ArrayList<>();
65 |
66 | int rowNum = 0;
67 | for (List rowValues : values) {
68 |
69 | int index = 0;
70 | List columns = new ArrayList<>();
71 | for (Map.Entry entry : columnDefs.entrySet()) {
72 | MockColumn column = new MockColumn(entry.getKey(), index, rowValues.get(index), entry.getValue());
73 |
74 | index++;
75 |
76 | columns.add(column);
77 |
78 | }
79 | result.add(new MockRowColumn(rowNum++, columns));
80 | }
81 |
82 | return result;
83 | }
84 |
85 | /**
86 | * Builder to configure {@link jdk.incubator.sql2.Result.Column columns} of the result.
87 | */
88 | public static class ColumnDefBuilder {
89 |
90 | private Map columnDefs = new LinkedHashMap<>();
91 |
92 | private ColumnDefBuilder() {
93 | }
94 |
95 | /**
96 | * Configure a new column.
97 | *
98 | * @param name
99 | * @param type
100 | * @return {@literal this} {@link ColumnDefBuilder}.
101 | */
102 | public ColumnDefBuilder withColumn(String name, SqlType type) {
103 |
104 | columnDefs.put(name, type);
105 | return this;
106 | }
107 |
108 | /**
109 | * @return the {@link ResultBuilder}.
110 | */
111 | public ResultBuilder andResult() {
112 | return new ResultBuilder(columnDefs);
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/AdbaUtilsUnitTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.R2dbcException;
19 | import jdk.incubator.sql2.Operation;
20 | import jdk.incubator.sql2.SqlException;
21 | import jdk.incubator.sql2.Submission;
22 | import org.junit.jupiter.api.Test;
23 | import org.junit.jupiter.api.extension.ExtendWith;
24 | import org.mockito.junit.jupiter.MockitoExtension;
25 | import reactor.core.publisher.Mono;
26 | import reactor.test.StepVerifier;
27 |
28 | import java.util.concurrent.CompletableFuture;
29 | import java.util.concurrent.CompletionStage;
30 | import java.util.function.Supplier;
31 |
32 | import static org.assertj.core.api.Assertions.assertThat;
33 | import static org.mockito.Mockito.*;
34 |
35 | /**
36 | * Unit tests for {@link AdbaUtils}.
37 | *
38 | * @author Mark Paluch
39 | */
40 | @ExtendWith(MockitoExtension.class)
41 | @SuppressWarnings("unchecked")
42 | class AdbaUtilsUnitTests {
43 |
44 | @Test
45 | void submitLaterShouldDeferExecution() {
46 |
47 | Operation operation = mock(Operation.class);
48 |
49 | AdbaUtils.submitLater(() -> operation);
50 |
51 | verifyZeroInteractions(operation);
52 | }
53 |
54 | @Test
55 | void submitLaterShouldSubmitOperation() {
56 |
57 | Operation operation = mock(Operation.class);
58 | Submission submission = mock(Submission.class);
59 |
60 | doReturn(submission).when(operation).submit();
61 | doReturn(CompletableFuture.completedFuture(new Object())).when(submission).getCompletionStage();
62 |
63 | Mono mono = AdbaUtils.submitLater(() -> operation);
64 |
65 | mono.as(StepVerifier::create).expectNextCount(1).verifyComplete();
66 |
67 | verify(operation).submit();
68 | }
69 |
70 | @Test
71 | void executeLaterShouldDeferExecution() {
72 |
73 | AdbaUtils.executeLater(() -> {
74 | throw new RuntimeException();
75 | });
76 | }
77 |
78 | @Test
79 | void executeLaterShouldObtainFuture() {
80 |
81 | Supplier> supplier = mock(Supplier.class);
82 | when(supplier.get()).thenReturn(CompletableFuture.completedFuture(new Object()));
83 |
84 | Mono mono = AdbaUtils.executeLater(supplier);
85 |
86 | mono.as(StepVerifier::create).expectNextCount(1).verifyComplete();
87 | }
88 |
89 | @Test
90 | void shouldMapGenericExceptions() {
91 |
92 | NullPointerException cause = new NullPointerException();
93 | R2dbcException result = AdbaUtils.exceptionMapper().apply(cause);
94 |
95 | assertThat(result).hasCause(cause);
96 | }
97 |
98 | @Test
99 | void shouldMapSqlExceptions() {
100 |
101 | SqlException cause = new SqlException("foo", null, "state", 42, "sql", 2);
102 |
103 | AdbaException result = AdbaUtils.exceptionMapper().apply(cause);
104 |
105 | assertThat(result).hasCause(cause);
106 | assertThat(result.getMessage()).isEqualTo("foo");
107 | assertThat(result.getErrorCode()).isEqualTo(42);
108 | assertThat(result.getSqlState()).isEqualTo("state");
109 | assertThat(result.getSqlString()).isEqualTo("sql");
110 | assertThat(result.getPosition()).isEqualTo(2);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import jdk.incubator.sql2.Operation;
19 | import jdk.incubator.sql2.SqlException;
20 | import jdk.incubator.sql2.Submission;
21 | import reactor.core.publisher.Mono;
22 |
23 | import java.util.concurrent.CompletionStage;
24 | import java.util.function.Function;
25 | import java.util.function.Supplier;
26 |
27 | /**
28 | * Utilities for ADBA calls to defer excution and map errors.
29 | *
30 | * @author Mark Paluch
31 | */
32 | class AdbaUtils {
33 |
34 | /**
35 | * Mapping function to translate ADBA exceptions into R2DBC exceptions.
36 | */
37 | private static final Function EXCEPTION_MAPPER = throwable -> {
38 |
39 | if (throwable instanceof SqlException) {
40 | SqlException ex = (SqlException) throwable;
41 |
42 | return new AdbaException(ex.getMessage(), ex.getSqlState(), ex.getVendorCode(), ex.getSqlString(),
43 | ex.getPosition(), ex);
44 | }
45 |
46 | if (throwable instanceof AdbaException) {
47 | return (AdbaException) throwable;
48 | }
49 |
50 | return new AdbaException(throwable);
51 | };
52 |
53 | /**
54 | * Create a {@link Mono} that submits an {@link Operation ADBA operation} on subscription.
55 | *
56 | * @param operationSupplier the suppler function to obtain a {@link Operation}.
57 | * @return {@link Mono} wrapper for a {@link Operation} supplier.
58 | */
59 | static Mono submitLater(Supplier> operationSupplier) {
60 |
61 | return Mono.create(it -> {
62 |
63 | Operation operation = operationSupplier.get();
64 | Submission submission = operation.submit();
65 | it.onCancel(submission::cancel);
66 | submission.getCompletionStage().whenComplete((result, e) -> {
67 |
68 | if (e != null) {
69 | it.error(e);
70 | } else {
71 | if (result != null) {
72 | it.success(result);
73 | } else {
74 | it.success();
75 | }
76 | }
77 | });
78 | }).onErrorMap(exceptionMapper());
79 | }
80 |
81 | /**
82 | * Create a {@link Mono} that invokes an asynchronous operation synchronized by {@link CompletionStage} on
83 | * subscription.
84 | *
85 | * @param completionStageSupplier the suppler function to obtain a {@link CompletionStage}.
86 | * @return {@link Mono} wrapper for a {@link CompletionStage} supplier.
87 | */
88 | static Mono executeLater(Supplier extends CompletionStage> completionStageSupplier) {
89 | return Mono.defer(() -> Mono.fromCompletionStage(completionStageSupplier.get())).onErrorMap(exceptionMapper());
90 | }
91 |
92 | /**
93 | * Exception mapping {@link Function} that translates {@link SqlException ADBA SqlException} to a
94 | * {@link AdbaException} using the R2DBC exception hierarchy.
95 | *
96 | * @return the exception mapping function.
97 | */
98 | static Function exceptionMapper() {
99 | return EXCEPTION_MAPPER;
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaRow.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.spi.Row;
19 | import io.r2dbc.spi.RowMetadata;
20 | import jdk.incubator.sql2.Result;
21 | import reactor.util.annotation.Nullable;
22 |
23 | import java.util.Collection;
24 | import java.util.LinkedHashMap;
25 | import java.util.Map;
26 |
27 | /**
28 | * ADBA-specific {@link Row} and {@link RowMetadata} implementation. {@link RowMetadata} is computed lazily when a metadata method is accessed.
29 | *
30 | * @author Mark Paluch
31 | */
32 | class AdbaRow implements Row, RowMetadata {
33 |
34 | private final jdk.incubator.sql2.Result.RowColumn delegate;
35 | private @Nullable
36 | Map metadataMap;
37 |
38 | /**
39 | * Creates a new {@link AdbaRow} for {@link jdk.incubator.sql2.Result.RowColumn}.
40 | *
41 | * @param delegate must not be {@literal null}.
42 | */
43 | private AdbaRow(Result.RowColumn delegate) {
44 | this.delegate = delegate;
45 | }
46 |
47 | /**
48 | * Creates a new {@link AdbaRow} for {@link jdk.incubator.sql2.Result.RowColumn}.
49 | *
50 | * @param delegate must not be {@literal null}.
51 | * @return the {@link AdbaRow} for {@link jdk.incubator.sql2.Result.RowColumn}.
52 | */
53 | static AdbaRow create(jdk.incubator.sql2.Result.RowColumn delegate) {
54 |
55 | Assert.notNull(delegate, "RowColumn must not be null!");
56 |
57 | return new AdbaRow(delegate);
58 | }
59 |
60 | @Override
61 | public T get(Object identifier, Class type) {
62 | return doGet(getColumn(identifier), type);
63 | }
64 |
65 | @Override
66 | public AdbaColumnMetadata getColumnMetadata(Object identifier) {
67 |
68 | Assert.notNull(identifier, "Identifier must not be null!");
69 |
70 | if (identifier instanceof Integer) {
71 | return getMetadata().get(identifier);
72 | }
73 |
74 | return getColumnMetadatas().stream().filter(it -> identifier.equals(it.getName())).findFirst().orElse(null);
75 | }
76 |
77 | @Override
78 | public Collection getColumnMetadatas() {
79 | return getMetadata().values();
80 | }
81 |
82 | private Map getMetadata() {
83 |
84 | if (metadataMap == null) {
85 | metadataMap = createMetadataMap();
86 | }
87 | return metadataMap;
88 | }
89 |
90 | private Map createMetadataMap() {
91 |
92 | delegate.at(1);
93 |
94 | Map metadataMap = new LinkedHashMap<>();
95 |
96 | for (Result.Column column : delegate) {
97 | metadataMap.put(column.absoluteIndex(), SimpleAdbaColumnMetadata.from(column));
98 | }
99 |
100 |
101 | return metadataMap;
102 | }
103 |
104 | private Result.Column getColumn(Object identifier) {
105 |
106 | if (identifier instanceof Integer) {
107 |
108 | return delegate.at((Integer) identifier);
109 | }
110 |
111 | return delegate.at((String) identifier);
112 | }
113 |
114 | private static T doGet(Result.Column column, @Nullable Class type) {
115 |
116 | if (type != null && !type.equals(Object.class)) {
117 | return column.get(type);
118 | }
119 |
120 | return column.get();
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockOperation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.Operation;
19 | import jdk.incubator.sql2.PrimitiveOperation;
20 |
21 | import java.time.Duration;
22 | import java.util.ArrayList;
23 | import java.util.List;
24 | import java.util.concurrent.CompletableFuture;
25 | import java.util.function.Consumer;
26 |
27 | /**
28 | * Mock implementation of {@link Operation} and {@link PrimitiveOperation}. This {@link Operation} can be customized for a specific outcome or {@link Throwable} to be emitted as result of the {@link #submit() submission}. The operation can be reused across multiple submissions and will create a new {@link MockSubmission} on {@link #submit()}.
29 | *
30 | * @author Mark Paluch
31 | * @see #completeWith(Object)
32 | * @see #completeWithError(Throwable)
33 | */
34 | public class MockOperation implements Operation, PrimitiveOperation {
35 |
36 | private final List onSubmit = new ArrayList<>();
37 |
38 | private Consumer handler = t -> {
39 | };
40 | private Duration timeout = Duration.ZERO;
41 | Throwable throwableToThrow;
42 | T outcome;
43 |
44 | @Override
45 | public MockOperation onError(Consumer handler) {
46 |
47 | this.handler = handler;
48 | return this;
49 | }
50 |
51 | @Override
52 | public MockOperation timeout(Duration minTime) {
53 |
54 | this.timeout = minTime;
55 | return this;
56 | }
57 |
58 | /**
59 | * @return the {@link Duration timeout} configured via {@link #timeout(Duration)}.
60 | */
61 | public Duration getTimeout() {
62 | return timeout;
63 | }
64 |
65 | /**
66 | * Setup stubbing for a successful outcome of this {@link Operation} emitting {@code outcome} on completion.
67 | *
68 | * @param outcome
69 | * @return {@literal this} {@link MockOperation}.
70 | */
71 | public MockOperation completeWith(T outcome) {
72 |
73 | this.outcome = outcome;
74 | this.throwableToThrow = null;
75 |
76 | return this;
77 | }
78 |
79 | /**
80 | * Setup stubbing for an exception outcome of this {@link Operation} emitting {@link Throwable} on completion.
81 | *
82 | * @param throwable
83 | * @return {@literal this} {@link MockOperation}.
84 | */
85 | public MockOperation completeWithError(Throwable throwable) {
86 |
87 | this.outcome = null;
88 | this.throwableToThrow = throwable;
89 |
90 | return this;
91 | }
92 |
93 | /**
94 | * Register a {@link callback-hook} that is invoked on submission.
95 | *
96 | * @param runnable
97 | * @return {@literal this} {@link MockOperation}.
98 | */
99 | public MockOperation onSubmit(Runnable runnable) {
100 |
101 | this.onSubmit.add(runnable);
102 |
103 | return this;
104 | }
105 |
106 | @Override
107 | public MockSubmission submit() {
108 |
109 | CompletableFuture result = new CompletableFuture<>();
110 | if (throwableToThrow != null) {
111 | result.completeExceptionally(throwableToThrow);
112 | handler.accept(throwableToThrow);
113 | } else {
114 | result.complete(outcome);
115 | }
116 |
117 | onSubmit.forEach(Runnable::run);
118 |
119 | return new MockSubmission<>(result);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/Parameters.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.Operation;
19 | import jdk.incubator.sql2.SqlType;
20 |
21 | import java.util.LinkedHashMap;
22 | import java.util.Map;
23 | import java.util.concurrent.CompletionStage;
24 |
25 | /**
26 | * Value object representing bound parameters.
27 | *
28 | * @author Mark Paluch
29 | * @see ParameterBinding
30 | */
31 | public class Parameters {
32 |
33 | private final Map bindings = new LinkedHashMap<>();
34 |
35 | /**
36 | * @return the bound parameters.
37 | */
38 | public Map getBindings() {
39 | return bindings;
40 | }
41 |
42 | /**
43 | * Set a parameter value. The value is captured and should not be modified
44 | * before the {@link Operation} is completed.
45 | *
46 | * @param id the identifier of the parameter marker to be set
47 | * @param value the value the parameter is to be set to
48 | * @param type the SQL type of the value to send to the database
49 | */
50 | public void set(String id, Object value, SqlType type) {
51 | bindings.put(id, ParameterBinding.create(value, type));
52 | }
53 |
54 | /**
55 | * Set a parameter value. Use a default SQL type determined by the type of the
56 | * value argument. The value is captured and should not be modified before the
57 | * {@link Operation} is completed.
58 | *
59 | * @param id the identifier of the parameter marker to be set
60 | * @param value the value the parameter is to be set to
61 | */
62 | public void set(String id, Object value) {
63 | bindings.put(id, ParameterBinding.create(value));
64 | }
65 |
66 | /**
67 | * Set a parameter value to be the value of a
68 | * {@link CompletionStage}. The {@link Operation} will
69 | * not be executed until the {@link CompletionStage} is
70 | * completed. This method allows submitting {@link Operation}s that depend on
71 | * the result of previous {@link Operation}s rather than requiring that the
72 | * dependent {@link Operation} be submitted only when the previous
73 | * {@link Operation} completes.
74 | *
75 | * @param id the identifier of the parameter marker to be set
76 | * @param source the {@link CompletionStage} that provides
77 | * the value the parameter is to be set to
78 | * @param type the SQL type of the value to send to the database
79 | */
80 | public void set(String id, CompletionStage> source, SqlType type) {
81 | bindings.put(id, ParameterBinding.create(source, type));
82 | }
83 |
84 | /**
85 | * Set a parameter value to be the future value of a
86 | * {@link CompletionStage}. The {@link Operation} will
87 | * not be executed until the {@link CompletionStage} is
88 | * completed. This method allows submitting {@link Operation}s that depend on
89 | * the result of previous {@link Operation}s rather than requiring that the
90 | * dependent {@link Operation} be submitted only when the previous
91 | * {@link Operation} completes. Use a default SQL type determined by the type
92 | * of the value of the {@link CompletionStage}
93 | * argument.
94 | *
95 | * @param id the identifier of the parameter marker to be set
96 | * @param source the {@link CompletionStage} that
97 | * provides the value the parameter is to be set to
98 | */
99 | public void set(String id, CompletionStage> source) {
100 | bindings.put(id, ParameterBinding.create(source));
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/AdbaConnectionUnitTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import io.r2dbc.adba.mock.MockTransaction;
19 | import io.r2dbc.spi.IsolationLevel;
20 | import jdk.incubator.sql2.Operation;
21 | import jdk.incubator.sql2.Session;
22 | import jdk.incubator.sql2.Submission;
23 | import jdk.incubator.sql2.TransactionOutcome;
24 | import org.junit.jupiter.api.BeforeEach;
25 | import org.junit.jupiter.api.Test;
26 | import org.junit.jupiter.api.extension.ExtendWith;
27 | import org.mockito.Mock;
28 | import org.mockito.junit.jupiter.MockitoExtension;
29 | import reactor.test.StepVerifier;
30 |
31 | import java.util.concurrent.CompletableFuture;
32 |
33 | import static org.assertj.core.api.Assertions.assertThat;
34 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
35 | import static org.mockito.Mockito.*;
36 |
37 | /**
38 | * Unit tests for {@link AdbaConnection}.
39 | *
40 | * @author Mark Paluch
41 | */
42 | @ExtendWith(MockitoExtension.class)
43 | @SuppressWarnings({"unchecked", "rawtypes"})
44 | class AdbaConnectionUnitTests {
45 |
46 | @Mock
47 | Session session;
48 | @Mock
49 | Operation operation;
50 | @Mock
51 | Submission submission;
52 |
53 | AdbaConnection sut;
54 |
55 | @BeforeEach
56 | void before() {
57 | sut = AdbaConnection.create(session);
58 | }
59 |
60 | @Test
61 | void beginTransaction() {
62 |
63 | sut.beginTransaction().as(StepVerifier::create).verifyComplete();
64 |
65 | verifyZeroInteractions(session);
66 | }
67 |
68 | @Test
69 | void commitTransaction() {
70 |
71 | MockTransaction mockTransaction = new MockTransaction();
72 | when(session.transactionCompletion()).thenReturn(mockTransaction);
73 | when(session.commitMaybeRollback(mockTransaction)).thenReturn(CompletableFuture.completedFuture(TransactionOutcome.COMMIT));
74 |
75 | sut.commitTransaction().as(StepVerifier::create).verifyComplete();
76 | verify(session).commitMaybeRollback(mockTransaction);
77 | }
78 |
79 | @Test
80 | void rollbackTransaction() {
81 |
82 | when(session.rollback()).thenReturn(CompletableFuture.completedFuture(TransactionOutcome.COMMIT));
83 |
84 | sut.rollbackTransaction().as(StepVerifier::create).verifyComplete();
85 | verify(session).rollback();
86 | }
87 |
88 | @Test
89 | void close() {
90 |
91 | when(session.closeOperation()).thenReturn(operation);
92 | when(operation.submit()).thenReturn(submission);
93 | when(submission.getCompletionStage()).thenReturn(CompletableFuture.completedFuture(null));
94 |
95 | sut.close().as(StepVerifier::create).verifyComplete();
96 | verify(operation).submit();
97 | }
98 |
99 | @Test
100 | void createStatement() {
101 |
102 | AdbaStatement statement = sut.createStatement("SELECT * FROM foo");
103 |
104 | assertThat(statement).isNotNull();
105 | }
106 |
107 | @Test
108 | void createBatch() {
109 | assertThatThrownBy(sut::createBatch).isInstanceOf(UnsupportedOperationException.class);
110 | }
111 |
112 | @Test
113 | void createSavepoint() {
114 | assertThatThrownBy(() -> sut.createSavepoint("foo")).isInstanceOf(UnsupportedOperationException.class);
115 | }
116 |
117 | @Test
118 | void releaseSavepoint() {
119 | assertThatThrownBy(() -> sut.releaseSavepoint("foo")).isInstanceOf(UnsupportedOperationException.class);
120 | }
121 |
122 | @Test
123 | void rollbackTransactionToSavepoint() {
124 | assertThatThrownBy(() -> sut.rollbackTransactionToSavepoint("foo")).isInstanceOf(UnsupportedOperationException.class);
125 | }
126 |
127 | @Test
128 | void setTransactionIsolationLevel() {
129 | assertThatThrownBy(() -> sut.setTransactionIsolationLevel(IsolationLevel.READ_COMMITTED)).isInstanceOf(UnsupportedOperationException.class);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | Licensed to the Apache Software Foundation (ASF) under one
3 | or more contributor license agreements. See the NOTICE file
4 | distributed with this work for additional information
5 | regarding copyright ownership. The ASF licenses this file
6 | to you under the Apache License, Version 2.0 (the
7 | "License"); you may not use this file except in compliance
8 | with the License. You may obtain a copy of the License at
9 |
10 | https://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing,
13 | software distributed under the License is distributed on an
14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations
17 | under the License.
18 | */
19 |
20 | import java.net.*;
21 | import java.io.*;
22 | import java.nio.channels.*;
23 | import java.util.Properties;
24 |
25 | public class MavenWrapperDownloader {
26 |
27 | /**
28 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
29 | */
30 | private static final String DEFAULT_DOWNLOAD_URL =
31 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
32 |
33 | /**
34 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
35 | * use instead of the default one.
36 | */
37 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
38 | ".mvn/wrapper/maven-wrapper.properties";
39 |
40 | /**
41 | * Path where the maven-wrapper.jar will be saved to.
42 | */
43 | private static final String MAVEN_WRAPPER_JAR_PATH =
44 | ".mvn/wrapper/maven-wrapper.jar";
45 |
46 | /**
47 | * Name of the property which should be used to override the default download url for the wrapper.
48 | */
49 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
50 |
51 | public static void main(String args[]) {
52 | System.out.println("- Downloader started");
53 | File baseDirectory = new File(args[0]);
54 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
55 |
56 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
57 | // wrapperUrl parameter.
58 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
59 | String url = DEFAULT_DOWNLOAD_URL;
60 | if(mavenWrapperPropertyFile.exists()) {
61 | FileInputStream mavenWrapperPropertyFileInputStream = null;
62 | try {
63 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
64 | Properties mavenWrapperProperties = new Properties();
65 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
66 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
67 | } catch (IOException e) {
68 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
69 | } finally {
70 | try {
71 | if(mavenWrapperPropertyFileInputStream != null) {
72 | mavenWrapperPropertyFileInputStream.close();
73 | }
74 | } catch (IOException e) {
75 | // Ignore ...
76 | }
77 | }
78 | }
79 | System.out.println("- Downloading from: : " + url);
80 |
81 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
82 | if(!outputFile.getParentFile().exists()) {
83 | if(!outputFile.getParentFile().mkdirs()) {
84 | System.out.println(
85 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
86 | }
87 | }
88 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
89 | try {
90 | downloadFileFromURL(url, outputFile);
91 | System.out.println("Done");
92 | System.exit(0);
93 | } catch (Throwable e) {
94 | System.out.println("- Error downloading");
95 | e.printStackTrace();
96 | System.exit(1);
97 | }
98 | }
99 |
100 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
101 | URL website = new URL(urlString);
102 | ReadableByteChannel rbc;
103 | rbc = Channels.newChannel(website.openStream());
104 | FileOutputStream fos = new FileOutputStream(destination);
105 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
106 | fos.close();
107 | rbc.close();
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockColumn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.SqlType;
19 | import reactor.util.annotation.Nullable;
20 |
21 | /**
22 | * Mock implementation of a column. Column indexes are zero-based and translated to one-based indexes by
23 | * {@link jdk.incubator.sql2.Result.RowColumn}.
24 | *
25 | * A column represents a single entry in a row result ("field in a particular row"). {@link MockColumn} can be created
26 | * by {@link ResultBuilder#builder() ResultBuilder} to form an entire result set with column definitions and result
27 | * values.
28 | *
29 | * @author Mark Paluch
30 | * @see ResultBuilder
31 | */
32 | public class MockColumn {
33 |
34 | private final String identifier;
35 | private final int index;
36 | private final Object value;
37 | private final @Nullable
38 | SqlType type;
39 |
40 | /**
41 | * Create a new {@link MockColumn} given {@code identifier}, {@code index} (zero-based) and its {@code value}.
42 | *
43 | * @param identifier must not be {@literal null}.
44 | * @param index positional index, zero-based.
45 | * @param value
46 | */
47 | public MockColumn(String identifier, int index, Object value) {
48 | this(identifier, index, value, null);
49 | }
50 |
51 | /**
52 | * Create a new {@link MockColumn} given {@code identifier}, {@code index} (zero-based), its {@code value}, and
53 | * {@link SqlType}.
54 | *
55 | * @param identifier must not be {@literal null}.
56 | * @param index positional index, zero-based.
57 | * @param value
58 | * @param type
59 | */
60 | public MockColumn(String identifier, int index, Object value, @Nullable SqlType type) {
61 |
62 | this.identifier = identifier;
63 | this.index = index;
64 | this.value = value;
65 | this.type = type;
66 | }
67 |
68 | /**
69 | * Return the value of this column as an instance of the given type.
70 | *
71 | * @param type
72 | * @return the value of this {@link jdk.incubator.sql2.Result.Column}.
73 | */
74 | @Nullable
75 | public T get(Class type) {
76 | return type.cast(this.value);
77 | }
78 |
79 | /**
80 | * Return the identifier of this {@link jdk.incubator.sql2.Result.Column}. May be {@literal null}.
81 | *
82 | * @return the identifier of this {@link jdk.incubator.sql2.Result.Column}. May be {@literal null}.
83 | */
84 | @Nullable
85 | public String identifier() {
86 | return this.identifier;
87 | }
88 |
89 | /**
90 | * Return the 1-based index of this {@link jdk.incubator.sql2.Result.Column}. The returned value is relative to the
91 | * slice if this {@link jdk.incubator.sql2.Result.Column} is the result of a call to {@code slice()}.
92 | * {@code col.slice(n).index() == 1}.
93 | *
94 | * @return the index of this {@link jdk.incubator.sql2.Result.Column}.
95 | */
96 | public int index() {
97 | return this.index;
98 | }
99 |
100 | /**
101 | * Return the SQL type of the value of this {@link jdk.incubator.sql2.Result.Column}.
102 | *
103 | * @return the SQL type of this value.
104 | */
105 | public SqlType sqlType() {
106 | return this.type;
107 | }
108 |
109 | /**
110 | * Return the Java type that best represents the value of this {@link jdk.incubator.sql2.Result.Column}.
111 | *
112 | * @return a {@link Class} that best represents the value of this {@link jdk.incubator.sql2.Result.Column}.
113 | */
114 | @SuppressWarnings("unchecked")
115 | public Class javaType() {
116 | return (Class) (this.value != null ? this.value.getClass() : Void.class);
117 | }
118 |
119 | /**
120 | * The length of the current value if defined.
121 | *
122 | * @return
123 | * @throws UnsupportedOperationException if the length of the current value is undefined.
124 | */
125 | public long length() {
126 |
127 | if (this.value instanceof String) {
128 | return ((String) this.value).length();
129 | }
130 |
131 | throw new UnsupportedOperationException(String.format("Length for %s not supported!", this.value));
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockParameterizedRowCountOperation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.*;
19 |
20 | import java.time.Duration;
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.List;
24 | import java.util.concurrent.CompletionStage;
25 | import java.util.function.Consumer;
26 | import java.util.function.Function;
27 |
28 | /**
29 | * Mock implementation of {@link ParameterizedRowOperation} allowing to complete with {@link #setRowCount(long) a row count}.
30 | *
31 | * @author Mark Paluch
32 | */
33 | public class MockParameterizedRowCountOperation extends SqlAwareMockOperation implements ParameterizedRowCountOperation {
34 |
35 | private final Parameters parameters = new Parameters();
36 | private final List returning = new ArrayList<>();
37 | private final List rowColumns = new ArrayList<>();
38 | private Long rowCount;
39 |
40 | @Override
41 | @SuppressWarnings("unchecked")
42 | public MockParameterizedRowCountOperation onError(Consumer handler) {
43 | return (MockParameterizedRowCountOperation) super.onError(handler);
44 | }
45 |
46 | @Override
47 | @SuppressWarnings("unchecked")
48 | public MockParameterizedRowCountOperation timeout(Duration minTime) {
49 | return (MockParameterizedRowCountOperation) super.timeout(minTime);
50 | }
51 |
52 | @Override
53 | @SuppressWarnings("unchecked")
54 | public MockParameterizedRowCountOperation completeWith(T outcome) {
55 | return (MockParameterizedRowCountOperation) super.completeWith(outcome);
56 | }
57 |
58 | /**
59 | * Setup stubbing for a successful outcome of this {@link Operation} emitting {@link RowColumn RowColumns} on completion.
60 | *
61 | * @param rowColumns
62 | * @return {@literal this} {@link MockParameterizedRowCountOperation}.
63 | */
64 | public MockParameterizedRowCountOperation completeWith(Iterable extends Result.RowColumn> rowColumns) {
65 |
66 | for (Result.RowColumn rowColumn : rowColumns) {
67 | this.rowColumns.add(rowColumn);
68 | }
69 |
70 | return this;
71 | }
72 |
73 | @Override
74 | @SuppressWarnings("unchecked")
75 | public MockParameterizedRowCountOperation completeWithError(Throwable throwable) {
76 | return (MockParameterizedRowCountOperation) super.completeWithError(throwable);
77 | }
78 |
79 | @Override
80 | @SuppressWarnings("unchecked")
81 | public MockParameterizedRowCountOperation onSubmit(Runnable runnable) {
82 | return (MockParameterizedRowCountOperation) super.onSubmit(runnable);
83 | }
84 |
85 | @Override
86 | public MockParameterizedRowOperation returning(String... keys) {
87 |
88 | this.returning.addAll(Arrays.asList(keys));
89 | return new MockParameterizedRowOperation<>();
90 | }
91 |
92 | @Override
93 | public MockParameterizedRowCountOperation apply(Function processor) {
94 |
95 | completeWith(processor.apply(() -> rowCount));
96 |
97 | return this;
98 | }
99 |
100 | /**
101 | * Set the row count to report.
102 | *
103 | * @param rowCount
104 | */
105 | public void setRowCount(long rowCount) {
106 | this.rowCount = rowCount;
107 | }
108 |
109 | @Override
110 | public MockParameterizedRowCountOperation set(String id, Object value, SqlType type) {
111 |
112 | parameters.set(id, value, type);
113 | return this;
114 | }
115 |
116 | @Override
117 | public MockParameterizedRowCountOperation set(String id, CompletionStage> source, SqlType type) {
118 | parameters.set(id, source, type);
119 | return this;
120 | }
121 |
122 | @Override
123 | public MockParameterizedRowCountOperation set(String id, CompletionStage> source) {
124 | parameters.set(id, source);
125 | return this;
126 | }
127 |
128 | @Override
129 | public MockParameterizedRowCountOperation set(String id, Object value) {
130 | parameters.set(id, value);
131 | return this;
132 | }
133 |
134 | /**
135 | * @return the bound parameters.
136 | */
137 | public Parameters getParameters() {
138 | return parameters;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/Binding.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import jdk.incubator.sql2.AdbaType;
19 | import jdk.incubator.sql2.ParameterizedOperation;
20 | import jdk.incubator.sql2.SqlClob;
21 | import jdk.incubator.sql2.SqlType;
22 | import reactor.util.annotation.Nullable;
23 |
24 | import java.util.*;
25 | import java.util.Map.Entry;
26 |
27 | /**
28 | * Value object holding the parameter binding for a {@link io.r2dbc.spi.Statement}.
29 | *
30 | * @author Mark Paluch
31 | */
32 | class Binding {
33 |
34 | private final static Map, AdbaType> typeMap = new LinkedHashMap<>();
35 |
36 | static {
37 |
38 | for (AdbaType adbaType : AdbaType.values()) {
39 | typeMap.put(adbaType.getJavaType(), adbaType);
40 | }
41 |
42 | typeMap.put(Void.class, AdbaType.NULL);
43 | typeMap.put(String.class, AdbaType.VARCHAR);
44 | typeMap.put(Object.class, AdbaType.OTHER);
45 | typeMap.put(Double.class, AdbaType.DOUBLE);
46 | typeMap.put(SqlClob.class, AdbaType.CLOB);
47 | typeMap.put(byte[].class, AdbaType.BINARY);
48 | }
49 |
50 | private final SortedMap> byIndex = new TreeMap<>();
51 | private final Map> byName = new LinkedHashMap<>();
52 |
53 | /**
54 | * Add a name-based parameter binding.
55 | *
56 | * @param identifier name of the parameter to bind, must not be {@literal null} or empty.
57 | * @param value the value. Can be {@link Optional#empty()} to bind a {@literal null} value. Must not be
58 | * {@literal null}.
59 | */
60 | void add(String identifier, Optional value) {
61 |
62 | Assert.hasText(identifier, "Identifier must not be empty!");
63 | Assert.notNull(value, "Optional must not be null!");
64 |
65 | byName.put(identifier, value);
66 | }
67 |
68 | /**
69 | * Add a index-based parameter binding. Index-based binding begins at position {@literal 0} for the first parameter to
70 | * bind (zero-based index).
71 | *
72 | * @param index index of the parameter to bind.
73 | * @param value the value. Can be {@link Optional#empty()} to bind a {@literal null} value. Must not be
74 | * {@literal null}.
75 | */
76 | void add(int index, Optional value) {
77 |
78 | Assert.isTrue(index >= 0, "Index must be greater or equal to zero!");
79 | Assert.notNull(value, "Optional must not be null!");
80 |
81 | byIndex.put(index, value);
82 | }
83 |
84 | /**
85 | * Bind registered parameters to a {@link ParameterizedOperation}.
86 | *
87 | * @param bindTo the bind target, must not be {@literal null}.
88 | * @return the bound {@link ParameterizedOperation}.
89 | */
90 | @SuppressWarnings("unchecked")
91 | > T bind(T bindTo) {
92 |
93 | T bound = bindTo;
94 | for (Entry> entry : byName.entrySet()) {
95 |
96 | Optional value = entry.getValue();
97 |
98 | String key = entry.getKey();
99 | Object valueToBind = value.orElse(null);
100 | bound = (T) bound.set(key, valueToBind, determineType(valueToBind));
101 | }
102 |
103 | for (Entry> entry : byIndex.entrySet()) {
104 |
105 | Optional value = entry.getValue();
106 |
107 | String key = entry.getKey().toString();
108 | Object valueToBind = value.orElse(null);
109 | bound = (T) bound.set(key, valueToBind, determineType(valueToBind));
110 | }
111 |
112 | return bound;
113 | }
114 |
115 | static SqlType determineType(@Nullable Object valueToBind) {
116 |
117 | if (valueToBind == null) {
118 | return AdbaType.NULL;
119 | }
120 |
121 | for (Entry, AdbaType> entry : typeMap.entrySet()) {
122 | if (entry.getKey().equals(valueToBind.getClass())) {
123 | return entry.getValue();
124 | }
125 | }
126 |
127 | for (Entry, AdbaType> entry : typeMap.entrySet()) {
128 | if (entry.getKey() != Object.class && entry.getKey().isInstance(valueToBind)) {
129 | return entry.getValue();
130 | }
131 | }
132 |
133 | return AdbaType.OTHER;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockRowColumn.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.Result;
19 | import jdk.incubator.sql2.SqlType;
20 | import reactor.util.annotation.Nullable;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 | import java.util.NoSuchElementException;
25 | import java.util.stream.Collectors;
26 |
27 | /**
28 | * Mock implementation of {@link jdk.incubator.sql2.Result.RowColumn}.
29 | *
A {@link jdk.incubator.sql2.Result.RowColumn} represents a single row in a row result that can be traversed for its columns. {@link jdk.incubator.sql2.Result.RowColumn} holds a mutable state regarding the its iteration progress across columns.
30 | *
31 | * @author Mark Paluch
32 | * @see ResultBuilder
33 | */
34 | public class MockRowColumn implements Result.RowColumn {
35 |
36 | private final long rowNumber;
37 | private final List columns;
38 |
39 | private boolean canceled = false;
40 | private int currentIndex;
41 |
42 | /**
43 | * Create a new {@link MockRowColumn} given its {@code rowNumber}.
44 | *
45 | * @param rowNumber
46 | */
47 | public MockRowColumn(long rowNumber) {
48 | this.rowNumber = rowNumber;
49 | this.columns = new ArrayList<>();
50 | }
51 |
52 | /**
53 | * Create a new {@link MockRowColumn} given its {@code rowNumber}, {@link MockColumn columns} and the {@code currentIndex}
54 | *
55 | * @param rowNumber
56 | * @param columns
57 | */
58 | public MockRowColumn(long rowNumber, List columns) {
59 | this(rowNumber, columns, 0);
60 | }
61 |
62 | private MockRowColumn(long rowNumber, List columns, int currentIndex) {
63 | this.rowNumber = rowNumber;
64 | this.columns = new ArrayList<>(columns);
65 | this.currentIndex = currentIndex;
66 | }
67 |
68 | /**
69 | * Add a column to this {@link MockRowColumn}. Adding columns leaves the current iteration index untouched.
70 | *
71 | * @param column must not be {@literal null}.
72 | * @return {@literal this} {@link MockRowColumn}.
73 | */
74 | public MockRowColumn addColumn(MockColumn column) {
75 |
76 | columns.add(column);
77 | return this;
78 | }
79 |
80 | @Override
81 | public long rowNumber() {
82 | return rowNumber;
83 | }
84 |
85 | @Override
86 | public void cancel() {
87 | canceled = true;
88 | }
89 |
90 | /**
91 | * @return {@literal true} if {@link #cancel()} was called.
92 | */
93 | public boolean isCanceled() {
94 | return canceled;
95 | }
96 |
97 | @Override
98 | @Nullable
99 | public T get(Class type) {
100 | return current().get(type);
101 | }
102 |
103 | @Override
104 | @Nullable
105 | public String identifier() {
106 | return current().identifier();
107 | }
108 |
109 | @Override
110 | public int index() {
111 | return this.currentIndex;
112 | }
113 |
114 | @Override
115 | public int absoluteIndex() {
116 | return this.currentIndex;
117 | }
118 |
119 | @Override
120 | public SqlType sqlType() {
121 | return current().sqlType();
122 | }
123 |
124 | @Override
125 | public Class javaType() {
126 | return current().javaType();
127 | }
128 |
129 | @Override
130 | public long length() {
131 | return current().length();
132 | }
133 |
134 | @Override
135 | public int numberOfValuesRemaining() {
136 | return columns.size() - currentIndex;
137 | }
138 |
139 | @Override
140 | public Column at(String id) {
141 |
142 | List columns = this.columns.stream().filter(it -> id.equals(it.identifier())).collect(Collectors.toList());
143 |
144 | if (columns.size() != 1) {
145 | throw new NoSuchElementException(String.format("Found %d for %s", columns.size(), id));
146 | }
147 |
148 | this.currentIndex = columns.get(0).index();
149 | return this;
150 | }
151 |
152 | @Override
153 | public Column at(int index) {
154 |
155 | if (index > 0) {
156 | this.currentIndex = index - 1;
157 | }
158 | if (index < 0) {
159 | if (Math.abs(index) > columns.size()) {
160 | throw new IndexOutOfBoundsException("Index " + index + " requested, but only " + columns.size() + " available.");
161 | }
162 |
163 | this.currentIndex = columns.size() + index;
164 | }
165 | return this;
166 | }
167 |
168 | @Override
169 | public Column slice(int numValues) {
170 | throw new UnsupportedOperationException();
171 | }
172 |
173 | @Override
174 | public Column clone() {
175 | return new MockRowColumn(rowNumber, columns, currentIndex);
176 | }
177 |
178 | private MockColumn current() {
179 | return columns.get(index());
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockParameterizedRowOperation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.*;
19 | import reactor.util.annotation.Nullable;
20 |
21 | import java.time.Duration;
22 | import java.util.ArrayList;
23 | import java.util.List;
24 | import java.util.concurrent.CompletionStage;
25 | import java.util.concurrent.Flow;
26 | import java.util.concurrent.SubmissionPublisher;
27 | import java.util.function.Consumer;
28 | import java.util.stream.Collector;
29 |
30 | /**
31 | * Mock implementation of {@link ParameterizedRowOperation} that can complete with a {@link #completeWith(Iterable) row result}.
32 | *
33 | * @author Mark Paluch
34 | */
35 | public class MockParameterizedRowOperation extends SqlAwareMockOperation implements ParameterizedRowOperation, ParameterizedRowPublisherOperation {
36 |
37 | private final Parameters parameters = new Parameters();
38 | private final List rowColumns = new ArrayList<>();
39 | @Nullable
40 | private Flow.Subscriber super Result.RowColumn> subscriber;
41 | private Long fetchSize;
42 |
43 | @Override
44 | @SuppressWarnings("unchecked")
45 | public MockParameterizedRowOperation onError(Consumer handler) {
46 | return (MockParameterizedRowOperation) super.onError(handler);
47 | }
48 |
49 | @Override
50 | @SuppressWarnings("unchecked")
51 | public MockParameterizedRowOperation timeout(Duration minTime) {
52 | return (MockParameterizedRowOperation) super.timeout(minTime);
53 | }
54 |
55 | @Override
56 | @SuppressWarnings("unchecked")
57 | public MockParameterizedRowOperation completeWith(T outcome) {
58 | return (MockParameterizedRowOperation) super.completeWith(outcome);
59 | }
60 |
61 | /**
62 | * Setup stubbing for a successful outcome of this {@link Operation} emitting {@link RowColumn RowColumns} on completion.
63 | *
64 | * @param rowColumns
65 | * @return {@literal this} {@link MockParameterizedRowOperation}.
66 | */
67 | public MockParameterizedRowOperation completeWith(Iterable extends Result.RowColumn> rowColumns) {
68 |
69 |
70 | for (Result.RowColumn rowColumn : rowColumns) {
71 | this.rowColumns.add(rowColumn);
72 | }
73 |
74 | return this;
75 | }
76 |
77 | @Override
78 | @SuppressWarnings("unchecked")
79 | public MockParameterizedRowOperation completeWithError(Throwable throwable) {
80 | return (MockParameterizedRowOperation) super.completeWithError(throwable);
81 | }
82 |
83 | @Override
84 | @SuppressWarnings("unchecked")
85 | public MockParameterizedRowOperation onSubmit(Runnable runnable) {
86 | return (MockParameterizedRowOperation) super.onSubmit(runnable);
87 | }
88 |
89 | @Override
90 | public MockParameterizedRowOperation fetchSize(long rows) throws IllegalArgumentException {
91 |
92 | this.fetchSize = rows;
93 | return this;
94 | }
95 |
96 | @Override
97 | public MockSubmission submit() {
98 |
99 | if (subscriber != null) {
100 |
101 | SubmissionPublisher publisher = new SubmissionPublisher<>();
102 | publisher.subscribe(subscriber);
103 |
104 |
105 | if (throwableToThrow != null) {
106 | publisher.closeExceptionally(throwableToThrow);
107 | } else {
108 | rowColumns.forEach(publisher::submit);
109 | publisher.close();
110 | }
111 | }
112 |
113 | return super.submit();
114 | }
115 |
116 | /**
117 | * @return the configured fetch size.
118 | */
119 | public Long getFetchSize() {
120 | return this.fetchSize;
121 | }
122 |
123 | @Override
124 | public ParameterizedRowPublisherOperation subscribe(Flow.Subscriber super Result.RowColumn> subscriber, CompletionStage extends T> result) {
125 |
126 | this.subscriber = subscriber;
127 | return this;
128 | }
129 |
130 | @Override
131 | public MockParameterizedRowOperation collect(Collector super Result.RowColumn, A, S> c) {
132 |
133 | super.completeWith(this.rowColumns.stream().collect(c));
134 | return this;
135 | }
136 |
137 | @Override
138 | public MockParameterizedRowOperation set(String id, Object value, SqlType type) {
139 |
140 | this.parameters.set(id, value, type);
141 | return this;
142 | }
143 |
144 | @Override
145 | public MockParameterizedRowOperation set(String id, CompletionStage> source, SqlType type) {
146 | this.parameters.set(id, source, type);
147 | return this;
148 | }
149 |
150 | @Override
151 | public MockParameterizedRowOperation set(String id, CompletionStage> source) {
152 | this.parameters.set(id, source);
153 | return this;
154 | }
155 |
156 | @Override
157 | public MockParameterizedRowOperation set(String id, Object value) {
158 | this.parameters.set(id, value);
159 | return this;
160 | }
161 |
162 | /**
163 | * @return the bound parameters.
164 | */
165 | public Parameters getParameters() {
166 | return this.parameters;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven2 Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
126 | )
127 |
128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
130 | if exist %WRAPPER_JAR% (
131 | echo Found %WRAPPER_JAR%
132 | ) else (
133 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
134 | echo Downloading from: %DOWNLOAD_URL%
135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
136 | echo Finished downloading %WRAPPER_JAR%
137 | )
138 | @REM End of extension
139 |
140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
141 | if ERRORLEVEL 1 goto error
142 | goto end
143 |
144 | :error
145 | set ERROR_CODE=1
146 |
147 | :end
148 | @endlocal & set ERROR_CODE=%ERROR_CODE%
149 |
150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
154 | :skipRcPost
155 |
156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
158 |
159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
160 |
161 | exit /B %ERROR_CODE%
162 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockDataSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.DataSource;
19 | import jdk.incubator.sql2.Session;
20 | import jdk.incubator.sql2.SessionProperty;
21 |
22 | import java.util.Collections;
23 | import java.util.LinkedHashMap;
24 | import java.util.Map;
25 | import java.util.concurrent.atomic.AtomicReference;
26 | import java.util.function.Consumer;
27 | import java.util.function.Function;
28 |
29 | /**
30 | * Mock implementation of {@link DataSource}.
31 | *
32 | * @author Mark Paluch
33 | */
34 | public class MockDataSource implements DataSource {
35 |
36 | private Function, MockSession> connectionSupplier;
37 | private boolean closed;
38 |
39 | /**
40 | * Creates a new {@link MockDataSource} that returns a new {@link MockSession} on each connection request.
41 | */
42 | public MockDataSource() {
43 | this.connectionSupplier = MockSession::new;
44 | }
45 |
46 | private MockDataSource(Function, MockSession> connectionSupplier) {
47 | this.connectionSupplier = connectionSupplier;
48 | }
49 |
50 | /**
51 | * Creates a new builder for {@link MockDataSource} that allows customization of the mock behavior.
52 | *
53 | * @return a new {@link MockDataSourceBuilder}.
54 | */
55 | public static MockDataSourceBuilder newMockBuilder() {
56 | return new MockDataSourceBuilder();
57 | }
58 |
59 | /**
60 | * Creates a new singleton {@link MockDataSource} that returns the same {@link MockSession} on each connect call.
61 | *
62 | * @return a new {@link MockDataSource}.
63 | */
64 | public static MockDataSource newSingletonMock() {
65 | return new MockDataSourceBuilder().singletonConnection().build();
66 | }
67 |
68 | @Override
69 | public Session.Builder builder() {
70 | return new MockSessionBuilder(connectionSupplier);
71 | }
72 |
73 | @Override
74 | public MockSession getSession() {
75 | return connectionSupplier.apply(Collections.emptyMap());
76 | }
77 |
78 | @Override
79 | public MockSession getSession(Consumer handler) {
80 | return connectionSupplier.apply(Collections.emptyMap());
81 | }
82 |
83 | @Override
84 | public void close() {
85 | this.closed = true;
86 | }
87 |
88 | /**
89 | * @return {@literal true} if this {@link DataSource} was closed.
90 | */
91 | public boolean isClosed() {
92 | return closed;
93 | }
94 |
95 | /**
96 | * Mock implementation of {@link Connection.Builder}.
97 | */
98 | public static class MockSessionBuilder implements Session.Builder {
99 |
100 | private final Map connectionProperties = new LinkedHashMap<>();
101 | private final Function, MockSession> connectionSupplier;
102 |
103 | private MockSessionBuilder(Function, MockSession> connectionSupplier) {
104 | this.connectionSupplier = connectionSupplier;
105 | }
106 |
107 | @Override
108 | public Session.Builder property(SessionProperty p, Object v) {
109 | connectionProperties.put(p, v);
110 | return this;
111 | }
112 |
113 | @Override
114 | public MockSession build() {
115 | return connectionSupplier.apply(new LinkedHashMap<>(connectionProperties));
116 | }
117 | }
118 |
119 | /**
120 | * Builder for a {@link MockDataSource}.
121 | */
122 | public static class MockDataSourceBuilder {
123 |
124 | private Function, MockSession> connectionSupplier;
125 |
126 | /**
127 | * Configure the builder to use a singleton connection. Concurrent calls to {@link Connection#connect()} are guaranteed to return the same connection instance.
128 | *
129 | * @return {@literal this} {@link MockDataSourceBuilder}.
130 | */
131 | public MockDataSourceBuilder singletonConnection() {
132 |
133 | AtomicReference ref = new AtomicReference<>();
134 |
135 | Function, MockSession> connectionSupplier = cp -> {
136 |
137 | MockSession mockSession = ref.get();
138 |
139 | if (mockSession == null) {
140 | ref.compareAndSet(null, new MockSession());
141 | }
142 |
143 | return ref.get();
144 |
145 | };
146 |
147 | return withConnectionSupplier(connectionSupplier);
148 | }
149 |
150 | /**
151 | * Configure the builder to return the provided {@link MockSession}.
152 | *
153 | * @param connection the connection to use, must not be {@literal null}.
154 | * @return {@literal this} {@link MockDataSourceBuilder}.
155 | */
156 | public MockDataSourceBuilder singletonConnection(MockSession connection) {
157 | return withConnectionSupplier(cp -> connection);
158 | }
159 |
160 | /**
161 | * Configure the builder to return the provided {@link MockSession}.
162 | *
163 | * @return {@literal this} {@link MockDataSourceBuilder}.
164 | */
165 | public MockDataSourceBuilder withConnectionSupplier(Function, MockSession> connectionSupplier) {
166 |
167 | this.connectionSupplier = connectionSupplier;
168 | return this;
169 | }
170 |
171 | /**
172 | * Build a new {@link MockDataSource}.
173 | *
174 | * @return the new {@link MockDataSource}.
175 | */
176 | public MockDataSource build() {
177 | return new MockDataSource(connectionSupplier);
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/AdbaStatement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.r2dbc.adba;
18 |
19 | import io.r2dbc.spi.Result;
20 | import io.r2dbc.spi.Row;
21 | import io.r2dbc.spi.RowMetadata;
22 | import io.r2dbc.spi.Statement;
23 | import jdk.incubator.sql2.ParameterizedRowCountOperation;
24 | import jdk.incubator.sql2.ParameterizedRowPublisherOperation;
25 | import jdk.incubator.sql2.Session;
26 | import org.reactivestreams.Publisher;
27 | import org.reactivestreams.Subscriber;
28 | import org.reactivestreams.Subscription;
29 | import reactor.core.publisher.EmitterProcessor;
30 | import reactor.core.publisher.Flux;
31 | import reactor.core.publisher.Mono;
32 |
33 | import java.util.Optional;
34 | import java.util.concurrent.CompletableFuture;
35 | import java.util.concurrent.Flow;
36 | import java.util.function.BiFunction;
37 |
38 | import static jdk.incubator.sql2.Result.RowColumn;
39 | import static jdk.incubator.sql2.Result.RowCount;
40 |
41 | /**
42 | * R2DBC wrapper for a {@link jdk.incubator.sql2.Session ADBA Connection}. Statements are executed late and lazily on
43 | * interaction with {@link Result#getRowsUpdated()} or {@link Result#map(BiFunction)} methods. Currently supported
44 | * operations are:
45 | *
46 | * {@link jdk.incubator.sql2.RowCountOperation}
47 | * {@link jdk.incubator.sql2.ParameterizedRowOperation}
48 | *
49 | *
50 | * @author Mark Paluch
51 | */
52 | class AdbaStatement implements Statement {
53 |
54 | private final Bindings bindings = new Bindings();
55 |
56 | private final jdk.incubator.sql2.Session session;
57 |
58 | private final String sql;
59 |
60 | private AdbaStatement(Session session, String sql) {
61 |
62 | this.session = session;
63 | this.sql = sql;
64 | }
65 |
66 | @Override
67 | public AdbaStatement add() {
68 | this.bindings.finish();
69 | return this;
70 | }
71 |
72 | @Override
73 | public AdbaStatement bind(int index, boolean value) {
74 | return bind(index, (Object) value);
75 | }
76 |
77 | @Override
78 | public AdbaStatement bind(int index, byte value) {
79 | return bind(index, (Object) value);
80 | }
81 |
82 | @Override
83 | public AdbaStatement bind(int index, char value) {
84 | return bind(index, (Object) value);
85 | }
86 |
87 | @Override
88 | public AdbaStatement bind(int index, double value) {
89 | return bind(index, (Object) value);
90 | }
91 |
92 | @Override
93 | public AdbaStatement bind(int index, float value) {
94 | return bind(index, (Object) value);
95 | }
96 |
97 | @Override
98 | public AdbaStatement bind(int index, int value) {
99 | return bind(index, (Object) value);
100 | }
101 |
102 | @Override
103 | public AdbaStatement bind(int index, long value) {
104 | return bind(index, (Object) value);
105 | }
106 |
107 | @Override
108 | public AdbaStatement bind(int index, short value) {
109 | return bind(index, (Object) value);
110 | }
111 |
112 | @Override
113 | public AdbaStatement bind(Object identifier, Object value) {
114 |
115 | this.bindings.getCurrent().add((String) identifier, Optional.of(value));
116 | return this;
117 | }
118 |
119 | @Override
120 | public AdbaStatement bind(int index, Object value) {
121 |
122 | this.bindings.getCurrent().add(index, Optional.of(value));
123 | return this;
124 | }
125 |
126 | @Override
127 | public AdbaStatement bindNull(Object identifier, Class> aClass) {
128 |
129 | this.bindings.getCurrent().add((String) identifier, Optional.empty());
130 |
131 | return this;
132 | }
133 |
134 | @Override
135 | public AdbaStatement bindNull(int index, Class> aClass) {
136 |
137 | this.bindings.getCurrent().add(index, Optional.empty());
138 |
139 | return this;
140 | }
141 |
142 | @Override
143 | public Mono execute() {
144 | return Mono.just(new AdbaResult());
145 | }
146 |
147 | @Override
148 | public Statement returnGeneratedValues(String... columns) {
149 | throw new UnsupportedOperationException("Unsupported SPI operation. Use SQL to retrieve generated keys for a modifying action");
150 | }
151 |
152 | /**
153 | * Creates a {@link AdbaStatement} given {@link Session} and {@code sql}.
154 | *
155 | * @param session must not be {@literal null}.
156 | * @param sql must not be {@literal null}.
157 | * @return the {@link AdbaStatement} for {@link Connection} and {@code sql}
158 | */
159 | static AdbaStatement create(Session session, String sql) {
160 |
161 | Assert.notNull(session, "Session must not be null!");
162 | Assert.notNull(sql, "SQL must not be null!");
163 |
164 | return new AdbaStatement(session, sql);
165 | }
166 |
167 | /**
168 | * R2DBC wrapper for ADBA operations.
169 | */
170 | class AdbaResult implements Result {
171 |
172 | @Override
173 | public Publisher getRowsUpdated() {
174 |
175 | return AdbaUtils.submitLater(() -> {
176 |
177 |
178 | ParameterizedRowCountOperation countOperation = session.rowCountOperation(sql);
179 |
180 | return bindings.getCurrent().bind(countOperation).apply(RowCount::getCount);
181 | }).map(Number::intValue);
182 | }
183 |
184 | @Override
185 | public Publisher map(BiFunction f) {
186 |
187 | EmitterProcessor rowProcessor = EmitterProcessor.create(true);
188 |
189 |
190 | return Flux.defer(() -> {
191 |
192 | ParameterizedRowPublisherOperation publisherOperation = session.rowPublisherOperation(sql);
193 |
194 | ParameterizedRowPublisherOperation subscribe =
195 | bindings.getCurrent().bind(publisherOperation);
196 |
197 | subscribe.subscribe(new FlowSubscriberAdapter<>(rowProcessor), new CompletableFuture<>()).submit();
198 |
199 | return rowProcessor;
200 | }).handle((rowColumn, sink) -> {
201 |
202 | AdbaRow row = AdbaRow.create(rowColumn);
203 |
204 | try {
205 |
206 | T mapped = f.apply(row, row);
207 | if (mapped == null) {
208 | return;
209 | }
210 |
211 | sink.next(mapped);
212 | } catch (Exception e) {
213 | sink.error(e);
214 | }
215 | }).onErrorMap(AdbaUtils.exceptionMapper());
216 | }
217 | }
218 |
219 | /**
220 | * Delegates Reactive Streams {@link Subscription} calls to a {@link Flow.Subscription}.
221 | */
222 | static class FlowSubscriptionAdapter implements Subscription {
223 |
224 | private final Flow.Subscription subscription;
225 |
226 | FlowSubscriptionAdapter(Flow.Subscription subscription) {
227 | this.subscription = subscription;
228 | }
229 |
230 | @Override
231 | public void request(long n) {
232 | subscription.request(n);
233 | }
234 |
235 | @Override
236 | public void cancel() {
237 | subscription.cancel();
238 | }
239 | }
240 |
241 | /**
242 | * Delegates {@link Flow.Subscriber} calls to a Reactive Streams {@link Subscriber}.
243 | *
244 | * @param
245 | */
246 | static class FlowSubscriberAdapter implements Flow.Subscriber {
247 |
248 | private final Subscriber delegate;
249 |
250 | FlowSubscriberAdapter(Subscriber delegate) {
251 | this.delegate = delegate;
252 | }
253 |
254 | @Override
255 | public void onSubscribe(Flow.Subscription subscription) {
256 | delegate.onSubscribe(new FlowSubscriptionAdapter(subscription));
257 | }
258 |
259 | @Override
260 | public void onNext(T item) {
261 | delegate.onNext(item);
262 | }
263 |
264 | @Override
265 | public void onError(Throwable throwable) {
266 | delegate.onError(throwable);
267 | }
268 |
269 | @Override
270 | public void onComplete() {
271 | delegate.onComplete();
272 | }
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven2 Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | # TODO classpath?
118 | fi
119 |
120 | if [ -z "$JAVA_HOME" ]; then
121 | javaExecutable="`which javac`"
122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
123 | # readlink(1) is not available as standard on Solaris 10.
124 | readLink=`which readlink`
125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
126 | if $darwin ; then
127 | javaHome="`dirname \"$javaExecutable\"`"
128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
129 | else
130 | javaExecutable="`readlink -f \"$javaExecutable\"`"
131 | fi
132 | javaHome="`dirname \"$javaExecutable\"`"
133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
134 | JAVA_HOME="$javaHome"
135 | export JAVA_HOME
136 | fi
137 | fi
138 | fi
139 |
140 | if [ -z "$JAVACMD" ] ; then
141 | if [ -n "$JAVA_HOME" ] ; then
142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
143 | # IBM's JDK on AIX uses strange locations for the executables
144 | JAVACMD="$JAVA_HOME/jre/sh/java"
145 | else
146 | JAVACMD="$JAVA_HOME/bin/java"
147 | fi
148 | else
149 | JAVACMD="`which java`"
150 | fi
151 | fi
152 |
153 | if [ ! -x "$JAVACMD" ] ; then
154 | echo "Error: JAVA_HOME is not defined correctly." >&2
155 | echo " We cannot execute $JAVACMD" >&2
156 | exit 1
157 | fi
158 |
159 | if [ -z "$JAVA_HOME" ] ; then
160 | echo "Warning: JAVA_HOME environment variable is not set."
161 | fi
162 |
163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
164 |
165 | # traverses directory structure from process work directory to filesystem root
166 | # first directory with .mvn subdirectory is considered project base directory
167 | find_maven_basedir() {
168 |
169 | if [ -z "$1" ]
170 | then
171 | echo "Path not specified to find_maven_basedir"
172 | return 1
173 | fi
174 |
175 | basedir="$1"
176 | wdir="$1"
177 | while [ "$wdir" != '/' ] ; do
178 | if [ -d "$wdir"/.mvn ] ; then
179 | basedir=$wdir
180 | break
181 | fi
182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
183 | if [ -d "${wdir}" ]; then
184 | wdir=`cd "$wdir/.."; pwd`
185 | fi
186 | # end of workaround
187 | done
188 | echo "${basedir}"
189 | }
190 |
191 | # concatenates all lines of a file
192 | concat_lines() {
193 | if [ -f "$1" ]; then
194 | echo "$(tr -s '\n' ' ' < "$1")"
195 | fi
196 | }
197 |
198 | BASE_DIR=`find_maven_basedir "$(pwd)"`
199 | if [ -z "$BASE_DIR" ]; then
200 | exit 1;
201 | fi
202 |
203 | ##########################################################################################
204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
205 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
206 | ##########################################################################################
207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
208 | if [ "$MVNW_VERBOSE" = true ]; then
209 | echo "Found .mvn/wrapper/maven-wrapper.jar"
210 | fi
211 | else
212 | if [ "$MVNW_VERBOSE" = true ]; then
213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
214 | fi
215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
216 | while IFS="=" read key value; do
217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
218 | esac
219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
220 | if [ "$MVNW_VERBOSE" = true ]; then
221 | echo "Downloading from: $jarUrl"
222 | fi
223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
224 |
225 | if command -v wget > /dev/null; then
226 | if [ "$MVNW_VERBOSE" = true ]; then
227 | echo "Found wget ... using wget"
228 | fi
229 | wget "$jarUrl" -O "$wrapperJarPath"
230 | elif command -v curl > /dev/null; then
231 | if [ "$MVNW_VERBOSE" = true ]; then
232 | echo "Found curl ... using curl"
233 | fi
234 | curl -o "$wrapperJarPath" "$jarUrl"
235 | else
236 | if [ "$MVNW_VERBOSE" = true ]; then
237 | echo "Falling back to using Java to download"
238 | fi
239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
240 | if [ -e "$javaClass" ]; then
241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
242 | if [ "$MVNW_VERBOSE" = true ]; then
243 | echo " - Compiling MavenWrapperDownloader.java ..."
244 | fi
245 | # Compiling the Java class
246 | ("$JAVA_HOME/bin/javac" "$javaClass")
247 | fi
248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
249 | # Running the downloader
250 | if [ "$MVNW_VERBOSE" = true ]; then
251 | echo " - Running MavenWrapperDownloader.java ..."
252 | fi
253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
254 | fi
255 | fi
256 | fi
257 | fi
258 | ##########################################################################################
259 | # End of extension
260 | ##########################################################################################
261 |
262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
263 | if [ "$MVNW_VERBOSE" = true ]; then
264 | echo $MAVEN_PROJECTBASEDIR
265 | fi
266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
267 |
268 | # For Cygwin, switch paths to Windows format before running java
269 | if $cygwin; then
270 | [ -n "$M2_HOME" ] &&
271 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
272 | [ -n "$JAVA_HOME" ] &&
273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
274 | [ -n "$CLASSPATH" ] &&
275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
276 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
278 | fi
279 |
280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
281 |
282 | exec "$JAVACMD" \
283 | $MAVEN_OPTS \
284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
287 |
--------------------------------------------------------------------------------
/src/test/java/io/r2dbc/adba/mock/MockSession.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba.mock;
17 |
18 | import jdk.incubator.sql2.*;
19 |
20 | import java.time.Duration;
21 | import java.util.ArrayList;
22 | import java.util.Collections;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.concurrent.CompletionStage;
26 | import java.util.function.BiConsumer;
27 | import java.util.function.Consumer;
28 | import java.util.function.LongConsumer;
29 | import java.util.function.Predicate;
30 | import java.util.logging.Logger;
31 | import java.util.stream.Collector;
32 |
33 | /**
34 | * Mock implementation of {@link Connection}.
35 | * Operations for connecting and closing are shared across the {@link Connection} instance and can be {@link Operation#submit() submitted} multiple times.
36 | *
37 | * SQL-based {@link Operation operations} are recorded by this connection for later introspection. Code interested in operation creation notifications (e.g. to customize an operation) can {@link #registerOnCreate(Class, BiConsumer)} callback hooks:
38 | *
39 | *
40 | * connection.registerOnCreate(MockParameterizedRowCountOperation.class, (sql, operation) -> {
41 | * operation.setRowCount(100);
42 | * });
43 | *
44 | * @author Mark Paluch
45 | */
46 | public class MockSession implements Session {
47 |
48 | private final List listeners = new ArrayList<>();
49 | private final List> operationCreationListener = new ArrayList<>();
50 | private final List>> operations = new ArrayList<>();
51 |
52 | private final Map connectionProperties;
53 | private final MockOperation attachOperation = new MockOperation().onSubmit(() -> setConnectionLifecycle(Lifecycle.ATTACHED));
54 | private final MockOperation closeOperation = new MockOperation().onSubmit(() -> setConnectionLifecycle(Lifecycle.CLOSED));
55 | private final MockOperation catchOperation = new MockOperation<>();
56 | private final MockOperation endTransactionOperation = new MockOperation<>();
57 |
58 | private Lifecycle lifecycle;
59 | private MockTransaction transaction = new MockTransaction();
60 |
61 | /**
62 | * Creates a new {@link MockSession}.
63 | */
64 | public MockSession() {
65 | this(Collections.emptyMap());
66 | }
67 |
68 | /**
69 | * Creates a new {@link MockSession} given {@code connectionProperties}.
70 | */
71 | public MockSession(Map connectionProperties) {
72 |
73 | this.connectionProperties = connectionProperties;
74 | setConnectionLifecycle(Lifecycle.NEW);
75 |
76 | registerOnCreate(it -> true, (sql, operation) -> operations.add(new Map.Entry<>() {
77 |
78 | @Override
79 | public String getKey() {
80 | return sql;
81 | }
82 |
83 | @Override
84 | public Operation> getValue() {
85 | return operation;
86 | }
87 |
88 | @Override
89 | public Operation> setValue(Operation> value) {
90 | throw new UnsupportedOperationException();
91 | }
92 | }));
93 | }
94 |
95 | @Override
96 | public MockOperation attachOperation() {
97 | return attachOperation;
98 | }
99 |
100 | @Override
101 | public MockOperation validationOperation(Validation depth) {
102 | throw new UnsupportedOperationException();
103 | }
104 |
105 | @Override
106 | public MockOperation closeOperation() {
107 | return closeOperation;
108 | }
109 |
110 | @Override
111 | public OperationGroup operationGroup() {
112 | throw new UnsupportedOperationException();
113 | }
114 |
115 | @Override
116 | public MockSession registerLifecycleListener(SessionLifecycleListener listener) {
117 |
118 | this.listeners.add(listener);
119 | return this;
120 | }
121 |
122 | @Override
123 | public MockSession deregisterLifecycleListener(SessionLifecycleListener listener) {
124 |
125 | this.listeners.remove(listener);
126 | return this;
127 | }
128 |
129 | @Override
130 | public Lifecycle getSessionLifecycle() {
131 | return lifecycle;
132 | }
133 |
134 | /**
135 | * Set the {@link jdk.incubator.sql2.Connection.Lifecycle}.
136 | *
137 | * @param lifecycle
138 | */
139 | public MockSession setConnectionLifecycle(Lifecycle lifecycle) {
140 |
141 | for (SessionLifecycleListener listener : listeners) {
142 | listener.lifecycleEvent(this, this.lifecycle, lifecycle);
143 |
144 | }
145 | this.lifecycle = lifecycle;
146 |
147 | return this;
148 | }
149 |
150 | @Override
151 | public MockSession abort() {
152 | setConnectionLifecycle(Lifecycle.ABORTING);
153 | return this;
154 | }
155 |
156 | @Override
157 | public Map getProperties() {
158 | return connectionProperties;
159 | }
160 |
161 | @Override
162 | public ShardingKey.Builder shardingKeyBuilder() {
163 | throw new UnsupportedOperationException();
164 | }
165 |
166 | @Override
167 | public OperationGroup parallel() {
168 | throw new UnsupportedOperationException();
169 | }
170 |
171 | @Override
172 | public OperationGroup independent() {
173 | throw new UnsupportedOperationException();
174 | }
175 |
176 | @Override
177 | public OperationGroup conditional(CompletionStage condition) {
178 | throw new UnsupportedOperationException();
179 | }
180 |
181 | @Override
182 | public OperationGroup collect(Collector c) {
183 | throw new UnsupportedOperationException();
184 | }
185 |
186 | @Override
187 | public MockOperation catchOperation() {
188 | return catchOperation;
189 | }
190 |
191 | @Override
192 | public ArrayRowCountOperation arrayRowCountOperation(String sql) {
193 | throw new UnsupportedOperationException();
194 | }
195 |
196 | @Override
197 | public MockParameterizedRowCountOperation rowCountOperation(String sql) {
198 | return newOperation(sql, new MockParameterizedRowCountOperation<>());
199 | }
200 |
201 | @Override
202 | public MockOperation operation(String sql) {
203 | return newOperation(sql, new MockOperation<>());
204 | }
205 |
206 | @Override
207 | public OutOperation outOperation(String sql) {
208 | throw new UnsupportedOperationException();
209 | }
210 |
211 | @Override
212 | public MockParameterizedRowOperation rowOperation(String sql) {
213 | return newOperation(sql, new MockParameterizedRowOperation<>());
214 | }
215 |
216 | @Override
217 | public ParameterizedRowPublisherOperation rowPublisherOperation(String sql) {
218 | return newOperation(sql, new MockParameterizedRowOperation<>());
219 | }
220 |
221 | @Override
222 | public MultiOperation multiOperation(String sql) {
223 | throw new UnsupportedOperationException();
224 | }
225 |
226 | @Override
227 | public TransactionCompletion transactionCompletion() {
228 | return transaction;
229 | }
230 |
231 | @Override
232 | public Operation endTransactionOperation(TransactionCompletion trans) {
233 |
234 | endTransactionOperation.completeWith(trans.isRollbackOnly() ? TransactionOutcome.ROLLBACK : TransactionOutcome.COMMIT);
235 |
236 | return endTransactionOperation;
237 | }
238 |
239 | @Override
240 | public LocalOperation localOperation() {
241 | throw new UnsupportedOperationException();
242 | }
243 |
244 | @Override
245 | public OperationGroup logger(Logger logger) {
246 | throw new UnsupportedOperationException();
247 | }
248 |
249 | @Override
250 | public OperationGroup timeout(Duration minTime) {
251 | throw new UnsupportedOperationException();
252 | }
253 |
254 | @Override
255 | public OperationGroup onError(Consumer handler) {
256 | throw new UnsupportedOperationException();
257 | }
258 |
259 | @Override
260 | public MockSubmission submit() {
261 | return new MockOperation<>().submit();
262 | }
263 |
264 | /**
265 | * Register a {@link BiConsumer listener} that is called whenever a SQL {@link Operation} is created for the given operation {@link Class type}.
266 | *
267 | * @param type filter predicate to filter callbacks for operations that are assignable to {@link Class type}.
268 | * @param listener callback listener.
269 | * @return {@literal this} {@link MockSession}.
270 | */
271 | public > MockSession registerOnCreate(Class super T> type, BiConsumer listener) {
272 | this.operationCreationListener.add(new CreationListener<>(type::isInstance, it -> true, listener));
273 | return this;
274 | }
275 |
276 | /**
277 | * Register a {@link BiConsumer listener} that is called whenever a SQL {@link Operation} is created matching the given {@link Predicate SQL string predicate}.
278 | *
279 | * @param sqlPredicate SQL predicate.
280 | * @param listener callback listener.
281 | * @return {@literal this} {@link MockSession}.
282 | */
283 | public > MockSession registerOnCreate(Predicate sqlPredicate, BiConsumer listener) {
284 | this.operationCreationListener.add(new CreationListener<>(it -> true, sqlPredicate, listener));
285 | return this;
286 | }
287 |
288 | @Override
289 | public Session requestHook(LongConsumer request) {
290 | return null;
291 | }
292 |
293 | private > T newOperation(String sql, T operation) {
294 | operationCreationListener.forEach(creationListener -> creationListener.notify(sql, operation));
295 | return operation;
296 | }
297 |
298 | /**
299 | * Value object encapsulating callback listeners.
300 | */
301 | private static class CreationListener> {
302 |
303 | private final BiConsumer listener;
304 | private final Predicate typeCondition;
305 | private final Predicate sqlCondition;
306 |
307 | CreationListener(Predicate typeCondition, Predicate sqlCondition, BiConsumer listener) {
308 | this.listener = listener;
309 | this.typeCondition = typeCondition;
310 | this.sqlCondition = sqlCondition;
311 | }
312 |
313 | @SuppressWarnings("unchecked")
314 | void notify(String sql, Operation> operation) {
315 |
316 | if (sqlCondition.test(sql) && typeCondition.test(operation)) {
317 | listener.accept(sql, (T) operation);
318 | }
319 | }
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | https://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 | 4.0.0
24 |
25 | io.r2dbc
26 | r2dbc-over-adba
27 | 0.8.0.BUILD-SNAPSHOT
28 | jar
29 |
30 | Reactive Relational Database Connectivity - ADBA Adapter
31 | https://github.com/r2dbc/r2dbc-over-adba
32 |
33 |
34 | 0.07
35 | 3.11.1
36 | 9
37 | 3.0.2
38 | 5.3.2
39 | 1.2.3
40 | 2.23.4
41 | 42.2.5
42 | UTF-8
43 | UTF-8
44 | ${project.version}
45 | Californium-SR3
46 | 1.7.25
47 | 2.1.0.RELEASE
48 | 1.10.1
49 |
50 |
51 |
52 |
53 |
54 | io.projectreactor
55 | reactor-bom
56 | ${reactor.version}
57 | pom
58 | import
59 |
60 |
61 | org.junit
62 | junit-bom
63 | ${junit.version}
64 | pom
65 | import
66 |
67 |
68 | org.testcontainers
69 | testcontainers-bom
70 | ${testcontainers.version}
71 | pom
72 | import
73 |
74 |
75 |
76 |
77 |
78 |
79 | io.projectreactor
80 | reactor-core
81 |
82 |
83 | io.r2dbc
84 | r2dbc-spi
85 | ${r2dbc-spi.version}
86 |
87 |
88 | jdk.incubator
89 | adba
90 | ${adba.version}
91 |
92 |
93 | org.slf4j
94 | slf4j-api
95 | ${slf4j.version}
96 |
97 |
98 |
99 | com.google.code.findbugs
100 | jsr305
101 | ${jsr305.version}
102 | provided
103 |
104 |
105 |
106 | ch.qos.logback
107 | logback-classic
108 | ${logback.version}
109 | test
110 |
111 |
112 | io.projectreactor
113 | reactor-test
114 | test
115 |
116 |
117 | io.r2dbc
118 | r2dbc-spi-test
119 | ${r2dbc-spi.version}
120 | test
121 |
122 |
123 | org.assertj
124 | assertj-core
125 | ${assertj.version}
126 | test
127 |
128 |
129 | org.junit.jupiter
130 | junit-jupiter-api
131 | test
132 |
133 |
134 | org.junit.jupiter
135 | junit-jupiter-engine
136 | test
137 |
138 |
139 | org.mockito
140 | mockito-core
141 | ${mockito.version}
142 | test
143 |
144 |
145 | org.mockito
146 | mockito-junit-jupiter
147 | ${mockito.version}
148 | test
149 |
150 |
151 | org.postgresql
152 | postgresql
153 | ${postgresql.version}
154 | test
155 |
156 |
157 | org.springframework.boot
158 | spring-boot-starter-jdbc
159 | ${spring-boot.version}
160 | test
161 |
162 |
163 | org.testcontainers
164 | postgresql
165 | test
166 |
167 |
168 |
169 |
170 |
171 |
172 | org.apache.maven.plugins
173 | maven-compiler-plugin
174 | 3.8.0
175 |
176 |
177 | -Werror
178 | -Xlint:all
179 | -Xlint:-options
180 | -Xlint:-processing
181 | -Xlint:-serial
182 |
183 | true
184 | ${java.version}
185 | ${java.version}
186 |
187 |
188 |
189 | org.apache.maven.plugins
190 | maven-deploy-plugin
191 | 2.8.2
192 |
193 |
194 | org.apache.maven.plugins
195 | maven-javadoc-plugin
196 | 3.0.1
197 |
198 |
199 | https://projectreactor.io/docs/core/release/api/
200 | https://www.reactive-streams.org/reactive-streams-1.0.2-javadoc/
201 |
202 |
203 |
204 |
205 | attach-javadocs
206 |
207 | jar
208 |
209 |
210 |
211 |
212 |
213 | org.apache.maven.plugins
214 | maven-source-plugin
215 | 3.0.1
216 |
217 |
218 | attach-javadocs
219 |
220 | jar
221 |
222 |
223 |
224 |
225 |
226 | org.apache.maven.plugins
227 | maven-surefire-plugin
228 | 2.22.1
229 |
230 | random
231 |
232 | **/*Example.java
233 | **/*Tests.java
234 |
235 |
236 |
237 |
238 |
239 |
240 | ${project.basedir}
241 |
242 | LICENSE
243 | NOTICE
244 |
245 | META-INF
246 |
247 |
248 |
249 |
250 |
251 |
252 | spring-milestones
253 | Spring Milestones
254 | https://repo.spring.io/milestone
255 |
256 | false
257 |
258 |
259 |
260 | spring-releases
261 | Spring Releases
262 | https://repo.spring.io/release
263 |
264 | false
265 |
266 |
267 |
268 | spring-snapshots
269 | Spring Snapshots
270 | https://repo.spring.io/snapshot
271 |
272 | true
273 |
274 |
275 |
276 | pull-vert-adba
277 | https://dl.bintray.com/pull-vert/adba/
278 |
279 |
280 |
281 |
282 |
283 | r2dbc-spi-artifactory
284 |
285 |
286 | r2dbcSpiArtifactory
287 |
288 |
289 |
290 |
291 | r2dbc-spi-artifactory
292 | ${r2dbcSpiArtifactory}
293 |
294 | true
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
--------------------------------------------------------------------------------
/src/main/java/io/r2dbc/adba/Assert.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.r2dbc.adba;
17 |
18 | import java.util.Collection;
19 | import java.util.function.Supplier;
20 |
21 | /**
22 | * Assertion utility class that assists in validating arguments.
23 | *
24 | * Useful for identifying programmer errors early and clearly at runtime.
25 | *
26 | * For example, if the contract of a public method states it does not allow {@code null} arguments, {@code Assert} can
27 | * be used to validate that contract. Doing this clearly indicates a contract violation when it occurs and protects the
28 | * class's invariants.
29 | *
30 | * Typically used to validate method arguments rather than configuration properties, to check for cases that are usually
31 | * programmer errors rather than configuration errors. In contrast to configuration initialization code, there is
32 | * usually no point in falling back to defaults in such methods.
33 | *
34 | * This class is similar to JUnit's assertion library. If an argument value is deemed invalid, an
35 | * {@link IllegalArgumentException} is thrown (typically). For example:
36 | *
37 | *
38 | * Assert.notNull(clazz, "The class must not be null");
39 | * Assert.isTrue(i > 0, "The value must be greater than zero");
40 | *
41 | *
42 | * Mainly for internal use within the framework; consider
43 | * Apache's Commons Lang for a more comprehensive suite of
44 | * {@code String} utilities. Imported from Spring Framework.
45 | */
46 | abstract class Assert {
47 |
48 | /**
49 | * Assert a boolean expression, throwing an {@code IllegalArgumentException} if the expression evaluates to
50 | * {@code false}.
51 | *
52 | *
53 | * Assert.isTrue(i > 0, "The value must be greater than zero");
54 | *
55 | *
56 | * @param expression a boolean expression
57 | * @param message the exception message to use if the assertion fails
58 | * @throws IllegalArgumentException if {@code expression} is {@code false}
59 | */
60 | public static void isTrue(boolean expression, String message) {
61 | if (!expression) {
62 | throw new IllegalArgumentException(message);
63 | }
64 | }
65 |
66 | /**
67 | * Assert a boolean expression, throwing an {@code IllegalArgumentException} if the expression evaluates to
68 | * {@code false}.
69 | *
70 | *
71 | * Assert.isTrue(i > 0, () -> "The value '" + i + "' must be greater than zero");
72 | *
73 | *
74 | * @param expression a boolean expression
75 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
76 | * @throws IllegalArgumentException if {@code expression} is {@code false}
77 | */
78 | public static void isTrue(boolean expression, Supplier messageSupplier) {
79 | if (!expression) {
80 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
81 | }
82 | }
83 |
84 | /**
85 | * Assert that an object is {@code null}.
86 | *
87 | *
88 | * Assert.isNull(value, "The value must be null");
89 | *
90 | *
91 | * @param object the object to check
92 | * @param message the exception message to use if the assertion fails
93 | * @throws IllegalArgumentException if the object is not {@code null}
94 | */
95 | public static void isNull(Object object, String message) {
96 | if (object != null) {
97 | throw new IllegalArgumentException(message);
98 | }
99 | }
100 |
101 | /**
102 | * Assert that an object is {@code null}.
103 | *
104 | *
105 | * Assert.isNull(value, () -> "The value '" + value + "' must be null");
106 | *
107 | *
108 | * @param object the object to check
109 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
110 | * @throws IllegalArgumentException if the object is not {@code null}
111 | */
112 | public static void isNull(Object object, Supplier messageSupplier) {
113 | if (object != null) {
114 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
115 | }
116 | }
117 |
118 | /**
119 | * Assert that an object is not {@code null}.
120 | *
121 | *
122 | * Assert.notNull(clazz, "The class must not be null");
123 | *
124 | *
125 | * @param object the object to check
126 | * @param message the exception message to use if the assertion fails
127 | * @throws IllegalArgumentException if the object is {@code null}
128 | */
129 | public static void notNull(Object object, String message) {
130 | if (object == null) {
131 | throw new IllegalArgumentException(message);
132 | }
133 | }
134 |
135 | /**
136 | * Assert that an object is not {@code null}.
137 | *
138 | *
139 | * Assert.notNull(clazz, () -> "The class '" + clazz.getName() + "' must not be null");
140 | *
141 | *
142 | * @param object the object to check
143 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
144 | * @throws IllegalArgumentException if the object is {@code null}
145 | */
146 | public static void notNull(Object object, Supplier messageSupplier) {
147 | if (object == null) {
148 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
149 | }
150 | }
151 |
152 | /**
153 | * Assert that the given String is not empty; that is, it must not be {@code null} and not the empty String.
154 | *
155 | *
156 | * Assert.hasLength(name, "Name must not be empty");
157 | *
158 | *
159 | * @param text the String to check
160 | * @param message the exception message to use if the assertion fails
161 | * @throws IllegalArgumentException if the text is empty
162 | * @see StringUtils#hasLength
163 | */
164 | public static void hasLength(String text, String message) {
165 | if (text == null || text.isEmpty()) {
166 | throw new IllegalArgumentException(message);
167 | }
168 | }
169 |
170 | /**
171 | * Assert that the given String is not empty; that is, it must not be {@code null} and not the empty String.
172 | *
173 | *
174 | * Assert.hasLength(name, () -> "Name for account '" + account.getId() + "' must not be empty");
175 | *
176 | *
177 | * @param text the String to check
178 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
179 | * @throws IllegalArgumentException if the text is empty
180 | * @see StringUtils#hasLength
181 | */
182 | public static void hasLength(String text, Supplier messageSupplier) {
183 | if (text.isEmpty()) {
184 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
185 | }
186 | }
187 |
188 | /**
189 | * Assert that the given String contains valid text content; that is, it must not be {@code null} and must contain at
190 | * least one non-whitespace character.
191 | *
192 | *
193 | * Assert.hasText(name, "'name' must not be empty");
194 | *
195 | *
196 | * @param text the String to check
197 | * @param message the exception message to use if the assertion fails
198 | * @throws IllegalArgumentException if the text does not contain valid text content
199 | * @see StringUtils#hasText
200 | */
201 | public static void hasText(String text, String message) {
202 | if (text == null || text.isEmpty()) {
203 | throw new IllegalArgumentException(message);
204 | }
205 | }
206 |
207 | /**
208 | * Assert that the given String contains valid text content; that is, it must not be {@code null} and must contain at
209 | * least one non-whitespace character.
210 | *
211 | *
212 | * Assert.hasText(name, () -> "Name for account '" + account.getId() + "' must not be empty");
213 | *
214 | *
215 | * @param text the String to check
216 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
217 | * @throws IllegalArgumentException if the text does not contain valid text content
218 | * @see StringUtils#hasText
219 | */
220 | public static void hasText(String text, Supplier messageSupplier) {
221 | if (text == null || text.isEmpty()) {
222 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
223 | }
224 | }
225 |
226 | /**
227 | * Assert that an array contains no {@code null} elements.
228 | *
229 | * Note: Does not complain if the array is empty!
230 | *
231 | *
232 | * Assert.noNullElements(array, "The array must contain non-null elements");
233 | *
234 | *
235 | * @param array the array to check
236 | * @param message the exception message to use if the assertion fails
237 | * @throws IllegalArgumentException if the object array contains a {@code null} element
238 | */
239 | public static void noNullElements(Object[] array, String message) {
240 | if (array != null) {
241 | for (Object element : array) {
242 | if (element == null) {
243 | throw new IllegalArgumentException(message);
244 | }
245 | }
246 | }
247 | }
248 |
249 | /**
250 | * Assert that an array contains no {@code null} elements.
251 | *
252 | * Note: Does not complain if the array is empty!
253 | *
254 | *
255 | * Assert.noNullElements(array, () -> "The " + arrayType + " array must contain non-null elements");
256 | *
257 | *
258 | * @param array the array to check
259 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
260 | * @throws IllegalArgumentException if the object array contains a {@code null} element
261 | */
262 | public static void noNullElements(Object[] array, Supplier messageSupplier) {
263 | if (array != null) {
264 | for (Object element : array) {
265 | if (element == null) {
266 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
267 | }
268 | }
269 | }
270 | }
271 |
272 | /**
273 | * Assert that a collection contains elements; that is, it must not be {@code null} and must contain at least one
274 | * element.
275 | *
276 | *
277 | * Assert.notEmpty(collection, "Collection must contain elements");
278 | *
279 | *
280 | * @param collection the collection to check
281 | * @param message the exception message to use if the assertion fails
282 | * @throws IllegalArgumentException if the collection is {@code null} or contains no elements
283 | */
284 | public static void notEmpty(Collection> collection, String message) {
285 | if (collection == null || collection.isEmpty()) {
286 | throw new IllegalArgumentException(message);
287 | }
288 | }
289 |
290 | /**
291 | * Assert that a collection contains elements; that is, it must not be {@code null} and must contain at least one
292 | * element.
293 | *
294 | *
295 | * Assert.notEmpty(collection, () -> "The " + collectionType + " collection must contain elements");
296 | *
297 | *
298 | * @param collection the collection to check
299 | * @param messageSupplier a supplier for the exception message to use if the assertion fails
300 | * @throws IllegalArgumentException if the collection is {@code null} or contains no elements
301 | */
302 | public static void notEmpty(Collection> collection, Supplier messageSupplier) {
303 | if (collection == null || collection.isEmpty()) {
304 | throw new IllegalArgumentException(nullSafeGet(messageSupplier));
305 | }
306 | }
307 |
308 | /**
309 | * Assert that the provided object is an instance of the provided class.
310 | *
311 | *
312 | * Assert.instanceOf(Foo.class, foo, "Foo expected");
313 | *
314 | *
315 | * @param type the type to check against
316 | * @param obj the object to check
317 | * @param message a message which will be prepended to provide further context. If it is empty or ends in ":" or ";"
318 | * or "," or ".", a full exception message will be appended. If it ends in a space, the name of the offending
319 | * object's type will be appended. In any other case, a ":" with a space and the name of the offending
320 | * object's type will be appended.
321 | * @throws IllegalArgumentException if the object is not an instance of type
322 | */
323 | public static void isInstanceOf(Class> type, Object obj, String message) {
324 | notNull(type, "Type to check against must not be null");
325 | if (!type.isInstance(obj)) {
326 | instanceCheckFailed(type, obj, message);
327 | }
328 | }
329 |
330 | /**
331 | * Assert that the provided object is an instance of the provided class.
332 | *
333 | *
334 | * Assert.instanceOf(Foo.class, foo, () -> "Processing " + Foo.class.getSimpleName() + ":");
335 | *
336 | *
337 | * @param type the type to check against
338 | * @param obj the object to check
339 | * @param messageSupplier a supplier for the exception message to use if the assertion fails. See
340 | * {@link #isInstanceOf(Class, Object, String)} for details.
341 | * @throws IllegalArgumentException if the object is not an instance of type
342 | */
343 | public static void isInstanceOf(Class> type, Object obj, Supplier messageSupplier) {
344 | notNull(type, "Type to check against must not be null");
345 | if (!type.isInstance(obj)) {
346 | instanceCheckFailed(type, obj, nullSafeGet(messageSupplier));
347 | }
348 | }
349 |
350 | /**
351 | * Assert that the provided object is an instance of the provided class.
352 | *
353 | *
354 | * Assert.instanceOf(Foo.class, foo);
355 | *
356 | *
357 | * @param type the type to check against
358 | * @param obj the object to check
359 | * @throws IllegalArgumentException if the object is not an instance of type
360 | */
361 | public static void isInstanceOf(Class> type, Object obj) {
362 | isInstanceOf(type, obj, "");
363 | }
364 |
365 | /**
366 | * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
367 | *
368 | *
369 | * Assert.isAssignable(Number.class, myClass, "Number expected");
370 | *
371 | *
372 | * @param superType the super type to check against
373 | * @param subType the sub type to check
374 | * @param message a message which will be prepended to provide further context. If it is empty or ends in ":" or ";"
375 | * or "," or ".", a full exception message will be appended. If it ends in a space, the name of the offending
376 | * sub type will be appended. In any other case, a ":" with a space and the name of the offending sub type
377 | * will be appended.
378 | * @throws IllegalArgumentException if the classes are not assignable
379 | */
380 | public static void isAssignable(Class> superType, Class> subType, String message) {
381 | notNull(superType, "Super type to check against must not be null");
382 | if (subType == null || !superType.isAssignableFrom(subType)) {
383 | assignableCheckFailed(superType, subType, message);
384 | }
385 | }
386 |
387 | /**
388 | * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
389 | *
390 | *
391 | * Assert.isAssignable(Number.class, myClass, () -> "Processing " + myAttributeName + ":");
392 | *
393 | *
394 | * @param superType the super type to check against
395 | * @param subType the sub type to check
396 | * @param messageSupplier a supplier for the exception message to use if the assertion fails. See
397 | * {@link #isAssignable(Class, Class, String)} for details.
398 | * @throws IllegalArgumentException if the classes are not assignable
399 | */
400 | public static void isAssignable(Class> superType, Class> subType, Supplier messageSupplier) {
401 | notNull(superType, "Super type to check against must not be null");
402 | if (subType == null || !superType.isAssignableFrom(subType)) {
403 | assignableCheckFailed(superType, subType, nullSafeGet(messageSupplier));
404 | }
405 | }
406 |
407 | /**
408 | * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
409 | *
410 | *
411 | * Assert.isAssignable(Number.class, myClass);
412 | *
413 | *
414 | * @param superType the super type to check
415 | * @param subType the sub type to check
416 | * @throws IllegalArgumentException if the classes are not assignable
417 | */
418 | public static void isAssignable(Class> superType, Class> subType) {
419 | isAssignable(superType, subType, "");
420 | }
421 |
422 | private static void instanceCheckFailed(Class> type, Object obj, String msg) {
423 | String className = (obj != null ? obj.getClass().getName() : "null");
424 | String result = "";
425 | boolean defaultMessage = true;
426 | if (msg != null && !msg.isEmpty()) {
427 | if (endsWithSeparator(msg)) {
428 | result = msg + " ";
429 | } else {
430 | result = messageWithTypeName(msg, className);
431 | defaultMessage = false;
432 | }
433 | }
434 | if (defaultMessage) {
435 | result = result + ("Object of class [" + className + "] must be an instance of " + type);
436 | }
437 | throw new IllegalArgumentException(result);
438 | }
439 |
440 | private static void assignableCheckFailed(Class> superType, Class> subType, String msg) {
441 | String result = "";
442 | boolean defaultMessage = true;
443 | if (msg != null && !msg.isEmpty()) {
444 | if (endsWithSeparator(msg)) {
445 | result = msg + " ";
446 | } else {
447 | result = messageWithTypeName(msg, subType);
448 | defaultMessage = false;
449 | }
450 | }
451 | if (defaultMessage) {
452 | result = result + (subType + " is not assignable to " + superType);
453 | }
454 | throw new IllegalArgumentException(result);
455 | }
456 |
457 | private static boolean endsWithSeparator(String msg) {
458 | return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith("."));
459 | }
460 |
461 | private static String messageWithTypeName(String msg, Object typeName) {
462 | return msg + (msg.endsWith(" ") ? "" : ": ") + typeName;
463 | }
464 |
465 | private static String nullSafeGet(Supplier messageSupplier) {
466 | return (messageSupplier != null ? messageSupplier.get() : null);
467 | }
468 | }
469 |
--------------------------------------------------------------------------------