├── .gitignore ├── LICENSE ├── NOTICE ├── README.adoc ├── labs ├── README.adoc ├── lab01 │ └── lab01.adoc ├── lab02 │ ├── import.sql │ └── lab02.adoc ├── lab03 │ ├── images │ │ ├── appsman.jpg │ │ └── logging.jpg │ └── lab03.adoc ├── lab04 │ ├── images │ │ ├── config-scs.jpg │ │ ├── config-scs1.jpg │ │ ├── config-scs2.jpg │ │ └── config-scs3.jpg │ └── lab04.adoc ├── lab05 │ ├── images │ │ ├── registry1.jpg │ │ ├── registry2.jpg │ │ └── ui.jpg │ └── lab05.adoc ├── lab06 │ ├── images │ │ ├── dash.jpg │ │ ├── dash1.jpg │ │ └── empty.jpg │ └── lab06.adoc └── labaccess.adoc └── presentations ├── Intro_CF_at_TM.pptx ├── README.adoc ├── Session_1_CN_Design_DDD.pptx ├── Session_2_Intro_Boot.pptx ├── Session_3_Polyglot_Persist.pptx ├── Session_4_Advanced_Boot.pptx ├── Session_5_Intro_SC.pptx ├── Session_6_SC_Config.pptx ├── Session_7_SC_Discovery_LB.pptx └── Session_8_Circuit_Breaker.pptx /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.iml 3 | .idea 4 | .DS_Store 5 | 6 | # Mobile Tools for Java (J2ME) 7 | .mtj.tmp/ 8 | 9 | # Package Files # 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are 3 | met: 4 | 5 | 1. Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided 11 | with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 VMware, Inc. 2 | 3 | This product is licensed to you under the BSD 2 clause (the "License"). You may not use this product except in compliance with the License. 4 | 5 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Pivotal Cloud Native Applications Workshop 2 | == Overview 3 | [.lead] 4 | This one day hands-on classroom style session will provide developers with hands on experience building cloud native applications using micro service architectures, Spring Boot, and Spring Cloud. The session includes presentations, demos and hands on labs. 5 | 6 | == Schedule 7 | 8 | 8:00 AM - 9:00 AM:: 9 | * Registration and Breakfast 10 | 9:00 AM - 9:30 AM:: 11 | * Welcome and Agenda 12 | * Intro: link:presentations/Intro_CF_at_TM.pptx[_Pivotal Cloud Foundry at T-Mobile_] 13 | 9:30 AM - 10:30 AM:: 14 | * Session 1: link:presentations/Session_1_CN_Design_DDD.pptx[_Cloud Native Design, Domain Driven Design, & Microservices_] 15 | 10:30 AM - 10:45 AM:: Break 16 | 10:45 AM - 12:00 PM:: 17 | * Session 2: link:presentations/Session_2_Intro_Boot.pptx[_Introducing Spring Boot_] 18 | * Lab 1: link:labs/lab01/lab01.adoc[Building A Spring Boot Application] 19 | * Session 3: link:presentations/Session_3_Polyglot_Persist.pptx[_Polyglot Persistence with Spring Data REST_] 20 | * Lab 2: link:labs/lab02/lab02.adoc[Build a Hypermedia-Driven RESTful Web Service with Spring Data REST] 21 | 12:00 PM - 12:45 PM:: Lunch 22 | 12:45 PM - 1:30 PM:: 23 | * Session 4: link:presentations/Session_4_Advanced_Boot.pptx[_Advancing Spring Boot with Actuator and Profiles_] 24 | * Lab 3: link:labs/lab03/lab03.adoc[Enhancing Boot Application with Metrics] 25 | 1:30 PM - 2:45 PM:: 26 | * Session 5: link:presentations/Session_5_Intro_SC.pptx[_Introducing Spring Cloud Netflix_] 27 | * Session 6: link:presentations/Session_6_SC_Config.pptx[_Spring Cloud Config_] 28 | * Lab 4: link:labs/lab04/lab04.adoc[Introducing Spring Cloud into Boot Application] 29 | 2:45 PM - 3:00 PM:: Break 30 | 3:00 PM - 3:45 PM:: 31 | * Session 7: link:presentations/Session_7_SC_Discovery_LB.pptx[_Spring Cloud Netflix - Service Discovery & Load Balancing_] 32 | * Lab 5: link:labs/lab05/lab05.adoc[Microservice Service Discover and Load Balancing] 33 | 3:45 PM - 4:30 PM:: 34 | * Session 8: link:presentations/Session_8_Circuit_Breaker.pptx[_Spring Cloud Netflix - Circuit Breakers_] 35 | * Lab 6: link:labs/lab06/lab06.adoc[Microservice Fault Tolerance with Circuit Breakers] 36 | 4:30 PM:: Wrap-Up and Closing 37 | -------------------------------------------------------------------------------- /labs/README.adoc: -------------------------------------------------------------------------------- 1 | = Labs Content 2 | -------------------------------------------------------------------------------- /labs/lab01/lab01.adoc: -------------------------------------------------------------------------------- 1 | = Building a Spring Boot Application 2 | 3 | == Create a Spring Boot Project 4 | 5 | . Browse to https://start.spring.io 6 | 7 | . Generate a Maven Project with Spring Boot 1.5.2. 8 | 9 | . Fill out the *Project metadata* fields as follows: 10 | + 11 | Group:: +io.pivotal+ 12 | Artifact:: +cloud-native-spring+ 13 | 14 | . In the dependencies section, add the following manually: 15 | + 16 | *Web* *Rest Repositories* *JPA* *H2* *Actuator* 17 | 18 | . Click the _Generate Project_ button. Your browser will download a zip file. 19 | 20 | . Copy then unpack the downloaded zip file to *CN-Workshop-TM/labs/lab01/cloud-native-spring* 21 | + 22 | Your directory structure should now look like: 23 | + 24 | [source, bash] 25 | --------------------------------------------------------------------- 26 | CN-Workshop-TM: 27 | ├── labs 28 | │   ├── lab01 29 | │   │   ├── cloud-native-spring 30 | --------------------------------------------------------------------- 31 | 32 | . Import the project’s pom.xml into your editor/IDE of choice. 33 | + 34 | *_STS Import Help:_* 35 | + 36 | Select File > Import… Then select Maven > Existing Maven Projects. On the Import Maven Projects page, browse to the /cloud-native-spring directory (e.g. CN-Workshop-TM/labs/lab01/cloud-native-spring) 37 | 38 | == Add an Endpoint 39 | 40 | . Add an @RestController annotation to the class _io.pivotal.CloudNativeSpringApplication_ (/cloud-native-spring/src/main/java/io/pivotal/CloudNativeSpringApplication.java). 41 | + 42 | [source, java, numbered] 43 | --------------------------------------------------------------------- 44 | package io.pivotal; 45 | 46 | import org.springframework.boot.SpringApplication; 47 | import org.springframework.boot.autoconfigure.SpringBootApplication; 48 | import org.springframework.web.bind.annotation.RestController; 49 | 50 | @SpringBootApplication 51 | @RestController 52 | public class CloudNativeSpringApplication { 53 | 54 | public static void main(String[] args) { 55 | SpringApplication.run(CloudNativeSpringApplication.class, args); 56 | } 57 | } 58 | --------------------------------------------------------------------- 59 | 60 | . Add the following request handler to the class _io.pivotal.CloudNativeSpringApplication (/cloud-native-spring/src/main/java/io/pivotal/CloudNativeSpringApplication.java). 61 | + 62 | [source,java] 63 | --------------------------------------------------------------------- 64 | @RequestMapping("/") 65 | public String hello() { 66 | return "Hello World!"; 67 | } 68 | --------------------------------------------------------------------- 69 | + 70 | Completed: 71 | + 72 | [source,java] 73 | --------------------------------------------------------------------- 74 | package io.pivotal; 75 | 76 | import org.springframework.boot.SpringApplication; 77 | import org.springframework.boot.autoconfigure.SpringBootApplication; 78 | import org.springframework.web.bind.annotation.RequestMapping; 79 | import org.springframework.web.bind.annotation.RestController; 80 | 81 | @SpringBootApplication 82 | @RestController 83 | public class CloudNativeSpringApplication { 84 | 85 | public static void main(String[] args) { 86 | SpringApplication.run(CloudNativeSpringApplication.class, args); 87 | } 88 | 89 | @RequestMapping("/") 90 | public String hello() { 91 | return "Hello World!"; 92 | } 93 | } 94 | --------------------------------------------------------------------- 95 | 96 | == Run the _cloud-native-spring_ Application 97 | 98 | . In a terminal, change working directory to *CN-Workshop-TM/labs/lab01/cloud-native-spring* 99 | + 100 | $ cd /CN-Workshop-TM/labs/lab01/cloud-native-spring 101 | 102 | . Run the application 103 | + 104 | $ mvn clean spring-boot:run 105 | 106 | . You should see the application start up an embedded Apache Tomcat server on port 8080 (review terminal output): 107 | + 108 | [source,bash] 109 | --------------------------------------------------------------------- 110 | 2015-10-02 13:26:59.264 INFO 44749 --- [lication.main()] s.b.c.e.t.TomcatEmbeddedServletContainer: Tomcat started on port(s): 8080 (http) 111 | 2015-10-02 13:26:59.267 INFO 44749 --- [lication.main()] io.pivotal.hello.CloudNativeSpringApplication: Started CloudNativeSpringApplication in 2.541 seconds (JVM running for 9.141) 112 | --------------------------------------------------------------------- 113 | 114 | . Browse to http://localhost:8080 115 | 116 | . Stop the _cloud-native-spring_ application. In the terminal window: *Ctrl + C* 117 | 118 | == Deploy _cloud-native-spring_ to Pivotal Cloud Foundry 119 | 120 | . Build the application 121 | + 122 | [source,bash] 123 | --------------------------------------------------------------------- 124 | $ mvn clean package 125 | --------------------------------------------------------------------- 126 | 127 | . Create an application manifest in the root folder /cloud-native-spring 128 | + 129 | $ touch manifest.yml 130 | 131 | . Add application metadata, using a text editor (of choice) 132 | + 133 | [source, bash] 134 | --------------------------------------------------------------------- 135 | --- 136 | applications: 137 | - name: cloud-native-spring 138 | host: cloud-native-spring-${random-word} 139 | memory: 1G 140 | instances: 1 141 | path: ./target/cloud-native-spring-0.0.1-SNAPSHOT.jar 142 | buildpack: java_buildpack_offline 143 | env: 144 | JAVA_OPTS: -Djava.security.egd=file:///dev/urandom 145 | --------------------------------------------------------------------- 146 | 147 | . Push application into Cloud Foundry 148 | + 149 | $ cf push -f manifest.yml 150 | 151 | . Find the URL created for your app in the health status report. Browse to your app. 152 | 153 | *Congratulations!* You’ve just completed your first Spring Boot application. 154 | -------------------------------------------------------------------------------- /labs/lab02/lab02.adoc: -------------------------------------------------------------------------------- 1 | :compat-mode: 2 | = Adding Persistence to Boot Application 3 | 4 | In this lab we'll utilize Spring Boot, Spring Data, and Spring Data REST to create a fully-functional hypermedia-driven RESTful web service. We'll then deploy it to Pivotal Cloud Foundry. 5 | 6 | == Create a Hypermedia-Driven RESTful Web Service with Spring Data REST (using JPA) 7 | 8 | This application will create a simple reading list by asking for books you have read and storing them in a simple relational repository. We'll continue building upon the Spring Boot application we build in Lab 1. The first stereotype we will need is the domain model itself, which is `City`. 9 | 10 | == Add the domain object - City 11 | . Create the package `io.pivotal.domain` and in that package create the class `City`. Into that file you can paste the following source code, which represents cities based on postal codes, global coordinates, etc: 12 | + 13 | [source, java, numbered] 14 | --------------------------------------------------------------------- 15 | package io.pivotal.domain; 16 | 17 | @Entity 18 | @Table(name="city") 19 | public class City implements Serializable { 20 | private static final long serialVersionUID = 1L; 21 | 22 | @Id 23 | @GeneratedValue 24 | private long id; 25 | 26 | @Column(nullable = false) 27 | private String name; 28 | 29 | @Column(nullable = false) 30 | private String county; 31 | 32 | @Column(nullable = false) 33 | private String stateCode; 34 | 35 | @Column(nullable = false) 36 | private String postalCode; 37 | 38 | @Column 39 | private String latitude; 40 | 41 | @Column 42 | private String longitude; 43 | 44 | public String getName() { return name; } 45 | 46 | public void setName(String name) { this.name = name; } 47 | 48 | public String getPostalCode() { return postalCode; } 49 | 50 | public void setPostalCode(String postalCode) { this.postalCode = postalCode; } 51 | 52 | public long getId() { return id; } 53 | 54 | public void setId(long id) { this.id = id; } 55 | 56 | public String getStateCode() { return stateCode; } 57 | 58 | public void setStateCode(String stateCode) { this.stateCode = stateCode; } 59 | 60 | public String getCounty() { return county; } 61 | 62 | public void setCounty(String county) { this.county = county; } 63 | 64 | public String getLatitude() { return latitude; } 65 | 66 | public void setLatitude(String latitude) { this.latitude = latitude; } 67 | 68 | public String getLongitude() { return longitude; } 69 | 70 | public void setLongitude(String longitude) { this.longitude = longitude; } 71 | } 72 | 73 | --------------------------------------------------------------------- 74 | + 75 | Notice that we're using JPA annotations on the class and its fields. You'll need to use your IDE's features to add the appropriate import statements. 76 | 77 | . Create the package +io.pivotal.repositories+ and in that package create the interface +CityRepository+. Paste the following code and add appropriate imports: 78 | + 79 | [source,java] 80 | --------------------------------------------------------------------- 81 | package io.pivotal.repositories; 82 | 83 | @RepositoryRestResource(collectionResourceRel = "cities", path = "cities") 84 | public interface CityRepository extends PagingAndSortingRepository { 85 | } 86 | --------------------------------------------------------------------- 87 | 88 | You’ll need to use your IDE’s features to add the appropriate import statements. 89 | 90 | . Add JPA and REST Repository support to the +io.pivotal.CloudNativeSpringApplication+ Spring Boot Application class. 91 | + 92 | [source,java] 93 | --------------------------------------------------------------------- 94 | @SpringBootApplication 95 | @RestController 96 | @EnableJpaRepositories // <---- Add this 97 | @Import(RepositoryRestMvcConfiguration.class) // <---- And this 98 | public class CloudNativeSpringApplication { 99 | 100 | public static void main(String[] args) { 101 | SpringApplication.run(CloudNativeSpringApplication.class, args); 102 | } 103 | } 104 | --------------------------------------------------------------------- 105 | 106 | == Run the _cloud-native-spring_ Application 107 | 108 | . Run the application 109 | + 110 | [source,bash] 111 | --------------------------------------------------------------------- 112 | $ mvn clean spring-boot:run 113 | --------------------------------------------------------------------- 114 | 115 | . Access the application using +curl+ or your web browser using the newly added REST repository endpoint at http://localhost:8080/cities. You'll see that the primary endpoint automatically exposes the ability to page, size, and sort the response JSON. 116 | + 117 | [source,bash] 118 | --------------------------------------------------------------------- 119 | $ curl -i http://localhost:8080/cities 120 | HTTP/1.1 200 OK 121 | Server: Apache-Coyote/1.1 122 | Content-Type: application/hal+json;charset=UTF-8 123 | Transfer-Encoding: chunked 124 | Date: Thu, 28 Apr 2016 14:44:06 GMT 125 | 126 | { 127 | "_embedded" : { 128 | "cities" : [ ] 129 | }, 130 | "_links" : { 131 | "self" : { 132 | "href" : "http://localhost:8080/cities" 133 | }, 134 | "profile" : { 135 | "href" : "http://localhost:8080/profile/cities" 136 | } 137 | }, 138 | "page" : { 139 | "size" : 20, 140 | "totalElements" : 0, 141 | "totalPages" : 0, 142 | "number" : 0 143 | } 144 | } 145 | --------------------------------------------------------------------- 146 | 147 | So what have you done? Created four small classes and one build file, resulting in a fully-functional REST microservice. The application's +DataSource+ is created automatically by Spring Boot using the in-memory database because no other +DataSource+ was detected in the project. 148 | 149 | Next we'll import some data. 150 | 151 | == Importing Data 152 | 153 | . Add this https://raw.githubusercontent.com/Pivotal-Field-Engineering/CN-Workshop-TM/master/labs/lab02/import.sql[import.sql file] found in *CN-Workshop-TM/labs/lab02/* to +src/main/resources+. This is a rather large dataset containing all of the postal codes in the United States and its territories. This file will automatically be picked up by Hibernate and imported into the in-memory database. 154 | 155 | . Restart the application. 156 | + 157 | [source,bash] 158 | --------------------------------------------------------------------- 159 | $ mvn clean spring-boot:run 160 | --------------------------------------------------------------------- 161 | 162 | . Access the application again. Notice the appropriate hypermedia is included for +next+, +previous+, and +self+. You can also select pages and page size by utilizing +?size=n&page=n+ on the URL string. Finally, you can sort the data utilizing +?sort=fieldName+ (replace fieldName with a cities attribute). 163 | + 164 | [source,bash] 165 | --------------------------------------------------------------------- 166 | $ curl -i localhost:8080/cities 167 | HTTP/1.1 200 OK 168 | Server: Apache-Coyote/1.1 169 | X-Application-Context: application 170 | Content-Type: application/hal+json 171 | Transfer-Encoding: chunked 172 | Date: Tue, 27 May 2014 19:59:58 GMT 173 | 174 | { 175 | "_links" : { 176 | "next" : { 177 | "href" : "http://localhost:8080/cities?page=1&size=20" 178 | }, 179 | "self" : { 180 | "href" : "http://localhost:8080/cities{?page,size,sort}", 181 | "templated" : true 182 | } 183 | }, 184 | "_embedded" : { 185 | "cities" : [ { 186 | "name" : "HOLTSVILLE", 187 | "county" : "SUFFOLK", 188 | "stateCode" : "NY", 189 | "postalCode" : "00501", 190 | "latitude" : "+40.922326", 191 | "longitude" : "-072.637078", 192 | "_links" : { 193 | "self" : { 194 | "href" : "http://localhost:8080/cities/1" 195 | } 196 | } 197 | }, 198 | 199 | // ... 200 | 201 | { 202 | "name" : "CASTANER", 203 | "county" : "LARES", 204 | "stateCode" : "PR", 205 | "postalCode" : "00631", 206 | "latitude" : "+18.269187", 207 | "longitude" : "-066.864993", 208 | "_links" : { 209 | "self" : { 210 | "href" : "http://localhost:8080/cities/20" 211 | } 212 | } 213 | } ] 214 | }, 215 | "page" : { 216 | "size" : 20, 217 | "totalElements" : 42741, 218 | "totalPages" : 2138, 219 | "number" : 0 220 | } 221 | } 222 | --------------------------------------------------------------------- 223 | 224 | . Try the following URL Paths in your browser or +curl+ to see how the application behaves: 225 | + 226 | http://localhost:8080/cities?size=5 227 | + 228 | http://localhost:8080/cities?size=5&page=3 229 | + 230 | http://localhost:8080/cities?sort=postalCode,desc 231 | 232 | Next we'll add searching capabilities. 233 | 234 | == Adding Search 235 | 236 | . Let's add some additional finder methods to +CityRepository+: 237 | + 238 | [source,java] 239 | --------------------------------------------------------------------- 240 | @RestResource(path = "name", rel = "name") 241 | Page findByNameIgnoreCase(@Param("q") String name, Pageable pageable); 242 | 243 | @RestResource(path = "nameContains", rel = "nameContains") 244 | Page findByNameContainsIgnoreCase(@Param("q") String name, Pageable pageable); 245 | 246 | @RestResource(path = "state", rel = "state") 247 | Page findByStateCodeIgnoreCase(@Param("q") String stateCode, Pageable pageable); 248 | 249 | @RestResource(path = "postalCode", rel = "postalCode") 250 | Page findByPostalCode(@Param("q") String postalCode, Pageable pageable); 251 | --------------------------------------------------------------------- 252 | 253 | . Run the application 254 | + 255 | [source,bash] 256 | --------------------------------------------------------------------- 257 | $ mvn clean spring-boot:run 258 | --------------------------------------------------------------------- 259 | 260 | . Access the application again. Notice that hypermedia for a new +search+ endpoint has appeared. 261 | + 262 | [source,bash] 263 | --------------------------------------------------------------------- 264 | $ curl -i "localhost:8080/cities" 265 | HTTP/1.1 200 OK 266 | Server: Apache-Coyote/1.1 267 | X-Application-Context: application 268 | Content-Type: application/hal+json 269 | Transfer-Encoding: chunked 270 | Date: Tue, 27 May 2014 20:33:52 GMT 271 | 272 | { 273 | "_links" : { 274 | "next" : { 275 | "href" : "http://localhost:8080/cities?page=1&size=20" 276 | }, 277 | "self" : { 278 | "href" : "http://localhost:8080/cities{?page,size,sort}", 279 | "templated" : true 280 | }, 281 | "search" : { 282 | "href" : "http://localhost:8080/cities/search" 283 | } 284 | }, 285 | // (Remainder omitted...) 286 | --------------------------------------------------------------------- 287 | 288 | . Access the new +search+ endpoint: 289 | + 290 | http://localhost:8080/cities/search 291 | + 292 | [source,bash] 293 | --------------------------------------------------------------------- 294 | $ curl -i "localhost:8080/cities/search" 295 | HTTP/1.1 200 OK 296 | Server: Apache-Coyote/1.1 297 | X-Application-Context: application 298 | Content-Type: application/hal+json 299 | Transfer-Encoding: chunked 300 | Date: Tue, 27 May 2014 20:38:32 GMT 301 | 302 | { 303 | "_links" : { 304 | "postalCode" : { 305 | "href" : "http://localhost:8080/cities/search/postalCode{?q,page,size,sort}", 306 | "templated" : true 307 | }, 308 | "state" : { 309 | "href" : "http://localhost:8080/cities/search/state{?q,page,size,sort}", 310 | "templated" : true 311 | }, 312 | "name" : { 313 | "href" : "http://localhost:8080/cities/search/name{?q,page,size,sort}", 314 | "templated" : true 315 | }, 316 | "nameContains" : { 317 | "href" : "http://localhost:8080/cities/search/nameContains{?q,page,size,sort}", 318 | "templated" : true 319 | } 320 | } 321 | } 322 | --------------------------------------------------------------------- 323 | + 324 | Note that we now have new search endpoints for each of the finders that we added. 325 | 326 | . Try a few of these endpoints. Feel free to substitute your own values for the parameters. 327 | + 328 | http://localhost:8080/cities/search/postalCode?q=75202 329 | + 330 | http://localhost:8080/cities/search/name?q=Boston 331 | + 332 | http://localhost:8080/cities/search/nameContains?q=Fort&size=1 333 | 334 | == Pushing to Cloud Foundry 335 | 336 | . Build the application 337 | + 338 | [source,bash] 339 | --------------------------------------------------------------------- 340 | $ mvn clean package 341 | --------------------------------------------------------------------- 342 | 343 | . You should already have an application manifest, +manifest.yml+, created in lab 1; this can be reused. You'll want to add a timeout param so that our service has enough time to initialize with its data loading: 344 | + 345 | [source,yml] 346 | --------------------------------------------------------------------- 347 | --- 348 | applications: 349 | - name: cloud-native-spring 350 | host: cloud-native-spring 351 | memory: 512M 352 | instances: 1 353 | path: ./target/cloud-native-spring-0.0.1-SNAPSHOT.jar 354 | buildpack: java_buildpack_offline 355 | timeout: 180 # to give time for the data to import 356 | env: 357 | JAVA_OPTS: -Djava.security.egd=file:///dev/urandom 358 | --------------------------------------------------------------------- 359 | 360 | . Push to Cloud Foundry: 361 | + 362 | [source,bash] 363 | --------------------------------------------------------------------- 364 | $ cf push 365 | 366 | ... 367 | 368 | 1 of 1 instances running 369 | 370 | App started 371 | 372 | Showing health and status for app cloud-native-spring... 373 | OK 374 | 375 | requested state: started 376 | instances: 1/1 377 | usage: 512M x 1 instances 378 | urls: cloud-native-spring.cf.mycloud.com 379 | 380 | state since cpu memory disk 381 | #0 running 2014-05-27 04:15:05 PM 0.0% 433M of 512M 128.9M of 1G 382 | --------------------------------------------------------------------- 383 | 384 | . Access the application at the random route provided by CF: 385 | + 386 | [source,bash] 387 | --------------------------------------------------------------------- 388 | $ curl -i cloud-native-spring.cf.mycloud.com/cities 389 | --------------------------------------------------------------------- 390 | -------------------------------------------------------------------------------- /labs/lab03/images/appsman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab03/images/appsman.jpg -------------------------------------------------------------------------------- /labs/lab03/images/logging.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab03/images/logging.jpg -------------------------------------------------------------------------------- /labs/lab03/lab03.adoc: -------------------------------------------------------------------------------- 1 | = Enhancing Boot Application with Metrics 2 | 3 | == Set up the Actuator 4 | 5 | Spring Boot includes a number of additional features to help you monitor and manage your application when it’s pushed to production. These features are added by adding _spring-boot-starter-actuator_ to the classpath. During our initial project setup with *start.spring.io* we've already included that. 6 | 7 | . Verify the Spring Boot Actuator dependency the following file: */cloud-native-spring/pom.xml*. You should see the following dependency in the list: 8 | + 9 | [source, xml] 10 | --------------------------------------------------------------------- 11 | 12 | org.springframework.boot 13 | spring-boot-starter-actuator 14 | 15 | --------------------------------------------------------------------- 16 | 17 | . By default Spring Boot will use Spring Security to protect these management endpoints (which is a good thing!). Though you wouldn't want to disable this in production, we'll do so in this sample app to make demonstration a bit easier and simpler. . Add the following properties to *cloud-native-spring/src/main/resources/application.yml*. You must create the file first. 18 | + 19 | [source, yaml] 20 | --------------------------------------------------------------------- 21 | endpoints: # add this section 22 | sensitive: false 23 | --------------------------------------------------------------------- 24 | 25 | . Run the updated application 26 | + 27 | [source,bash] 28 | --------------------------------------------------------------------- 29 | $ mvn clean spring-boot:run 30 | --------------------------------------------------------------------- 31 | + 32 | Try out the following endpoints. The output is omitted here because it can be quite large: 33 | + 34 | http://localhost:8080/health 35 | + 36 | Displays Application and Datasource health information. This can be customized based on application functionality, which we'll do later. 37 | + 38 | http://localhost:8080/beans 39 | + 40 | Dumps all of the beans in the Spring context. 41 | + 42 | http://localhost:8080/autoconfig 43 | + 44 | Dumps all of the auto-configuration performed as part of application bootstrapping. 45 | + 46 | http://localhost:8080/configprops 47 | + 48 | Displays a collated list of all @ConfigurationProperties. 49 | + 50 | http://localhost:8080/env 51 | + 52 | Dumps the application’s shell environment as well as all Java system properties. 53 | + 54 | http://localhost:8080/mappings 55 | + 56 | Dumps all URI request mappings and the controller methods to which they are mapped. 57 | + 58 | http://localhost:8080/dump 59 | + 60 | Performs a thread dump. 61 | + 62 | http://localhost:8080/trace 63 | + 64 | Displays trace information (by default the last few HTTP requests). 65 | 66 | . Stop the cloud-native-spring application. 67 | 68 | == Include Version Control Info 69 | 70 | Spring Boot provides an endpoint (http://localhost:8080/info) that allows the exposure of arbitrary metadata. By default, it is empty. 71 | 72 | One thing that _actuator_ does well is expose information about the specific build and version control coordinates for a given deployment. 73 | 74 | . Edit the following file: */cloud-native-spring/pom.xml*. Add the _git-commit-id-plugin_ to your Maven build. You must edit the file and add the plugin code below into the existing __ XML structure, which should already have one plugin defined. The _git-commit-id-plugin_ adds Git branch and commit coordinates to the */info* endpoint: 75 | + 76 | [source, xml] 77 | --------------------------------------------------------------------- 78 | 79 | pl.project13.maven 80 | git-commit-id-plugin 81 | 82 | ../../../.git 83 | 84 | 85 | --------------------------------------------------------------------- 86 | + 87 | *NOTE* The path *../../../.git* refers to the .git directory at the root of the lab materials repo. 88 | + 89 | Completed: 90 | + 91 | [source, xml] 92 | --------------------------------------------------------------------- 93 | 94 | 96 | 4.0.0 97 | 98 | io.pivotal 99 | cloud-native-spring 100 | 0.0.1-SNAPSHOT 101 | jar 102 | 103 | cloud-native-spring 104 | Demo project for Spring Boot 105 | 106 | 107 | org.springframework.boot 108 | spring-boot-starter-parent 109 | 1.2.8.RELEASE 110 | 111 | 112 | 113 | 114 | UTF-8 115 | 1.8 116 | 117 | 118 | 119 | 120 | org.springframework.boot 121 | spring-boot-starter-actuator 122 | 123 | 124 | org.springframework.boot 125 | spring-boot-starter-data-jpa 126 | 127 | 128 | org.springframework.boot 129 | spring-boot-starter-data-rest 130 | 131 | 132 | org.springframework.boot 133 | spring-boot-starter-web 134 | 135 | 136 | 137 | com.h2database 138 | h2 139 | runtime 140 | 141 | 142 | mysql 143 | mysql-connector-java 144 | runtime 145 | 146 | 147 | org.springframework.boot 148 | spring-boot-starter-test 149 | test 150 | 151 | 152 | 153 | 154 | 155 | 156 | org.springframework.boot 157 | spring-boot-maven-plugin 158 | 159 | 160 | pl.project13.maven 161 | git-commit-id-plugin 162 | 163 | ../../../.git 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | --------------------------------------------------------------------- 172 | 173 | . Run the _cloud-native-spring_ application: 174 | + 175 | $ mvn clean spring-boot:run 176 | 177 | . Browse to http://localhost:8080/info. Git commit information is now included 178 | + 179 | [source,json] 180 | --------------------------------------------------------------------- 181 | { 182 | "git":{ 183 | "branch": "master", 184 | "commit": { 185 | "id": "123456", 186 | "time": "2016-03-03T12:05:10-0500" 187 | } 188 | } 189 | } 190 | --------------------------------------------------------------------- 191 | 192 | . Stop the _cloud-native-spring_ application 193 | + 194 | *What Just Happened?* 195 | + 196 | By including the _git-commit-id-plugin_, details about git commit information will be included in the */info* endpoint. Git information is captured in a _git.properties_ file that is generated with the build. Review the following file: */cloud-native-spring/target/classes/git.properties* 197 | 198 | == Include Build Info 199 | 200 | . Add the following properties to *cloud-native-spring/src/main/resources/application.yml*. You must create the file first. 201 | + 202 | [source, yaml] 203 | --------------------------------------------------------------------- 204 | info: # add this section 205 | build: 206 | artifact: @project.artifactId@ 207 | name: @project.name@ 208 | description: @project.description@ 209 | version: @project.version@ 210 | --------------------------------------------------------------------- 211 | + 212 | These will add the project’s Maven coordinates to the /info endpoint. The Spring Boot Maven plugin will cause them to automatically be replaced in the assembled JAR. 213 | + 214 | *NOTE:* if STS reports a problem with the application.yml due to @ character the problem can safely be ignored. 215 | 216 | . Build and run the cloud-native-spring application: 217 | + 218 | [source,bash] 219 | --------------------------------------------------------------------- 220 | $ mvn clean spring-boot:run 221 | --------------------------------------------------------------------- 222 | 223 | . Browse to http://localhost:8080/info. Build information is now included. 224 | + 225 | [source,json] 226 | --------------------------------------------------------------------- 227 | { 228 | "build": { 229 | "artifact": "cloud-native-spring", 230 | "name": "cloud-native-spring", 231 | "description": "Demo project for Spring Boot", 232 | "version": "0.0.1-SNAPSHOT" 233 | }, 234 | "git":{ 235 | "branch": "master", 236 | "commit": { 237 | "id": "123456", 238 | "time": "2016-03-03T12:05:10-0500" 239 | } 240 | } 241 | } 242 | --------------------------------------------------------------------- 243 | 244 | . Stop the cloud-native-spring application. 245 | + 246 | *What Just Happened?* 247 | + 248 | We have mapped Maven properties from the pom.xml into the /info endpoint. 249 | + 250 | Read more about exposing data in the /info endpoint link:http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready[here] 251 | 252 | == Health Indicators 253 | 254 | Spring Boot provides an endpoint http://localhost:8080/health that exposes various health indicators that describe the health of the given application. 255 | 256 | Normally, when Spring Security is not enabled, the /health endpoint will only expose an UP or DOWN value. 257 | 258 | [source,json] 259 | --------------------------------------------------------------------- 260 | { 261 | "status": "UP" 262 | } 263 | --------------------------------------------------------------------- 264 | 265 | . To simplify working with the endpoint for this lab, we will turn off additional security for the health endpoint. Add the following to */cloud-native-spring/src/main/resources/application.yml*: 266 | + 267 | [source, yaml] 268 | --------------------------------------------------------------------- 269 | management: 270 | security: 271 | enabled: false 272 | --------------------------------------------------------------------- 273 | 274 | . Build and run the cloud-native-spring application: 275 | + 276 | [source,bash] 277 | --------------------------------------------------------------------- 278 | $ mvn clean spring-boot:run 279 | --------------------------------------------------------------------- 280 | 281 | . Browse to http://localhost:8080/health. Out of the box is a _DiskSpaceHealthIndicator_ that monitors health in terms of available disk space. Would your Ops team like to know if the app is close to running out of disk space? DiskSpaceHealthIndicator can be customized via _DiskSpaceHealthIndicatorProperties_. For instance, setting a different threshold for when to report the status as DOWN. 282 | + 283 | [source,json] 284 | --------------------------------------------------------------------- 285 | { 286 | "status": "UP", 287 | "diskSpace": { 288 | "status": "UP", 289 | "free": 42345678945, 290 | "threshold": 12345678 291 | } 292 | } 293 | --------------------------------------------------------------------- 294 | 295 | . Stop the cloud-native-spring application. 296 | 297 | . Create the class _io.pivotal.FlappingHealthIndicator_ (/cloud-native-spring/src/main/java/io/pivotal/FlappingHealthIndicator.java) and into it paste the following code: 298 | + 299 | [source,java] 300 | --------------------------------------------------------------------- 301 | package io.pivotal; 302 | 303 | import java.util.Random; 304 | 305 | import org.springframework.boot.actuate.health.Health; 306 | import org.springframework.boot.actuate.health.HealthIndicator; 307 | import org.springframework.stereotype.Component; 308 | 309 | @Component 310 | public class FlappingHealthIndicator implements HealthIndicator { 311 | 312 | private Random random = new Random(System.currentTimeMillis()); 313 | 314 | @Override 315 | public Health health() { 316 | int result = random.nextInt(100); 317 | if (result < 50) { 318 | return Health.down().withDetail("flapper", "failure").withDetail("random", result).build(); 319 | } else { 320 | return Health.up().withDetail("flapper", "ok").withDetail("random", result).build(); 321 | } 322 | } 323 | } 324 | --------------------------------------------------------------------- 325 | + 326 | This demo health indicator will randomize the health check. 327 | 328 | . Build and run the _cloud-native-spring_ application: 329 | + 330 | [source,bash] 331 | --------------------------------------------------------------------- 332 | $ mvn clean spring-boot:run 333 | --------------------------------------------------------------------- 334 | 335 | . Browse to http://localhost:8080/health and verify that the output is similar to the following (and changes randomly!). 336 | + 337 | [source,json] 338 | --------------------------------------------------------------------- 339 | { 340 | "status": "UP", 341 | "flapping": { 342 | "status": "UP", 343 | "flapper": "ok", 344 | "random": 42 345 | }, 346 | "diskSpace": { 347 | "status": "UP", 348 | "free": 42345678945, 349 | "threshold": 12345678 350 | } 351 | } 352 | --------------------------------------------------------------------- 353 | 354 | == Metrics 355 | 356 | Spring Boot provides an endpoint http://localhost:8080/metrics that exposes several automatically collected metrics for your application. It also allows for the creation of custom metrics. 357 | 358 | . Browse to http://localhost:8080/metrics. Review the metrics exposed. 359 | + 360 | [source,json] 361 | --------------------------------------------------------------------- 362 | { 363 | "mem": 418830, 364 | "mem.free": 239376, 365 | "processors": 8, 366 | "instance.uptime": 59563, 367 | "uptime": 69462, 368 | "systemload.average": 1.5703125, 369 | "heap.committed": 341504, 370 | "heap.init": 262144, 371 | "heap.used": 102127, 372 | "heap": 3728384, 373 | "nonheap.committed": 79696, 374 | "nonheap.init": 2496, 375 | "nonheap.used": 77326, 376 | "nonheap": 0, 377 | "threads.peak": 14, 378 | "threads.daemon": 11, 379 | "threads.totalStarted": 17, 380 | "threads": 13, 381 | "classes": 9825, 382 | "classes.loaded": 9825, 383 | "classes.unloaded": 0, 384 | "gc.ps_scavenge.count": 9, 385 | "gc.ps_scavenge.time": 80, 386 | "gc.ps_marksweep.count": 2, 387 | "gc.ps_marksweep.time": 157, 388 | "httpsessions.max": -1, 389 | "httpsessions.active": 0, 390 | "gauge.response.metrics": 75, 391 | "gauge.response.star-star.favicon.ico": 9, 392 | "counter.status.200.star-star.favicon.ico": 1, 393 | "counter.status.200.metrics": 1 394 | } 395 | --------------------------------------------------------------------- 396 | 397 | . Stop the cloud-native-spring application. 398 | 399 | == Deploy _cloud-native-spring_ to Pivotal Cloud Foundry 400 | . Build the application 401 | + 402 | [source,bash] 403 | --------------------------------------------------------------------- 404 | $ mvn clean package 405 | --------------------------------------------------------------------- 406 | 407 | . When running a Spring Boot application on Pivotal Cloud Foundry with the actuator endpoints enabled, you can visualize actuator management information on the Applications Manager app dashboard. To enable this there are a few properties we need to add. Add the following to */cloud-native-spring/src/main/resources/application.yml*: 408 | + 409 | [source, yaml] 410 | --------------------------------------------------------------------- 411 | management: 412 | security: 413 | enabled: false 414 | info: 415 | git: 416 | mode: full 417 | cloudfoundry: 418 | enabled: true 419 | skip-ssl-validation: true 420 | --------------------------------------------------------------------- 421 | 422 | . In order to add full build information to you artifact that is pushed to cloudfoundry, update */cloud-native-spring/pom.xml* and add the following execution and classifier to the spring-boot-maven-plugin: 423 | + 424 | [source, xml] 425 | --------------------------------------------------------------------- 426 | 427 | 428 | 429 | build-info 430 | 431 | 432 | 433 | 434 | exec 435 | 436 | --------------------------------------------------------------------- 437 | + 438 | The full plugin config should look like the following: 439 | + 440 | [source, xml] 441 | --------------------------------------------------------------------- 442 | 443 | org.springframework.boot 444 | spring-boot-maven-plugin 445 | 446 | 447 | 448 | build-info 449 | 450 | 451 | 452 | 453 | exec 454 | 455 | 456 | --------------------------------------------------------------------- 457 | 458 | . By specifying a classifier we actually just produced 2 jars, one that is executable and one that can be used as an artifact that could be included in other apps (such as our Client UI app we'll create later). Because of this we need to chance the name of the jar we included in our manifest.yml file. Change the jar in the path property to *./target/cloud-native-spring-0.0.1-SNAPSHOT-exec.jar*: 459 | + 460 | [source, yaml] 461 | --------------------------------------------------------------------- 462 | --- 463 | applications: 464 | - name: cloud-native-spring 465 | host: cloud-native-spring 466 | memory: 512M 467 | instances: 1 468 | path: ./target/cloud-native-spring-0.0.1-SNAPSHOT-exec.jar 469 | buildpack: java_buildpack_offline 470 | timeout: 180 # to give time for the data to import 471 | env: 472 | JAVA_OPTS: -Djava.security.egd=file:///dev/urandom 473 | --------------------------------------------------------------------- 474 | . Push application into Cloud Foundry 475 | + 476 | $ cf push -f manifest.yml 477 | 478 | . Find the URL created for your app in the health status report. Browse to your app. Also view your application details in the Apps Mananger UI: 479 | + 480 | image::images/appsman.jpg[] 481 | 482 | . From this UI you can also dynamically change logging levels: 483 | + 484 | image::images/logging.jpg[] 485 | 486 | *Congratulations!* You’ve just learned how to add health and metrics to any Spring Boot application. 487 | -------------------------------------------------------------------------------- /labs/lab04/images/config-scs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab04/images/config-scs.jpg -------------------------------------------------------------------------------- /labs/lab04/images/config-scs1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab04/images/config-scs1.jpg -------------------------------------------------------------------------------- /labs/lab04/images/config-scs2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab04/images/config-scs2.jpg -------------------------------------------------------------------------------- /labs/lab04/images/config-scs3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab04/images/config-scs3.jpg -------------------------------------------------------------------------------- /labs/lab04/lab04.adoc: -------------------------------------------------------------------------------- 1 | = Adding Spring Cloud Config to Boot Application 2 | 3 | In this lab we'll utilize Spring Boot and Spring Cloud to configure our application from a configuration dynamically retrieved from a git repository. We'll then deploy it to Pivotal Cloud Foundry and auto-provision an instance of a configuration server using Pivotal Spring Cloud Services. 4 | 5 | == Update _Hello_ REST service 6 | 7 | . These features are added by adding _spring-cloud-services-starter-config-client_ to the classpath. Open your Maven POM found here: */cloud-native-spring/pom.xml*. Add the following spring cloud services dependency: 8 | + 9 | [source, xml] 10 | --------------------------------------------------------------------- 11 | 12 | io.pivotal.spring.cloud 13 | spring-cloud-services-starter-config-client 14 | 15 | --------------------------------------------------------------------- 16 | 17 | . We also need to add a general entry for Spring Cloud dependency management. Add this snippet to your POM: 18 | + 19 | [source, xml] 20 | --------------------------------------------------------------------- 21 | 22 | 23 | 24 | io.pivotal.spring.cloud 25 | spring-cloud-services-dependencies 26 | 1.3.1.RELEASE 27 | pom 28 | import 29 | 30 | 31 | org.springframework.cloud 32 | spring-cloud-dependencies 33 | Camden.SR4 34 | pom 35 | import 36 | 37 | 38 | 39 | --------------------------------------------------------------------- 40 | 41 | . Add an @Value annotation, private field, and associated usage to the class _io.pivotal.CloudNativeSpringApplication_ (/cloud-native-spring/src/main/java/io/pivotal/CloudNativeSpringApplication.java): 42 | + 43 | [source, java, numbered] 44 | --------------------------------------------------------------------- 45 | @Value("${greeting:Hola}") 46 | private String _greeting; 47 | 48 | @RequestMapping("/") 49 | public String hello() { 50 | return _greeting + " World!"; 51 | } 52 | --------------------------------------------------------------------- 53 | + 54 | Completed: 55 | + 56 | [source,java,numbered] 57 | --------------------------------------------------------------------- 58 | package io.pivotal; 59 | 60 | import org.springframework.beans.factory.annotation.Value; 61 | import org.springframework.boot.SpringApplication; 62 | import org.springframework.boot.autoconfigure.SpringBootApplication; 63 | import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration; 64 | import org.springframework.context.annotation.Import; 65 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 66 | import org.springframework.web.bind.annotation.RequestMapping; 67 | import org.springframework.web.bind.annotation.RestController; 68 | 69 | @SpringBootApplication 70 | @RestController 71 | @EnableJpaRepositories 72 | @Import(RepositoryRestMvcAutoConfiguration.class) 73 | public class CloudNativeSpringApplication { 74 | 75 | public static void main(String[] args) { 76 | SpringApplication.run(CloudNativeSpringApplication.class, args); 77 | } 78 | 79 | @Value("${greeting:Hola}") 80 | private String _greeting; 81 | 82 | @RequestMapping("/") 83 | public String hello() { 84 | return _greeting + " World!"; 85 | } 86 | } 87 | --------------------------------------------------------------------- 88 | 89 | . When we introduced the Spring Cloud Services Starter Config Client dependency Spring Security will also be included (Config servers will be protected by OAuth2). However, this will also enable basic authentication to all our service endpoints. Add the following configuration to */cloud-native-spring/src/main/resources/application.yml*: 90 | + 91 | [source, yaml] 92 | --------------------------------------------------------------------- 93 | security: 94 | basic: 95 | enabled: false 96 | --------------------------------------------------------------------- 97 | 98 | . We'll also want to give our Spring Boot App a name so that it can lookup application-specific configuration from the config server later. Add the following configuration to */cloud-native-spring/src/main/resources/application.yml*: 99 | + 100 | [source, yaml] 101 | --------------------------------------------------------------------- 102 | spring: 103 | application: 104 | name: cloud-native-spring 105 | --------------------------------------------------------------------- 106 | 107 | . Complete YML: 108 | + 109 | [source, yaml] 110 | --------------------------------------------------------------------- 111 | spring: 112 | application: 113 | name: cloud-native-spring 114 | 115 | endpoints: # add this section 116 | sensitive: false 117 | 118 | info: # add this section 119 | build: 120 | artifact: @project.artifactId@ 121 | name: @project.name@ 122 | description: @project.description@ 123 | version: @project.version@ 124 | 125 | management: 126 | security: 127 | enabled: false 128 | info: 129 | git: 130 | mode: full 131 | cloudfoundry: 132 | enabled: true 133 | skip-ssl-validation: true 134 | 135 | security: 136 | basic: 137 | --------------------------------------------------------------------- 138 | 139 | == Run the _cloud-native-spring_ Application and verify dynamic config is working 140 | 141 | . Run the application 142 | + 143 | [source,bash] 144 | --------------------------------------------------------------------- 145 | $ mvn clean spring-boot:run 146 | --------------------------------------------------------------------- 147 | 148 | . Browse to http://localhost:8080 and verify you now see your new greeting. 149 | 150 | . Stop the _cloud-native-spring_ application 151 | 152 | == Create Spring Cloud Config Server instance 153 | 154 | . Now that our application is ready to read its config from a cloud config server, we need to deploy one! This can be done through cloudfoundry using the services marketplace. Browse to the marketplace in Pivotal Cloudfoundry Apps Manager, https://apps.cfpoc2.internal.t-mobile.com, navigate to the space you have been using to push your app, and select Config Server: 155 | + 156 | image::images/config-scs.jpg[] 157 | 158 | . In the resulting details page, select the _standard_, single tenant plan. Name the instance *config-server*, select the space that you've been using to push all your applications. At this time you don't need to select a application to bind to the service: 159 | + 160 | image::images/config-scs1.jpg[] 161 | 162 | . After we create the service instance you'll be redirected to your _Space_ landing page that lists your apps and services. The config server is deployed on-demand and will take a few moments to deploy. Once the messsage _The Service Instance is Initializing_ disappears click on the service you provisioned. Select the manage link towards the top of the resulting screen to view the instance id and a JSON document with a single element, count, which validates that the instance provisioned correctly: 163 | + 164 | image::images/config-scs2.jpg[] 165 | 166 | . We now need to update the service instance with our GIT repository information. Using the cloudfoundry CLI execute the following update service command: 167 | + 168 | [source,bash] 169 | --------------------------------------------------------------------- 170 | $ cf update-service config-server -c '{"git": { "uri": "https://github.com/azwickey-pivotal/config-repo" } }' 171 | --------------------------------------------------------------------- 172 | 173 | . Refresh you Config Server management page and you will see the following message. Wait until the screen refreshes and the service is reintialized: 174 | + 175 | image::images/config-scs3.jpg[] 176 | 177 | . We will now bind our application to our config-server within our Cloudfoundry deployment manifest. Add these 2 entries to the bottom of */cloud-native-spring/manifest.yml* 178 | + 179 | [source, yml] 180 | --------------------------------------------------------------------- 181 | env: 182 | TRUST_CERTS: api.cfpoc2.internal.t-mobile.com 183 | services: 184 | - config-server 185 | --------------------------------------------------------------------- 186 | + 187 | Complete: 188 | + 189 | [source, yml] 190 | --------------------------------------------------------------------- 191 | --- 192 | applications: 193 | - name: cloud-native-spring 194 | host: cloud-native-spring 195 | memory: 512M 196 | instances: 1 197 | path: ./target/cloud-native-spring-0.0.1-SNAPSHOT.jar 198 | buildpack: java_buildpack_offline 199 | timeout: 180 200 | env: 201 | TRUST_CERTS: https://api.cfpoc2.internal.t-mobile.com 202 | JAVA_OPTS: -Djava.security.egd=file:///dev/urandom 203 | services: 204 | - config-server 205 | --------------------------------------------------------------------- 206 | 207 | == Deploy and test application 208 | 209 | . Build the application 210 | + 211 | [source,bash] 212 | --------------------------------------------------------------------- 213 | $ mvn clean package 214 | --------------------------------------------------------------------- 215 | 216 | . Push application into Cloud Foundry 217 | + 218 | [source,bash] 219 | --------------------------------------------------------------------- 220 | $ cf push -f manifest.yml 221 | --------------------------------------------------------------------- 222 | 223 | . Test your application by navigating to the root URL of the application, which will invoke the hello() service. You should now see a greeting that is read from the cloud config server! 224 | + 225 | Bon Jour World! 226 | 227 | . What just happened?? A Spring component within the Spring Cloud Starter Config Client module called a _service connector_ automatically detected that there was a Cloud Config service bound into the application. The service connector configured the application automatically to connect to the cloud config server and download the configuration and wire it into the application 228 | 229 | . If you navigate to the GIT repo we specified for our configuration, https://github.com/azwickey-pivotal/config-repo, you'll see a file named cloud-native-spring.yml. This filename is the same as our spring.application.name value for our Boot application. The configuration is read from this file, in our case the following property: 230 | + 231 | [source, yaml] 232 | --------------------------------------------------------------------- 233 | greeting: Bon Jour 234 | --------------------------------------------------------------------- 235 | 236 | . Next we'll learn how to register our service with a service registry and load balance requests using Spring Cloud components. 237 | -------------------------------------------------------------------------------- /labs/lab05/images/registry1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab05/images/registry1.jpg -------------------------------------------------------------------------------- /labs/lab05/images/registry2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab05/images/registry2.jpg -------------------------------------------------------------------------------- /labs/lab05/images/ui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab05/images/ui.jpg -------------------------------------------------------------------------------- /labs/lab05/lab05.adoc: -------------------------------------------------------------------------------- 1 | = Adding Service Registration and Discover with Spring Cloud 2 | 3 | In this lab we'll utilize Spring Boot and Spring Cloud to configure our application register itself with a service registry. To do this we'll also need to provision an instance of a Eureka service registry using Pivotal Cloudfoundry Spring Cloud Services. We'll also add a simple client application that looks up our application from the service registry and makes requests to our Cities service. 4 | 5 | == Update _Cloud-Native-Spring_ Boot Application to Register with Eureka 6 | 7 | . These features are added by adding _spring-cloud-services-starter-service-registry_ to the classpath. Open your Maven POM found here: */cloud-native-spring/pom.xml*. Add the following spring cloud services dependency: 8 | + 9 | [source, xml] 10 | --------------------------------------------------------------------- 11 | 12 | io.pivotal.spring.cloud 13 | spring-cloud-services-starter-service-registry 14 | 15 | --------------------------------------------------------------------- 16 | + 17 | 18 | . Thanks to Spring Cloud instructing your application to register with Eureka is as simple as adding a single annotation to your app! Add an @EnableDiscoveryClient annotation to the class _io.pivotal.CloudNativeSpringApplication_ (/cloud-native-spring/src/main/java/io/pivotal/CloudNativeApplication.java): 19 | + 20 | [source, java, numbered] 21 | --------------------------------------------------------------------- 22 | @SpringBootApplication 23 | @RestController 24 | @EnableJpaRepositories 25 | @EnableDiscoveryClient 26 | @Import(RepositoryRestMvcAutoConfiguration.class) 27 | public class CloudNativeSpringApplication { 28 | --------------------------------------------------------------------- 29 | + 30 | Completed: 31 | + 32 | [source,java,numbered] 33 | --------------------------------------------------------------------- 34 | package io.pivotal; 35 | 36 | import org.springframework.beans.factory.annotation.Value; 37 | import org.springframework.boot.SpringApplication; 38 | import org.springframework.boot.autoconfigure.SpringBootApplication; 39 | import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration; 40 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 41 | import org.springframework.context.annotation.Import; 42 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 43 | import org.springframework.web.bind.annotation.RequestMapping; 44 | import org.springframework.web.bind.annotation.RestController; 45 | 46 | @SpringBootApplication 47 | @RestController 48 | @EnableJpaRepositories 49 | @EnableDiscoveryClient 50 | @Import(RepositoryRestMvcAutoConfiguration.class) 51 | public class CloudNativeSpringApplication { 52 | 53 | public static void main(String[] args) { 54 | SpringApplication.run(CloudNativeSpringApplication.class, args); 55 | } 56 | 57 | @Value("${greeting:Hola}") 58 | private String _greeting; 59 | 60 | @RequestMapping("/") 61 | public String hello() { 62 | return _greeting + " World!"; 63 | } 64 | } 65 | --------------------------------------------------------------------- 66 | 67 | == Create Spring Cloud Service Registry instance and deploy application 68 | 69 | . Now that our application is ready to read registry with an Eureka instance, we need to deploy one! This can be done through cloudfoundry using the services marketplace. Previously we did this through the Marketplace UI, but this time we will use the Cloudfoundry CLI (though we could also do this through the UI: 70 | + 71 | [source,bash] 72 | --------------------------------------------------------------------- 73 | $ cf create-service p-service-registry standard service-registry 74 | --------------------------------------------------------------------- 75 | 76 | . After you create the service registry instance navigate to your cloudfoundry space in the Apps Manager UI and refresh the page. You should now see the newly create service registry intance. Select the manage link to view the registry dashboard. Note that there are not any registered applications at the moment: 77 | + 78 | image::images/registry1.jpg[] 79 | 80 | . We will now bind our application to our service-registry within our Cloudfoundry deployment manifest. Add thee additional reference to a the service to the bottom of */cloud-native-spring/manifest.yml* in the services list: 81 | + 82 | [source, yml] 83 | --------------------------------------------------------------------- 84 | services: 85 | - config-server 86 | - service-registry 87 | --------------------------------------------------------------------- 88 | + 89 | Complete: 90 | + 91 | [source, yml] 92 | --------------------------------------------------------------------- 93 | --- 94 | applications: 95 | - name: cloud-native-spring 96 | host: cloud-native-spring 97 | memory: 512M 98 | instances: 1 99 | path: ./target/cloud-native-spring-0.0.1-SNAPSHOT.jar 100 | buildpack: java_buildpack_offline 101 | timeout: 180 102 | env: 103 | CF_TARGET: https://api.cfpoc2.internal.t-mobile.com 104 | JAVA_OPTS: -Djava.security.egd=file:///dev/urandom 105 | services: 106 | - config-server 107 | - service-registry 108 | --------------------------------------------------------------------- 109 | 110 | == Deploy and test application 111 | 112 | . Build the application 113 | + 114 | [source,bash] 115 | --------------------------------------------------------------------- 116 | $ mvn clean package 117 | --------------------------------------------------------------------- 118 | 119 | . For the 2nd half of this lab we'll need to have this maven artifact in our local repository, so install it with the following command: 120 | + 121 | [source,bash] 122 | --------------------------------------------------------------------- 123 | $ mvn install 124 | --------------------------------------------------------------------- 125 | 126 | . Push application into Cloud Foundry 127 | + 128 | [source,bash] 129 | --------------------------------------------------------------------- 130 | $ cf push -f manifest.yml 131 | --------------------------------------------------------------------- 132 | 133 | . If we now test our application URLs we will no change. However, if we view the Service Registry dashboard (accessible from the _manage_ link in Apps Manager) you will see that a service named cloud-native-spring has registered: 134 | + 135 | image::images/registry2.jpg[] 136 | 137 | . Next we'll create a simple UI application that will read the service registry to discover the location of our cities REST service and connect. 138 | 139 | == Create another Spring Boot Project as a Client UI 140 | 141 | . Browse to https://start.spring.io 142 | 143 | . Generate a Maven Project with Spring Boot 1.5.2. 144 | 145 | . Fill out the *Project metadata* fields as follows: 146 | + 147 | Group:: +io.pivotal+ 148 | Artifact:: +cloud-native-spring-ui+ 149 | 150 | . In the dependencies section, add the following: 151 | + 152 | *Vaadin* *Actuator* *Feign* 153 | 154 | . Click the _Generate Project_ button. Your browser will download a zip file. 155 | 156 | . Copy then unpack the downloaded zip file to *CN-Workshop-TM/labs/lab05/cloud-native-spring-ui* 157 | + 158 | Your directory structure should now look like: 159 | + 160 | [source, bash] 161 | --------------------------------------------------------------------- 162 | CN-Workshop-TM: 163 | ├── labs 164 | │   ├── lab01 165 | │   │   ├── cloud-native-spring 166 | │   ├── lab05 167 | │   │   ├── cloud-native-spring-ui 168 | --------------------------------------------------------------------- 169 | 170 | . Import the project’s pom.xml into your editor/IDE of choice. 171 | 172 | . We will need to add a the general entry for Spring Cloud dependency management as we added to our other project. Open your Maven POM found here: */cloud-native-spring-ui/pom.xml*: 173 | + 174 | [source, xml] 175 | --------------------------------------------------------------------- 176 | 177 | 178 | 179 | io.pivotal.spring.cloud 180 | spring-cloud-services-dependencies 181 | 1.3.1.RELEASE 182 | pom 183 | import 184 | 185 | 186 | org.springframework.cloud 187 | spring-cloud-dependencies 188 | Camden.SR4 189 | pom 190 | import 191 | 192 | 193 | 194 | --------------------------------------------------------------------- 195 | 196 | . As before, we need to add _spring-cloud-services-starter-service-registry_ to the classpath. Add this to your POM: 197 | + 198 | [source, xml] 199 | --------------------------------------------------------------------- 200 | 201 | io.pivotal.spring.cloud 202 | spring-cloud-services-starter-service-registry 203 | 204 | --------------------------------------------------------------------- 205 | + 206 | We'll also be using the Domain object from our main Boot application. Add that as a dependency too: 207 | + 208 | [source, xml] 209 | --------------------------------------------------------------------- 210 | 211 | io.pivotal 212 | cloud-native-spring 213 | 0.0.1-SNAPSHOT 214 | 215 | --------------------------------------------------------------------- 216 | 217 | . Since this UI is going to consume REST services its an awesome opportunity to use Feign. Feign will handle *ALL* the work of invoking our services and marshalling/unmarshalling JSON into domain objects. We'll add a Feign Client interface into our app. Take note of how Feign references the downstream service; its only the name of the service it will lookup from Eureka service registry. Add the following interface declaration to the _CloudNativeSpringUIApplication_: 218 | + 219 | [source,java,numbered] 220 | --------------------------------------------------------------------- 221 | @FeignClient("https://cloud-native-spring") 222 | public interface CityClient { 223 | 224 | @RequestMapping(method=RequestMethod.GET, value="/cities", consumes="application/hal+json") 225 | Resources getCities(); 226 | } 227 | --------------------------------------------------------------------- 228 | + 229 | We'll also need to add a few annotations to our boot application: 230 | + 231 | [source,java,numbered] 232 | --------------------------------------------------------------------- 233 | @SpringBootApplication 234 | @EnableFeignClients 235 | @EnableDiscoveryClient 236 | public class CloudNativeSpringUiApplication { 237 | --------------------------------------------------------------------- 238 | + 239 | Completed: 240 | + 241 | [source,java,numbered] 242 | --------------------------------------------------------------------- 243 | package io.pivotal; 244 | 245 | import io.pivotal.domain.City; 246 | import org.springframework.boot.SpringApplication; 247 | import org.springframework.boot.autoconfigure.SpringBootApplication; 248 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 249 | import org.springframework.cloud.netflix.feign.EnableFeignClients; 250 | import org.springframework.cloud.netflix.feign.FeignClient; 251 | import org.springframework.hateoas.Resources; 252 | import org.springframework.web.bind.annotation.RequestMapping; 253 | import org.springframework.web.bind.annotation.RequestMethod; 254 | 255 | @SpringBootApplication 256 | @EnableFeignClients 257 | @EnableDiscoveryClient 258 | public class CloudNativeSpringUiApplication { 259 | 260 | public static void main(String[] args) { 261 | SpringApplication.run(CloudNativeSpringUiApplication.class, args); 262 | } 263 | 264 | @FeignClient("https://cloud-native-spring") 265 | protected interface CityClient { 266 | 267 | @RequestMapping(method=RequestMethod.GET, value="/cities", consumes="application/hal+json") 268 | Resources getCities(); 269 | } 270 | } 271 | 272 | --------------------------------------------------------------------- 273 | 274 | . Next we'll create a Vaadin UI for rendering our data. The point of this workshop isn't to go into detail on creating UIs; for now suffice to say that Vaadin is a great tool for quickly creating User Interfaces. Our UI will consume our Feign client we just created. Create the class _io.pivotal.AppUI_ (/cloud-native-spring-ui/src/main/java/io/pivotal/AppUI.java) and into it paste the following code: 275 | + 276 | [source,java] 277 | --------------------------------------------------------------------- 278 | package io.pivotal; 279 | 280 | import com.vaadin.annotations.Theme; 281 | 282 | import com.vaadin.server.VaadinRequest; 283 | import com.vaadin.spring.annotation.SpringUI; 284 | import com.vaadin.ui.Grid; 285 | import com.vaadin.ui.UI; 286 | import io.pivotal.domain.City; 287 | import org.springframework.beans.factory.annotation.Autowired; 288 | 289 | import java.util.ArrayList; 290 | import java.util.Collection; 291 | 292 | @SpringUI 293 | @Theme("valo") 294 | public class AppUI extends UI { 295 | 296 | private final CloudNativeSpringUiApplication.CityClient _client; 297 | private final Grid _grid; 298 | 299 | @Autowired 300 | public AppUI(CloudNativeSpringUiApplication.CityClient client) { 301 | _client = client; 302 | _grid = new Grid<>(City.class); 303 | } 304 | 305 | @Override 306 | protected void init(VaadinRequest request) { 307 | setContent(_grid); 308 | _grid.setWidth(100, Unit.PERCENTAGE); 309 | _grid.setHeight(100, Unit.PERCENTAGE); 310 | Collection collection = new ArrayList<>(); 311 | _client.getCities().forEach(collection::add); 312 | _grid.setItems(collection); 313 | } 314 | } 315 | --------------------------------------------------------------------- 316 | . We'll also want to give our UI App a name so that it can register properly with Eureka and potentially use cloud config in the future. Add the following configuration to */cloud-native-spring-ui/src/main/resources/application.properties*: 317 | + 318 | [source, yaml] 319 | --------------------------------------------------------------------- 320 | spring.application.name=cloud-native-spring-ui 321 | --------------------------------------------------------------------- 322 | 323 | == Deploy and test application 324 | 325 | . Build the application. We have to skip the tests otherwise we may fail because of having 2 spring boot apps on the classpath 326 | + 327 | [source,bash] 328 | --------------------------------------------------------------------- 329 | $ mvn clean package -DskipTests 330 | --------------------------------------------------------------------- 331 | 332 | . Create an application manifest in the root folder /cloud-native-spring-ui 333 | + 334 | $ touch manifest.yml 335 | 336 | . Add application metadata 337 | + 338 | [source, bash] 339 | --------------------------------------------------------------------- 340 | --- 341 | applications: 342 | - name: cloud-native-spring-ui 343 | host: cloud-native-spring-ui 344 | memory: 1G 345 | instances: 1 346 | path: ./target/cloud-native-spring-ui-0.0.1-SNAPSHOT.jar 347 | buildpack: java_buildpack_offline 348 | timeout: 180 349 | env: 350 | TRUST_CERTS: api.cfpoc2.internal.t-mobile.com 351 | JAVA_OPTS: -Djava.security.egd=file:///dev/urandom 352 | services: 353 | - service-registry 354 | --------------------------------------------------------------------- 355 | 356 | . Push application into Cloud Foundry 357 | + 358 | [source,bash] 359 | --------------------------------------------------------------------- 360 | $ cf push -f manifest.yml 361 | --------------------------------------------------------------------- 362 | 363 | . Test your application by navigating to the root URL of the application, which will invoke Vaadin UI. You should now see a table listing the first set of rows returned from the cities microservice: 364 | + 365 | image::images/ui.jpg[] 366 | 367 | . From a commandline stop the cloud-native-spring microservice (the original city service, not the new UI) 368 | + 369 | [source,bash] 370 | --------------------------------------------------------------------- 371 | $ cf stop cloud-native-spring 372 | --------------------------------------------------------------------- 373 | . Refresh the UI app. What happens? Now you get a nasty error that is not very user friendly! 374 | 375 | . Next we'll learn how to make our UI Application more resilient in the case that our downstream services are unavailable. 376 | -------------------------------------------------------------------------------- /labs/lab06/images/dash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab06/images/dash.jpg -------------------------------------------------------------------------------- /labs/lab06/images/dash1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab06/images/dash1.jpg -------------------------------------------------------------------------------- /labs/lab06/images/empty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/labs/lab06/images/empty.jpg -------------------------------------------------------------------------------- /labs/lab06/lab06.adoc: -------------------------------------------------------------------------------- 1 | = Adding Spring Cloud Config to Boot Application 2 | 3 | In this lab we'll utilize Spring Boot and Spring Cloud to make our UI Application more resilient. We'll leverage Spring Cloud Circuit Breaker to configure our application behavior when our downstream dependencies are not available. Finally, we'll use the circuit break dashboard to view metrics of the circuit breaker we implemented, which will be auto-provisioned within Cloud Foundry Pivotal Spring Cloud Services. 4 | 5 | == Define a Circuit Breaker within the _UI Application_ 6 | 7 | . These features are added by adding _spring-cloud-services-starter-circuit-breaker_ to the classpath. Open you Maven POM found here: */cloud-native-spring-ui/pom.xml*. Add the following spring cloud services dependency: 8 | + 9 | [source, xml] 10 | --------------------------------------------------------------------- 11 | 12 | io.pivotal.spring.cloud 13 | spring-cloud-services-starter-circuit-breaker 14 | 15 | --------------------------------------------------------------------- 16 | 17 | . The first thing we need to add to our application is an @EnableCircuitBreaker annotation to the Spring Boot application. Add this annotation below the other ones on the CloudNativeSpringUIApplication declaration in the class _io.pivotal.CloudNativeSpringUIApplication_ (/cloud-native-spring-ui/src/main/java/io/pivotal/CloudNativeSpringUIApplication.java): 18 | + 19 | [source, java, numbered] 20 | --------------------------------------------------------------------- 21 | @SpringBootApplication 22 | @EnableFeignClients 23 | @EnableDiscoveryClient 24 | @EnableCircuitBreaker 25 | public class CloudNativeSpringUiApplication { 26 | --------------------------------------------------------------------- 27 | 28 | . When we introduced an @FeignClient into our application we were only required to provide an interface. We'll provide a dummy class that implements that interface for our fallback. We'll also reference that class as a fallback in our FeignClient annotion. First, create this inner class in the _CloudNativeSpringUIApplication_: 29 | + 30 | [source, java, numbered] 31 | --------------------------------------------------------------------- 32 | @Component 33 | public class CityClientFallback implements CityClient { 34 | @Override 35 | public Resources getCities() { 36 | //We'll just return an empty response 37 | return new Resources(Collections.EMPTY_LIST); 38 | } 39 | } 40 | --------------------------------------------------------------------- 41 | + 42 | . Also modify the _@FeignClient_ annotation to reference this class as the fallback in case of failure: 43 | + 44 | [source, java, numbered] 45 | --------------------------------------------------------------------- 46 | @FeignClient(name = "cloud-native-spring", fallback = CityClientFallback.class) 47 | public interface CityClient { 48 | --------------------------------------------------------------------- 49 | + 50 | . Your Boot Application should now look like this _CloudNativeSpringUiApplication_: 51 | + 52 | [source, java, numbered] 53 | --------------------------------------------------------------------- 54 | package io.pivotal; 55 | 56 | import io.pivotal.domain.City; 57 | import org.springframework.boot.SpringApplication; 58 | import org.springframework.boot.autoconfigure.SpringBootApplication; 59 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 60 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 61 | import org.springframework.cloud.netflix.feign.EnableFeignClients; 62 | import org.springframework.cloud.netflix.feign.FeignClient; 63 | import org.springframework.hateoas.Resources; 64 | import org.springframework.stereotype.Component; 65 | import org.springframework.web.bind.annotation.RequestMapping; 66 | import org.springframework.web.bind.annotation.RequestMethod; 67 | 68 | import java.util.Collections; 69 | 70 | @SpringBootApplication 71 | @EnableFeignClients 72 | @EnableDiscoveryClient 73 | @EnableCircuitBreaker 74 | public class CloudNativeSpringUiApplication { 75 | 76 | public static void main(String[] args) { 77 | SpringApplication.run(CloudNativeSpringUiApplication.class, args); 78 | } 79 | 80 | @FeignClient(name = "cloud-native-spring", fallback = CityClientFallback.class) 81 | public interface CityClient { 82 | @RequestMapping(method= RequestMethod.GET, value="/cities", consumes="application/hal+json") 83 | Resources getCities(); 84 | } 85 | 86 | @Component 87 | public class CityClientFallback implements CityClient { 88 | @Override 89 | public Resources getCities() { 90 | //We'll just return an empty response 91 | return new Resources(Collections.EMPTY_LIST); 92 | } 93 | } 94 | } 95 | 96 | --------------------------------------------------------------------- 97 | 98 | == Create the Circuit Breaker Dashboard 99 | 100 | . When we modified our application to use a Hystrix Circuit Breaker our application automatically begins streaming out metrics about the health of our methods wrapped with a HystrixCommand. We can stream these events through a AMQP message bus into Turbine to view on a Circuit Breaker dashboard. This can be done through cloudfoundry using the services marketplace by executing the following command: 101 | + 102 | [source,bash] 103 | --------------------------------------------------------------------- 104 | $ cf create-service p-circuit-breaker-dashboard standard circuit-breaker-dashboard 105 | --------------------------------------------------------------------- 106 | 107 | . If we view the Circuit Breaker Dashboard (accessible from the _manage_ link in Apps Manager) you will see that a dashboard has been deployed but is empty (You may get an _initializing_ message for a few seconds. This should eventually refresh to a dashboard): 108 | + 109 | image::images/dash.jpg[] 110 | 111 | . We will now bind our application to our circuit-breaker-dashboard within our Cloudfoundry deployment manifest. Add these additional reference to a the service to the bottom of */cloud-native-spring-ui/manifest.yml* in the services list: 112 | + 113 | [source, yml] 114 | --------------------------------------------------------------------- 115 | services: 116 | - service-registry 117 | - circuit-breaker-dashboard 118 | --------------------------------------------------------------------- 119 | 120 | == Deploy and test application 121 | 122 | . Build the application 123 | + 124 | [source,bash] 125 | --------------------------------------------------------------------- 126 | $ mvn clean package -DskipTests 127 | --------------------------------------------------------------------- 128 | 129 | . Push application into Cloud Foundry 130 | + 131 | [source,bash] 132 | --------------------------------------------------------------------- 133 | $ cf push -f manifest.yml 134 | --------------------------------------------------------------------- 135 | 136 | . Test your application by navigating to the root URL of the application. If the dependent cities REST service is still stopped, you should simply see a blank table. Remember that last time you received a nasty exception in the browser? Now your Circuit Breaker fallback method is automatically called and the fallback behavior is executed. 137 | + 138 | image::images/empty.jpg[] 139 | 140 | . From a commandline start the cloud-native-spring microservice (the original city service, not the new UI) 141 | + 142 | [source,bash] 143 | --------------------------------------------------------------------- 144 | $ cf start cloud-native-spring 145 | --------------------------------------------------------------------- 146 | 147 | . Refresh the UI app and you should once again see a table listing the first page of cities. 148 | + 149 | image::../lab05/images/ui.jpg[] 150 | 151 | . Refresh your UI application a few times to force some traffic though the circuit breaker call path. After doing this you should now see the dashboard populated with metrics about the health of your Hystrix circuit breaker: 152 | + 153 | image::images/dash1.jpg[] 154 | -------------------------------------------------------------------------------- /labs/labaccess.adoc: -------------------------------------------------------------------------------- 1 | = PCF Environment Access 2 | 3 | == Account set up 4 | 5 | . If you do not have an account yet, please see the instructor 6 | 7 | == Target the Environment 8 | 9 | . If you haven't already, download the latest release of the Cloud Foundry CLI from https://github.com/cloudfoundry/cli/releases for your operating system and install it. 10 | 11 | . Set the API target for the CLI: (set appropriate end point for your environment) 12 | + 13 | ---- 14 | $ cf api https://api.cfpoc2.internal.t-mobile.com --skip-ssl-validation 15 | ---- 16 | 17 | . Login to Pivotal Cloudfoundry: 18 | + 19 | ---- 20 | $ cf login 21 | ---- 22 | + 23 | Follow the prompts 24 | 25 | == Apps Manager UI 26 | 27 | . An alternative to installing the CF CLI is via your PCF Apps Manager interface. 28 | 29 | . Navigate in a web browser to https://apps.cfpoc2.internal.t-mobile.com 30 | 31 | . Login to the interface with your email and workshop password. Note the pasword will be supplied by the instructor on the day of delivery. 32 | 33 | . Click the 'Tools' link, and download the CLI matching your operating system 34 | -------------------------------------------------------------------------------- /presentations/Intro_CF_at_TM.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Intro_CF_at_TM.pptx -------------------------------------------------------------------------------- /presentations/README.adoc: -------------------------------------------------------------------------------- 1 | = Presentation Content 2 | 3 | * Intro: link:presentations/Intro_CF_at_TM.pptx[_Pivotal Cloud Foundry at T-Mobile_] 4 | * Session 1: link:presentations/Session_1_CN_Design_DDD.pptx[_Cloud Native Design, Domain Driven Design, & Microservices_] 5 | * Session 2: link:presentations/Session_2_Intro_Boot.pptx[_Introducing Spring Boot_] 6 | * Session 3: link:presentations/Session_3_Polyglot_Persist.pptx[_Polyglot Persistence with Spring Data REST_] 7 | * Session 4: link:presentations/Session_4_Advanced_Boot.pptx[_Advancing Spring Boot with Actuator and Profiles_] 8 | * Session 5: link:presentations/Session_5_Intro_SC.pptx[_Introducing Spring Cloud Netflix_] 9 | * Session 6: link:presentations/Session_6_SC_Config.pptx[_Spring Cloud Config_] 10 | * Session 7: link:presentations/Session_7_SC_Discovery_LB.pptx[_Spring Cloud Netflix - Service Discovery & Load Balancing_] 11 | * Session 8: link:presentations/Session_8_Circuit_Breaker.pptx[_Spring Cloud Netflix - Circuit Breakers_] 12 | -------------------------------------------------------------------------------- /presentations/Session_1_CN_Design_DDD.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_1_CN_Design_DDD.pptx -------------------------------------------------------------------------------- /presentations/Session_2_Intro_Boot.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_2_Intro_Boot.pptx -------------------------------------------------------------------------------- /presentations/Session_3_Polyglot_Persist.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_3_Polyglot_Persist.pptx -------------------------------------------------------------------------------- /presentations/Session_4_Advanced_Boot.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_4_Advanced_Boot.pptx -------------------------------------------------------------------------------- /presentations/Session_5_Intro_SC.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_5_Intro_SC.pptx -------------------------------------------------------------------------------- /presentations/Session_6_SC_Config.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_6_SC_Config.pptx -------------------------------------------------------------------------------- /presentations/Session_7_SC_Discovery_LB.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_7_SC_Discovery_LB.pptx -------------------------------------------------------------------------------- /presentations/Session_8_Circuit_Breaker.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/CN-Workshop-TM/56c2d6259a83716f571105d737fdb4b48f6ea6ce/presentations/Session_8_Circuit_Breaker.pptx --------------------------------------------------------------------------------