Skip to content

Commit

Permalink
Update to Spring Boot 1.4 and add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Syer committed Aug 25, 2016
1 parent b4121e6 commit 778e175
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 63 deletions.
14 changes: 8 additions & 6 deletions README.adoc
Expand Up @@ -62,6 +62,8 @@ include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/

include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/create_directory_structure_hello.adoc[]

To get you started quickly, here are the complete configurations for the server and client applications:

`configuration-service/pom.xml`
[source,xml]
----
Expand All @@ -81,15 +83,15 @@ include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/

[[initial]]
== Stand up a Config Server
You'll first need a Config Service ( `configuration-service`) to act as a sort of intermediary between your Spring applications and a typically version-controlled repository of configuration files. You can use Spring Cloud's `@EnableConfigServer` to standup a config server that other applications can talk to. This is a regular Spring Boot application with one annotation added to _enable_ the config server.
You'll first need a Config Service to act as a sort of intermediary between your Spring applications and a typically version-controlled repository of configuration files. You can use Spring Cloud's `@EnableConfigServer` to standup a config server that other applications can talk to. This is a regular Spring Boot application with one annotation added to _enable_ the config server.

`configuration-service/src/main/java/hello/ConfigServiceApplication.java`
[source,java]
----
include::complete/configuration-service/src/main/java/hello/ConfigServiceApplication.java[]
----

The Config Server needs to know which repository to manage. There are several choices here, but we'll use a Git-based filesystem repository. You could as easily point the Config Server to a Github or GitLab repository, as well. On the file system, create a new directory and `git init` it. Then add a file called `a-bootiful-client.properties` to the Git repository. Make sure to also `git commit` it, as well. Later, you will connect to the Config Server with a Spring Boot application whose `spring.application.name` property identifies it as `a-bootiful-client` to the Config Server. This is how the Config Server will know which set of configuration to send to a specific client. It will _also_ send all the values from any file named `application.properties` or `application.yml` in the Git repository, though conflicting property keys in the more specifically named file (`a-bootiful-client.properties`) override those in `application.properties` or `application.yml`.
The Config Server needs to know which repository to manage. There are several choices here, but we'll use a Git-based filesystem repository. You could as easily point the Config Server to a Github or GitLab repository, as well. On the file system, create a new directory and `git init` it. Then add a file called `a-bootiful-client.properties` to the Git repository. Make sure to also `git commit` it, as well. Later, you will connect to the Config Server with a Spring Boot application whose `spring.application.name` property identifies it as `a-bootiful-client` to the Config Server. This is how the Config Server will know which set of configuration to send to a specific client. It will _also_ send all the values from any file named `application.properties` or `application.yml` in the Git repository. Property keys in more specifically named files (like `a-bootiful-client.properties`) override those in `application.properties` or `application.yml`.

Add a simple property and value, `message = Hello world`, to the newly created `a-bootiful-client.properties` file and then `git commit` the change.

Expand All @@ -104,7 +106,7 @@ include::complete/configuration-service/src/main/resources/application.propertie

== Reading Configuration from the Config Server using the Config Client

Now that we've stood up a Config Server, let's stand up a new Spring Boot application (`configuration-client`) that uses the Config Server to load its own configuration and that _refreshes_ its configuration to reflect changes to the Config Server on-demand, without restarting the JVM. Add the `org.springframework.cloud:spring-cloud-starter-config` dependency to the `configuration-client` build in order to connect to the Config Server. Spring will see the configuration property files just like it would any property file loaded from `application.properties` or `application.yml` or any other `PropertySource`.
Now that we've stood up a Config Server, let's stand up a new Spring Boot application that uses the Config Server to load its own configuration and that _refreshes_ its configuration to reflect changes to the Config Server on-demand, without restarting the JVM. Add the `org.springframework.cloud:spring-cloud-starter-config` dependency in order to connect to the Config Server. Spring will see the configuration property files just like it would any property file loaded from `application.properties` or `application.yml` or any other `PropertySource`.

The properties to configure the Config Client must necessarily be read in _before_ the rest of the application's configuration is read from the Config Server, during the _bootstrap_ phase. Specify the client's `spring.application.name` as `a-bootiful-client` and the location of the Config Server `spring.cloud.config.uri` in `configuration-client/src/main/resources/bootstrap.properties`, where it will be loaded earlier than any other configuration.

Expand All @@ -116,7 +118,7 @@ include::complete/configuration-client/src/main/resources/bootstrap.properties[]
----


