Add Context Refresh Hikari sample
Closes gh-39
This commit is contained in:
committed by
Sébastien Deleuze
parent
f01a91d613
commit
f9d6380b59
@@ -37,6 +37,11 @@ h|test
|
||||
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-cr-app-test]
|
||||
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-test]
|
||||
|
||||
|context-refresh-hikari
|
||||
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-hikari-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-hikari-app-test]
|
||||
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-hikari-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-hikari-cr-app-test]
|
||||
|
|
||||
|
||||
|context-refresh-http
|
||||
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-app-test]
|
||||
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-cr-app-test]
|
||||
|
||||
@@ -12,6 +12,9 @@ groups:
|
||||
- name: context-refresh
|
||||
app_test: true
|
||||
test: true
|
||||
- name: context-refresh-hikari
|
||||
app_test: true
|
||||
test: false
|
||||
- name: context-refresh-http
|
||||
app_test: true
|
||||
test: true
|
||||
|
||||
33
cloud/context-refresh-hikari/build.gradle
Normal file
33
cloud/context-refresh-hikari/build.gradle
Normal file
@@ -0,0 +1,33 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "org.springframework.boot"
|
||||
id "org.springframework.cr.smoke-test"
|
||||
}
|
||||
|
||||
// Not using Spring Cloud bom due to existing issues with dependency management
|
||||
ext {
|
||||
set('springCloudStarterVersion', "4.1.0-SNAPSHOT")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.crac:crac:$cracVersion")
|
||||
implementation(project(":cr-listener"))
|
||||
implementation("org.springframework.cloud:spring-cloud-starter:${springCloudStarterVersion}")
|
||||
runtimeOnly("org.postgresql:postgresql")
|
||||
|
||||
|
||||
appTestImplementation(project(":cr-smoke-test-support"))
|
||||
appTestImplementation(project(":cloud:context-refresh-hikari"))
|
||||
}
|
||||
|
||||
crSmokeTest {
|
||||
webApplication = true
|
||||
}
|
||||
|
||||
// `@ApplicationTest`s need to only be executed during the `checkpointRestoreAppTest and not `appTest`
|
||||
// (we're checking for a value that we want to have been modified in between these two phases).
|
||||
appTest.enabled = false
|
||||
|
||||
3
cloud/context-refresh-hikari/dev.properties
Normal file
3
cloud/context-refresh-hikari/dev.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
spring.datasource.url: jdbc:postgresql://${DB1_HOST:localhost}:${DB1_PORT_5432:5432}/database1
|
||||
spring.datasource.username=user1
|
||||
spring.datasource.password=passwd1
|
||||
26
cloud/context-refresh-hikari/docker-compose.yml
Normal file
26
cloud/context-refresh-hikari/docker-compose.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
db1:
|
||||
image: 'postgres:16.0-alpine'
|
||||
container_name: db1
|
||||
volumes:
|
||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
environment:
|
||||
- POSTGRES_USER=user1
|
||||
- POSTGRES_PASSWORD=passwd1
|
||||
- POSTGRES_DB=database1
|
||||
ports:
|
||||
- "5432"
|
||||
|
||||
db2:
|
||||
image: 'postgres:16.0-alpine'
|
||||
container_name: db2
|
||||
volumes:
|
||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
environment:
|
||||
- POSTGRES_USER=user2
|
||||
- POSTGRES_PASSWORD=passwd2
|
||||
- POSTGRES_DB=database2
|
||||
ports:
|
||||
- "5432"
|
||||
1
cloud/context-refresh-hikari/init.sql
Normal file
1
cloud/context-refresh-hikari/init.sql
Normal file
@@ -0,0 +1 @@
|
||||
create table if not exists users (id bigserial not null, email varchar(255), name varchar(255), primary key (id));
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.example.contextrefreshhikari;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import com.example.contextrefreshhikari.entities.User;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cr.smoketest.support.assertj.AssertableOutput;
|
||||
import org.springframework.cr.smoketest.support.junit.ApplicationTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SuppressWarnings({ "JUnitMalformedDeclaration" })
|
||||
@ApplicationTest
|
||||
public class ContextRefreshHikariApplicationTests {
|
||||
|
||||
// Bring the config file back to init values before next app start,
|
||||
// since the app updates the property file after properties set
|
||||
@AfterAll
|
||||
static void cleanUp() throws IOException {
|
||||
editExternalConfigurationProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionMetadata(WebTestClient webClient) {
|
||||
webClient.get().uri("/metadata").exchange().expectStatus().isOk().expectBody().consumeWith(result -> {
|
||||
String connectionString = new String(result.getResponseBodyContent());
|
||||
assertThat(connectionString).startsWith("jdbc:postgresql");
|
||||
assertThat(connectionString).endsWith("database2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void users(WebTestClient webTestClient, AssertableOutput output) {
|
||||
// We've connected to a different db after restore, so it should not have any
|
||||
// users
|
||||
webTestClient.get().uri("/users/add").exchange().expectStatus().isOk();
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/users")
|
||||
.header("ContentType", MediaType.APPLICATION_JSON_VALUE)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(User.class)
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
private static void editExternalConfigurationProperties() throws IOException {
|
||||
Files.writeString(Path.of("./dev.properties"), """
|
||||
spring.datasource.url: jdbc:postgresql://${DB1_HOST:localhost}:${DB1_PORT_5432:5432}/database1
|
||||
spring.datasource.username=user1
|
||||
spring.datasource.password=passwd1
|
||||
""", Charset.defaultCharset(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
spring:
|
||||
config:
|
||||
import: file:././dev.properties
|
||||
|
||||
config:
|
||||
path: ././dev.properties
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.example.contextrefreshhikari;
|
||||
|
||||
import com.example.contextrefreshhikari.entities.User;
|
||||
import com.example.contextrefreshhikari.repositories.UserRepository;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ContextRefreshHikariApplication {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(ContextRefreshHikariApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ContextRefreshHikariApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner run(UserRepository userRepository) {
|
||||
return (String[] args) -> {
|
||||
User user1 = new User("John", "john@domain.com");
|
||||
User user2 = new User("Julie", "julie@domain.com");
|
||||
userRepository.save(user1);
|
||||
userRepository.save(user2);
|
||||
userRepository.findAll().forEach(LOG::info);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.example.contextrefreshhikari.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ConfigUpdateController {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(ConfigUpdateController.class);
|
||||
|
||||
@Value("${config.path}")
|
||||
private String path;
|
||||
|
||||
@EventListener(ApplicationStartedEvent.class)
|
||||
public void editExternalConfig() throws IOException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Updating file values");
|
||||
}
|
||||
editExternalConfigurationProperties("""
|
||||
spring.datasource.url: jdbc:postgresql://${DB2_HOST:localhost}:${DB2_PORT_5432:5432}/database2
|
||||
spring.datasource.username=user2
|
||||
spring.datasource.password=passwd2
|
||||
""");
|
||||
}
|
||||
|
||||
@GetMapping("/reset")
|
||||
void reset() throws IOException {
|
||||
editExternalConfigurationProperties("""
|
||||
spring.datasource.url: jdbc:postgresql://${DB1_HOST:localhost}:${DB1_PORT_5432:5432}/database1
|
||||
spring.datasource.username=user1
|
||||
spring.datasource.password=passwd1
|
||||
""");
|
||||
}
|
||||
|
||||
private void editExternalConfigurationProperties(String newFileContent) throws IOException {
|
||||
Files.writeString(Path.of(path), newFileContent, Charset.defaultCharset(), StandardOpenOption.WRITE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.example.contextrefreshhikari.controllers;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/metadata")
|
||||
public class DataSourceMetadataController {
|
||||
|
||||
@Autowired
|
||||
DataSource dataSource;
|
||||
|
||||
@GetMapping
|
||||
public String getConnectionsUrl() throws SQLException {
|
||||
return dataSource.getConnection().getMetaData().getURL();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.example.contextrefreshhikari.controllers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.example.contextrefreshhikari.entities.User;
|
||||
import com.example.contextrefreshhikari.repositories.UserRepository;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
public class UserController {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserController(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<User> getUsers() {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
@GetMapping("/add")
|
||||
void addUsers() {
|
||||
User user1 = new User("Steve", "steve@domain.com");
|
||||
User user2 = new User("Sarah", "sarah@domain.com");
|
||||
userRepository.save(user1);
|
||||
userRepository.save(user2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.example.contextrefreshhikari.entities;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String email;
|
||||
|
||||
public User(String name, String email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.example.contextrefreshhikari.repositories;
|
||||
|
||||
import com.example.contextrefreshhikari.entities.User;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
spring:
|
||||
datasource:
|
||||
hikari:
|
||||
allow-pool-suspension: true
|
||||
driver-class-name: org.postgresql.Driver
|
||||
cloud:
|
||||
refresh:
|
||||
never-refreshable:
|
||||
extra-refreshable:
|
||||
- javax.sql.DataSource
|
||||
- com.zaxxer.hikari.HikariDataSource
|
||||
application:
|
||||
name: context-refresh-hikari
|
||||
config:
|
||||
import: file:./cloud/context-refresh-hikari/dev.properties
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate.id.new_generator_mappings: false
|
||||
config:
|
||||
path: ./cloud/context-refresh-hikari/dev.properties
|
||||
@@ -14,6 +14,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SuppressWarnings({ "JUnitMalformedDeclaration", "DataFlowIssue" })
|
||||
@ApplicationTest
|
||||
public class ContextRefreshHttpApplicationTests {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.contextrefresh;
|
||||
package com.example.contextrefreshhttp.controllers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.contextrefreshhttp;
|
||||
package com.example.contextrefreshhttp.controllers;
|
||||
|
||||
import com.example.contextrefreshhttp.config.SimpleConfigurationProperties;
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SuppressWarnings({ "JUnitMalformedDeclaration", "DataFlowIssue" })
|
||||
@ApplicationTest
|
||||
public class ContextRefreshApplicationTests {
|
||||
|
||||
@@ -21,10 +22,7 @@ public class ContextRefreshApplicationTests {
|
||||
// since the app updates the property file after properties set
|
||||
@AfterAll
|
||||
static void cleanUp() throws IOException {
|
||||
editExternalConfigurationProperties("""
|
||||
simple.test=testVal
|
||||
test=propVal
|
||||
""");
|
||||
editExternalConfigurationProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,9 +47,11 @@ public class ContextRefreshApplicationTests {
|
||||
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("testValNew"));
|
||||
}
|
||||
|
||||
private static void editExternalConfigurationProperties(String newFileContent) throws IOException {
|
||||
Files.writeString(Path.of("./dev.properties"), newFileContent, Charset.defaultCharset(),
|
||||
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
private static void editExternalConfigurationProperties() throws IOException {
|
||||
Files.writeString(Path.of("./dev.properties"), """
|
||||
simple.test=testVal
|
||||
test=propVal
|
||||
""", Charset.defaultCharset(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.contextrefresh;
|
||||
package com.example.contextrefresh.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -24,7 +24,7 @@ public class ConfigUpdateController {
|
||||
private String path;
|
||||
|
||||
@EventListener(ApplicationStartedEvent.class)
|
||||
public void doSomethingAfterStartup() throws IOException {
|
||||
public void editExternalConfig() throws IOException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Updating file values");
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.contextrefreshhttp;
|
||||
package com.example.contextrefresh.controllers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.contextrefresh;
|
||||
package com.example.contextrefresh.controllers;
|
||||
|
||||
import com.example.contextrefresh.config.SimpleConfigurationProperties;
|
||||
|
||||
Reference in New Issue
Block a user