diff --git a/data/data-jdbc/README.adoc b/data/data-jdbc/README.adoc new file mode 100644 index 0000000..60b192c --- /dev/null +++ b/data/data-jdbc/README.adoc @@ -0,0 +1 @@ +Tests if Spring Data JDBC with HikariCP is working. diff --git a/data/data-jdbc/build.gradle b/data/data-jdbc/build.gradle new file mode 100644 index 0000000..ccb85ee --- /dev/null +++ b/data/data-jdbc/build.gradle @@ -0,0 +1,24 @@ +plugins { + id "java" + id "org.springframework.boot" + id "org.springframework.cr.smoke-test" +} + +dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation("org.springframework.boot:spring-boot-starter-data-jdbc") + + implementation("org.crac:crac:$cracVersion") + implementation(project(":cr-listener")) + + runtimeOnly("org.postgresql:postgresql") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + appTestImplementation(project(":cr-smoke-test-support")) + appTestImplementation("org.awaitility:awaitility:4.2.0") +} + +crSmokeTest { + webApplication = false +} diff --git a/data/data-jdbc/docker-compose.yml b/data/data-jdbc/docker-compose.yml new file mode 100644 index 0000000..b1e0de0 --- /dev/null +++ b/data/data-jdbc/docker-compose.yml @@ -0,0 +1,7 @@ +services: + postgres: + image: 'postgres:14' + environment: + - 'POSTGRES_PASSWORD=password' + ports: + - '5432' diff --git a/data/data-jdbc/src/appTest/java/com/example/data/jdbc/DataJdbcApplicationTests.java b/data/data-jdbc/src/appTest/java/com/example/data/jdbc/DataJdbcApplicationTests.java new file mode 100644 index 0000000..b8f5d7d --- /dev/null +++ b/data/data-jdbc/src/appTest/java/com/example/data/jdbc/DataJdbcApplicationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.data.jdbc; + +import static org.assertj.core.api.Assertions.*; + +import java.time.Duration; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.springframework.cr.smoketest.support.assertj.AssertableOutput; +import org.springframework.cr.smoketest.support.junit.ApplicationTest; + +@ApplicationTest +public class DataJdbcApplicationTests { + + @Test + void connectionTest(AssertableOutput output) { + Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> { + assertThat(output).hasLineMatching("Starting DataReader: was (INITIALIZED|STOPPED)"); + assertThat(output).hasLineMatching("count \\d+: Author\\{id=\\d, name='.*'}"); + }); + } + +} diff --git a/data/data-jdbc/src/main/java/com/example/data/jdbc/Author.java b/data/data-jdbc/src/main/java/com/example/data/jdbc/Author.java new file mode 100644 index 0000000..fd959a5 --- /dev/null +++ b/data/data-jdbc/src/main/java/com/example/data/jdbc/Author.java @@ -0,0 +1,45 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.data.jdbc; + +import org.springframework.data.annotation.Id; + +public class Author { + + @Id + private final Long id; + + private final String name; + + public Author(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "Author{" + "id=" + id + ", name='" + name + '\'' + '}'; + } + +} diff --git a/data/data-jdbc/src/main/java/com/example/data/jdbc/AuthorRepository.java b/data/data-jdbc/src/main/java/com/example/data/jdbc/AuthorRepository.java new file mode 100644 index 0000000..a2ceb65 --- /dev/null +++ b/data/data-jdbc/src/main/java/com/example/data/jdbc/AuthorRepository.java @@ -0,0 +1,22 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.data.jdbc; + +import org.springframework.data.repository.ListCrudRepository; + +public interface AuthorRepository extends ListCrudRepository { + +} diff --git a/data/data-jdbc/src/main/java/com/example/data/jdbc/DataJdbcApplication.java b/data/data-jdbc/src/main/java/com/example/data/jdbc/DataJdbcApplication.java new file mode 100644 index 0000000..fb216e8 --- /dev/null +++ b/data/data-jdbc/src/main/java/com/example/data/jdbc/DataJdbcApplication.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.data.jdbc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DataJdbcApplication { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(DataJdbcApplication.class, args); + } + +} diff --git a/data/data-jdbc/src/main/java/com/example/data/jdbc/DataReader.java b/data/data-jdbc/src/main/java/com/example/data/jdbc/DataReader.java new file mode 100644 index 0000000..558c8eb --- /dev/null +++ b/data/data-jdbc/src/main/java/com/example/data/jdbc/DataReader.java @@ -0,0 +1,104 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.data.jdbc; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.SmartLifecycle; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +@Component +@EnableScheduling +class DataReader implements SmartLifecycle { + + private final Object lifecycleMonitor = new Object(); + + private final AtomicInteger counter = new AtomicInteger(0); + + private State state = State.INITIALIZED; + + private List authorIds; + + @Autowired + private AuthorRepository authorRepository; + + enum State { + + INITIALIZED, STARTED, STOPPED + + } + + @Override + public void start() { + + synchronized (lifecycleMonitor) { + switch (state) { + case INITIALIZED, STOPPED -> { + System.out.printf("Starting DataReader: was %s\n", state); + if (ObjectUtils.isEmpty(authorIds)) { + authorIds = insertAuthors().stream().map(Author::getId).collect(Collectors.toList()); + } + state = State.STARTED; + } + } + } + } + + @Scheduled(fixedDelay = 1000) + public void scheduled() { + + if (isRunning()) { + + int count = counter.incrementAndGet(); + Author author = authorRepository.findById(authorIds.get(count % 3)).orElse(null); + System.out.printf("count %03d: %s\n", count, author); + } + } + + @Override + public void stop() { + + synchronized (lifecycleMonitor) { + switch (state) { + case INITIALIZED, STARTED -> { + System.out.printf("Stopping DataReader: was %s\n", state); + state = State.STOPPED; + } + } + } + } + + @Override + public boolean isRunning() { + synchronized (lifecycleMonitor) { + return State.STARTED.equals(state); + } + } + + private List insertAuthors() { + + return authorRepository.saveAll(List.of(new Author(null, "Brandon Sanderson"), // + new Author(null, "Brent Weeks"), // + new Author(null, "Peter V. Brett"))); + } + +} diff --git a/data/data-jdbc/src/main/resources/application.properties b/data/data-jdbc/src/main/resources/application.properties new file mode 100644 index 0000000..d350522 --- /dev/null +++ b/data/data-jdbc/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.sql.init.mode=always +spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT_5432:5432}/postgres +spring.datasource.username=postgres +spring.datasource.password=password +spring.datasource.hikari.allow-pool-suspension=true diff --git a/data/data-jdbc/src/main/resources/schema.sql b/data/data-jdbc/src/main/resources/schema.sql new file mode 100644 index 0000000..59a003d --- /dev/null +++ b/data/data-jdbc/src/main/resources/schema.sql @@ -0,0 +1,5 @@ +create table author +( + id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar NOT NULL +);