The client may access any value in the Config Server using the traditional mechanisms (`@Value("${...}")` or the `Environment` abstraction). Create a Spring MVC REST controller that returns the resolved `message` property's value. Consult the https://spring.io/guides/gs/rest-service/[Building a RESTful Web Service] guide to learn more about building REST services with Spring MVC and Spring Boot.
The client may access any value in the Config Server using the traditional mechanisms (e.g. `@ConfigurationProperties`, `@Value("${...}")` or through the `Environment` abstraction). Create a Spring MVC REST controller that returns the resolved `message` property's value. Consult the https://spring.io/guides/gs/rest-service/[Building a RESTful Web Service] guide to learn more about building REST services with Spring MVC and Spring Boot.

By default, the configuration values are read on the client's startup, and not again. You can force a bean to _refresh_ its configuration - to pull updated values from the Config Server - by annotating the `MessageRestController` with the Spring Cloud Config `@RefreshScope` and then by triggering a _refresh_ event.

Expand All @@ -128,9 +130,9 @@ include::complete/configuration-client/src/main/java/hello/ConfigClientApplicati

== Test the application

Test the end-to-end result by starting the `configuration-service` first and then, once loaded, starting the `configuration-client`. Visit the `configuration-client` in the browser, `http://localhost:8080/message`. There, you should see the String `Hello world` reflected in the response.
Test the end-to-end result by starting the Config Service first and then, once loaded, starting the client. Visit the client app in the browser, `http://localhost:8080/message`. There, you should see the String `Hello world` reflected in the response.

Change the `message` key in the `a-bootiful-client.properties` file in the Git repository to something different (`Hello Spring!`, perhaps?). You can confirm that the Config Server sees the change by visiting `http://localhost:8888/a-bootiful-client/default`. You need to invoke the `refresh` Spring Boot Actuator endpoint in order to force the client to refresh itself and draw the new value in. Spring 's Actuator exposes operational endpoints, like health checks and environment information, about an application. In order to use it you must add `org.springframework.boot:spring-boot-starter-actuator` to the `configuration-client`'s CLASSPATH. You can invoke the `refresh` Actuator endpoint by sending an empty HTTP `POST` to the client's `refresh` endpoint, `http://localhost:8080/refresh`, and then confirm it worked by reviewing the `http://localhost:8080/message` endpoint.
Change the `message` key in the `a-bootiful-client.properties` file in the Git repository to something different (`Hello Spring!`, perhaps?). You can confirm that the Config Server sees the change by visiting `http://localhost:8888/a-bootiful-client/default`. You need to invoke the `refresh` Spring Boot Actuator endpoint in order to force the client to refresh itself and draw the new value in. Spring 's Actuator exposes operational endpoints, like health checks and environment information, about an application. In order to use it you must add `org.springframework.boot:spring-boot-starter-actuator` to the client app's CLASSPATH. You can invoke the `refresh` Actuator endpoint by sending an empty HTTP `POST` to the client's `refresh` endpoint, `http://localhost:8080/refresh`, and then confirm it worked by reviewing the `http://localhost:8080/message` endpoint.


== Summary
Expand Down
6 changes: 3 additions & 3 deletions complete/configuration-client/pom.xml
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.8.RELEASE</version>
<version>1.4.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

Expand Down Expand Up @@ -44,8 +44,8 @@
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Angel.SR4</version>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
Expand Up @@ -19,7 +19,7 @@ public static void main(String[] args) {
@RestController
class MessageRestController {

@Value("${message}")
@Value("${message:Hello default}")
private String message;

@RequestMapping("/message")
Expand Down
@@ -1,2 +1,3 @@
spring.application.name=a-bootiful-client
# N.B. this is the default:
spring.cloud.config.uri=http://localhost:8888
@@ -0,0 +1,52 @@
/*
* Copyright 2012-2015 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
*
* http://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 hello;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ConfigClientApplicationTest {

@Autowired
private ConfigurableEnvironment environment;

@Autowired
private MessageRestController controller;

@Autowired
private ContextRefresher refresher;

@Test
public void contextLoads() {
assertThat(controller.getMessage()).isNotEqualTo("Hello test");
EnvironmentTestUtils.addEnvironment(environment, "message:Hello test");
assertThat(controller.getMessage()).isNotEqualTo("Hello test");
refresher.refresh();
assertThat(controller.getMessage()).isEqualTo("Hello test");
}

}
106 changes: 53 additions & 53 deletions complete/configuration-service/pom.xml
@@ -1,58 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>configuration-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Angel.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>configuration-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>
@@ -0,0 +1,40 @@
/*
* Copyright 2012-2015 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
*
* http://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 hello;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.config.server.environment.EnvironmentController;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(properties = { "spring.profiles.active=native" })
public class ConfigServiceApplicationTest {

@Autowired
private EnvironmentController controller;

@Test
public void contextLoads() {
assertThat(controller).isNotNull();
}

}

0 comments on commit 778e175

Please sign in to comment.