├── .gitignore ├── README.asc ├── api-gateway ├── configserver │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── pivotal │ │ │ │ └── microservices │ │ │ │ └── configserver │ │ │ │ └── Application.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── io │ │ └── pivotal │ │ └── microservices │ │ └── configserver │ │ └── ApplicationTests.java ├── eureka │ ├── manifest.yml │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── io │ │ │ └── pivotal │ │ │ └── microservices │ │ │ └── eureka │ │ │ └── Application.java │ │ └── resources │ │ └── application.yml ├── gateway │ ├── manifest.yml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── pivotal │ │ │ │ └── microservices │ │ │ │ └── apigateway │ │ │ │ ├── Application.java │ │ │ │ ├── models │ │ │ │ └── MovieDetails.java │ │ │ │ └── services │ │ │ │ ├── catalog │ │ │ │ ├── CatalogIntegrationService.java │ │ │ │ └── Movie.java │ │ │ │ ├── recommendations │ │ │ │ ├── Movie.java │ │ │ │ └── RecommendationsIntegrationService.java │ │ │ │ └── reviews │ │ │ │ ├── Review.java │ │ │ │ └── ReviewsIntegrationService.java │ │ └── resources │ │ │ ├── application.yml │ │ │ └── bootstrap.yml │ │ └── test │ │ └── java │ │ └── io │ │ └── pivotal │ │ └── microservices │ │ └── apigateway │ │ └── ApplicationTests.java └── hystrixDashboard │ ├── hitIt.sh │ ├── manifest.yml │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── io │ │ └── pivotal │ │ └── microservices │ │ └── hystrixdashboard │ │ └── Application.java │ └── resources │ └── application.yml ├── bootcamp ├── manifest.yml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── bootiful │ │ │ ├── Application.java │ │ │ ├── BootServletInitializer.java │ │ │ └── CloudConfiguration.java │ └── resources │ │ ├── application.properties │ │ └── templates │ │ └── reservations.html │ └── test │ └── java │ └── bootiful │ └── ApplicationTests.java ├── configuration ├── code │ ├── .gitignore │ ├── README.asc │ ├── basics │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ ├── boot │ │ │ │ └── Application.java │ │ │ └── env │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ ├── application.yml │ │ │ └── simple.properties │ ├── cf.sh │ ├── config-client │ │ ├── .gitignore │ │ ├── manifest.yml │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── cloud │ │ │ │ └── client │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ └── bootstrap.yml │ ├── config-server │ │ ├── manifest.yml │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── cloud │ │ │ │ └── server │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ ├── application.yml │ │ │ └── bootstrap.yml │ └── pom.xml └── deck.key ├── polyglot-persistence ├── catalog │ ├── manifest.yml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── pivotal │ │ │ │ └── microservices │ │ │ │ └── catalog │ │ │ │ ├── Application.java │ │ │ │ ├── config │ │ │ │ └── CloudConfig.java │ │ │ │ ├── models │ │ │ │ ├── Genre.java │ │ │ │ └── Movie.java │ │ │ │ └── repositories │ │ │ │ ├── GenreRepository.java │ │ │ │ └── MovieRepository.java │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── bootstrap.yml │ │ │ └── import.sql │ │ └── test │ │ └── java │ │ └── io │ │ └── pivotal │ │ └── microservices │ │ └── catalog │ │ └── ApplicationTests.java ├── recommendations │ ├── manifest.yml │ ├── pom.xml │ ├── scripts │ │ ├── createGrapheneService.sh │ │ ├── loadGraph.sh │ │ ├── loadLikes.sh │ │ ├── loadMovies.sh │ │ └── loadPeople.sh │ ├── src │ │ └── main │ │ │ ├── java │ │ │ ├── io │ │ │ │ └── pivotal │ │ │ │ │ └── microservices │ │ │ │ │ └── recommendations │ │ │ │ │ ├── Application.java │ │ │ │ │ ├── config │ │ │ │ │ ├── CloudConfig.java │ │ │ │ │ ├── LocalConfig.java │ │ │ │ │ └── Neo4jConfig.java │ │ │ │ │ ├── controllers │ │ │ │ │ └── ApiController.java │ │ │ │ │ ├── model │ │ │ │ │ ├── Likes.java │ │ │ │ │ ├── Movie.java │ │ │ │ │ └── Person.java │ │ │ │ │ └── repositories │ │ │ │ │ ├── LikesRepository.java │ │ │ │ │ ├── MovieRepository.java │ │ │ │ │ └── PersonRepository.java │ │ │ └── org │ │ │ │ └── springframework │ │ │ │ └── cloud │ │ │ │ └── neo4j │ │ │ │ ├── GraphDatabaseServiceConnectorCreator.java │ │ │ │ ├── GraphDatabaseServiceInfo.java │ │ │ │ └── GraphDatabaseServiceInfoCreator.java │ │ │ └── resources │ │ │ ├── META-INF │ │ │ └── services │ │ │ │ ├── org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator │ │ │ │ └── org.springframework.cloud.service.ServiceConnectorCreator │ │ │ ├── application.yml │ │ │ └── bootstrap.yml │ └── test └── reviews │ ├── manifest.yml │ ├── pom.xml │ ├── scripts │ └── loadReviews.sh │ └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── pivotal │ │ │ └── microservices │ │ │ └── reviews │ │ │ ├── Application.java │ │ │ ├── config │ │ │ └── CloudConfig.java │ │ │ ├── models │ │ │ └── Review.java │ │ │ └── repositories │ │ │ └── ReviewRepository.java │ └── resources │ │ ├── application.yml │ │ └── bootstrap.yml │ └── test │ └── java │ └── io │ └── pivotal │ └── microservices │ └── reviews │ └── ApplicationTests.java ├── security ├── code │ ├── .gitignore │ ├── README.asc │ ├── auth-service │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── auth │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── bootstrap.yml │ │ │ ├── data.sql │ │ │ └── schema.sql │ ├── bin │ │ ├── baby.jpg │ │ ├── download_photo.sh │ │ ├── oauth.sh │ │ └── upload_photo.sh │ ├── bookmark-service │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── bookmarks │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ ├── application.yml │ │ │ └── bootstrap.yml │ ├── eureka │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── registry │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ └── application.yml │ ├── passport-service │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── passport │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ ├── application.yml │ │ │ └── bootstrap.yml │ ├── photo-service │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── photos │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ ├── application.yml │ │ │ └── bootstrap.yml │ └── pom.xml └── deck.key └── service-registry ├── README.asc ├── bin ├── baby.jpg ├── download_photo.sh └── upload_photo.sh ├── bookmark-service ├── manifest.yml ├── pom.xml └── src │ └── main │ ├── java │ └── bookmarks │ │ └── Application.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── cf.sh ├── eureka-service ├── manifest.yml ├── pom.xml └── src │ └── main │ ├── java │ └── registry │ │ └── Application.java │ └── resources │ └── application.yml ├── passport-service ├── manifest.yml ├── pom.xml └── src │ └── main │ ├── java │ └── passport │ │ └── Application.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── photo-service ├── manifest.yml ├── pom.xml └── src │ └── main │ ├── java │ └── photos │ │ └── Application.java │ └── resources │ ├── application.yml │ └── bootstrap.yml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | target 15 | *.ipr 16 | *.iws 17 | *.iml 18 | -------------------------------------------------------------------------------- /README.asc: -------------------------------------------------------------------------------- 1 | = The Pivotal Micorservice: Spring and Cloud Foundry 2 | 3 | 1. Pivotal Platform Basics 4 | * Why Are You Here? 20 min (Matt) 5 | * "Boot" Camp - start.spring.io, psvm, java -jar, starters, single table/class-based web service (Josh) 35 min 6 | * Cloud Foundry Camp - push it, put the table in a bound service, scale it, kill it (watch it rise), logs, monitoring stuff (Matt) 35 min 7 | 8 | 2. Microservice Building Blocks 9 | * Configuration… (Josh) 45 10 | * Polyglot Persistence / Data (Matt) 45 11 | 12 | 3. Building Reliable Distributed Systems 13 | * Registration / Discovery / client side routing (Josh) 14 | * Fault Tolerance (Matt) 45 15 | 16 | 5. Living on the Edge 17 | * API gateway / integration / clients / reactive/ concurrency (Matt) 45 18 | * Security (Josh) 45 19 | -------------------------------------------------------------------------------- /api-gateway/configserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-config-server 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-config-server 12 | Config Server 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | 33 | 34 | UTF-8 35 | io.pivotal.microservices.configserver.Application 36 | 1.8 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-maven-plugin 44 | 45 | 46 | 47 | 48 | 49 | 50 | spring-snapshots 51 | Spring Snapshots 52 | https://repo.spring.io/snapshot 53 | 54 | true 55 | 56 | 57 | 58 | spring-milestones 59 | Spring Milestones 60 | https://repo.spring.io/milestone 61 | 62 | false 63 | 64 | 65 | 66 | 67 | 68 | spring-snapshots 69 | Spring Snapshots 70 | https://repo.spring.io/snapshot 71 | 72 | true 73 | 74 | 75 | 76 | spring-milestones 77 | Spring Milestones 78 | https://repo.spring.io/milestone 79 | 80 | false 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /api-gateway/configserver/src/main/java/io/pivotal/microservices/configserver/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.configserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | @ComponentScan 10 | @EnableAutoConfiguration 11 | public class Application { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(Application.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /api-gateway/configserver/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-lab/03153fee15b7d13691b031ba4af2ad95700cb25d/api-gateway/configserver/src/main/resources/application.properties -------------------------------------------------------------------------------- /api-gateway/configserver/src/test/java/io/pivotal/microservices/configserver/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.configserver; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.test.context.web.WebAppConfiguration; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = Application.class) 11 | @WebAppConfiguration 12 | public class ApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /api-gateway/eureka/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: ms-service-registry 4 | memory: 512M 5 | instances: 1 6 | path: target/microservices-eureka-0.0.1-SNAPSHOT.jar 7 | -------------------------------------------------------------------------------- /api-gateway/eureka/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-eureka 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-eureka 12 | Eureka Service Registry 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-eureka-server 25 | 26 | 27 | 28 | 29 | UTF-8 30 | io.pivotal.microservices.eureka.Application 31 | 1.8 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-maven-plugin 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-parent 48 | 1.0.0.M3 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | spring-snapshots 58 | Spring Snapshots 59 | https://repo.spring.io/snapshot 60 | 61 | true 62 | 63 | 64 | 65 | spring-milestones 66 | Spring Milestones 67 | https://repo.spring.io/milestone 68 | 69 | false 70 | 71 | 72 | 73 | 74 | 75 | spring-snapshots 76 | Spring Snapshots 77 | https://repo.spring.io/snapshot 78 | 79 | true 80 | 81 | 82 | 83 | spring-milestones 84 | Spring Milestones 85 | https://repo.spring.io/milestone 86 | 87 | false 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /api-gateway/eureka/src/main/java/io/pivotal/microservices/eureka/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @ComponentScan 11 | @EnableAutoConfiguration 12 | @EnableEurekaServer 13 | public class Application { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(Application.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /api-gateway/eureka/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8761 3 | 4 | eureka: 5 | client: 6 | registerWithEureka: false 7 | fetchRegistry: false 8 | server: 9 | waitTimeInMsWhenSyncEmpty: 0 -------------------------------------------------------------------------------- /api-gateway/gateway/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: ms-api-gateway-service 4 | memory: 512M 5 | instances: 1 6 | path: target/microservices-api-gateway-0.0.1-SNAPSHOT.jar 7 | services: 8 | - eureka-service 9 | -------------------------------------------------------------------------------- /api-gateway/gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-api-gateway 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-api-gateway 12 | API Gateway Service 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-eureka 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-starter-zuul 38 | 39 | 40 | 41 | 42 | UTF-8 43 | io.pivotal.microservices.apigateway.Application 44 | 1.8 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.cloud 60 | spring-cloud-starter-parent 61 | 1.0.0.M3 62 | pom 63 | import 64 | 65 | 66 | 67 | 68 | 69 | 70 | spring-snapshots 71 | Spring Snapshots 72 | https://repo.spring.io/snapshot 73 | 74 | true 75 | 76 | 77 | 78 | spring-milestones 79 | Spring Milestones 80 | https://repo.spring.io/milestone 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | spring-snapshots 89 | Spring Snapshots 90 | https://repo.spring.io/snapshot 91 | 92 | true 93 | 94 | 95 | 96 | spring-milestones 97 | Spring Milestones 98 | https://repo.spring.io/milestone 99 | 100 | false 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway; 2 | 3 | import io.pivotal.microservices.apigateway.models.MovieDetails; 4 | import io.pivotal.microservices.apigateway.services.catalog.CatalogIntegrationService; 5 | import io.pivotal.microservices.apigateway.services.recommendations.RecommendationsIntegrationService; 6 | import io.pivotal.microservices.apigateway.services.reviews.ReviewsIntegrationService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 10 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 11 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 12 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 13 | import org.springframework.context.annotation.ComponentScan; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.web.bind.annotation.PathVariable; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.context.request.async.DeferredResult; 19 | import rx.Observable; 20 | import rx.Observer; 21 | 22 | @Configuration 23 | @ComponentScan 24 | @EnableAutoConfiguration 25 | @EnableEurekaClient 26 | @EnableZuulProxy 27 | @EnableHystrix 28 | @RestController 29 | public class Application { 30 | 31 | public static void main(String[] args) { 32 | SpringApplication.run(Application.class, args); 33 | } 34 | 35 | 36 | @Autowired 37 | CatalogIntegrationService catalogIntegrationService; 38 | 39 | @Autowired 40 | ReviewsIntegrationService reviewsIntegrationService; 41 | 42 | @Autowired 43 | RecommendationsIntegrationService recommendationsIntegrationService; 44 | 45 | @RequestMapping("/movie/{mlId}") 46 | public DeferredResult movieDetails(@PathVariable String mlId) { 47 | Observable details = Observable.zip(catalogIntegrationService.getMovie(mlId), 48 | reviewsIntegrationService.reviewsFor(mlId), 49 | recommendationsIntegrationService.getRecommendations(mlId), (movie, reviews, recommendations) -> { 50 | MovieDetails movieDetails = new MovieDetails(); 51 | movieDetails.setMlId(movie.getMlId()); 52 | movieDetails.setTitle(movie.getTitle()); 53 | movieDetails.setReviews(reviews); 54 | movieDetails.setRecommendations(recommendations); 55 | return movieDetails; 56 | } 57 | ); 58 | return toDeferredResult(details); 59 | } 60 | 61 | public DeferredResult toDeferredResult(Observable details) { 62 | DeferredResult result = new DeferredResult<>(); 63 | details.subscribe(new Observer() { 64 | @Override 65 | public void onCompleted() { 66 | } 67 | 68 | @Override 69 | public void onError(Throwable throwable) { 70 | } 71 | 72 | @Override 73 | public void onNext(MovieDetails movieDetails) { 74 | result.setResult(movieDetails); 75 | } 76 | }); 77 | return result; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/models/MovieDetails.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.models; 2 | 3 | import io.pivotal.microservices.apigateway.services.recommendations.Movie; 4 | import io.pivotal.microservices.apigateway.services.reviews.Review; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class MovieDetails { 10 | private String title; 11 | private String mlId; 12 | private List reviews = new ArrayList<>(); 13 | private List recommendations = new ArrayList<>(); 14 | 15 | public String getTitle() { 16 | return title; 17 | } 18 | 19 | public void setTitle(String title) { 20 | this.title = title; 21 | } 22 | 23 | public String getMlId() { 24 | return mlId; 25 | } 26 | 27 | public void setMlId(String mlId) { 28 | this.mlId = mlId; 29 | } 30 | 31 | public List getReviews() { 32 | return reviews; 33 | } 34 | 35 | public void setReviews(List reviews) { 36 | this.reviews = reviews; 37 | } 38 | 39 | public List getRecommendations() { 40 | return recommendations; 41 | } 42 | 43 | public void setRecommendations(List recommendations) { 44 | this.recommendations = recommendations; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/services/catalog/CatalogIntegrationService.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.services.catalog; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import com.netflix.hystrix.contrib.javanica.command.ObservableResult; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.web.client.RestTemplate; 8 | import rx.Observable; 9 | 10 | @Service 11 | public class CatalogIntegrationService { 12 | 13 | @Autowired 14 | RestTemplate restTemplate; 15 | 16 | @HystrixCommand(fallbackMethod = "stubMovie") 17 | public Observable getMovie(final String mlId) { 18 | return new ObservableResult() { 19 | @Override 20 | public Movie invoke() { 21 | return restTemplate.getForObject("http://catalog-service/catalog/movies/{mlId}", Movie.class, mlId); 22 | } 23 | }; 24 | } 25 | 26 | private Movie stubMovie(final String mlId) { 27 | Movie stub = new Movie(); 28 | stub.setMlId(mlId); 29 | stub.setTitle("Interesting...the wrong title. Sssshhhh!"); 30 | return stub; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/services/catalog/Movie.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.services.catalog; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | @JsonIgnoreProperties(ignoreUnknown = true) 6 | public class Movie { 7 | private String title; 8 | private String mlId; 9 | 10 | public String getTitle() { 11 | return title; 12 | } 13 | 14 | public void setTitle(String title) { 15 | this.title = title; 16 | } 17 | 18 | public String getMlId() { 19 | return mlId; 20 | } 21 | 22 | public void setMlId(String mlId) { 23 | this.mlId = mlId; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/services/recommendations/Movie.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.services.recommendations; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | @JsonIgnoreProperties(ignoreUnknown = true) 6 | public class Movie { 7 | private String title; 8 | private String mlId; 9 | 10 | public String getTitle() { 11 | return title; 12 | } 13 | 14 | public void setTitle(String title) { 15 | this.title = title; 16 | } 17 | 18 | public String getMlId() { 19 | return mlId; 20 | } 21 | 22 | public void setMlId(String mlId) { 23 | this.mlId = mlId; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/services/recommendations/RecommendationsIntegrationService.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.services.recommendations; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import com.netflix.hystrix.contrib.javanica.command.ObservableResult; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.core.ParameterizedTypeReference; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.web.client.RestTemplate; 10 | import rx.Observable; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | @Service 16 | public class RecommendationsIntegrationService { 17 | @Autowired 18 | RestTemplate restTemplate; 19 | 20 | @HystrixCommand(fallbackMethod = "stubRecommendations") 21 | public Observable> getRecommendations(final String mlId) { 22 | return new ObservableResult>() { 23 | @Override 24 | public List invoke() { 25 | ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() { 26 | }; 27 | return restTemplate.exchange("http://recommendations-service/recommendations/forMovie/{mlId}", HttpMethod.GET, null, responseType, mlId).getBody(); 28 | } 29 | }; 30 | } 31 | 32 | private List stubRecommendations(final String mlId) { 33 | Movie one = new Movie(); 34 | one.setMlId("25"); 35 | one.setMlId("A movie which doesn't exist"); 36 | Movie two = new Movie(); 37 | two.setMlId("26"); 38 | two.setMlId("A movie about nothing"); 39 | return Arrays.asList(one, two); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/services/reviews/Review.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.services.reviews; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | @JsonIgnoreProperties(ignoreUnknown = true) 6 | public class Review { 7 | private String mlId; 8 | private String userName; 9 | private String title; 10 | private String review; 11 | private int rating; 12 | 13 | public String getMlId() { 14 | return mlId; 15 | } 16 | 17 | public void setMlId(String mlId) { 18 | this.mlId = mlId; 19 | } 20 | 21 | public String getUserName() { 22 | return userName; 23 | } 24 | 25 | public void setUserName(String userName) { 26 | this.userName = userName; 27 | } 28 | 29 | public String getTitle() { 30 | return title; 31 | } 32 | 33 | public void setTitle(String title) { 34 | this.title = title; 35 | } 36 | 37 | public String getReview() { 38 | return review; 39 | } 40 | 41 | public void setReview(String review) { 42 | this.review = review; 43 | } 44 | 45 | public int getRating() { 46 | return rating; 47 | } 48 | 49 | public void setRating(int rating) { 50 | this.rating = rating; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/java/io/pivotal/microservices/apigateway/services/reviews/ReviewsIntegrationService.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway.services.reviews; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import com.netflix.hystrix.contrib.javanica.command.ObservableResult; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.core.ParameterizedTypeReference; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.web.client.RestTemplate; 10 | import rx.Observable; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | @Service 16 | public class ReviewsIntegrationService { 17 | 18 | @Autowired 19 | RestTemplate restTemplate; 20 | 21 | @HystrixCommand(fallbackMethod = "stubReviews") 22 | public Observable> reviewsFor(String mlId) { 23 | return new ObservableResult>() { 24 | @Override 25 | public List invoke() { 26 | ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() { 27 | }; 28 | return restTemplate.exchange("http://reviews-service/reviews/reviews/{mlId}", HttpMethod.GET, null, responseType, mlId).getBody(); 29 | } 30 | }; 31 | } 32 | 33 | private List stubReviews(String mlId) { 34 | Review review = new Review(); 35 | review.setMlId(mlId); 36 | review.setRating(4); 37 | review.setTitle("Interesting...the wrong title. Sssshhhh!"); 38 | review.setReview("Awesome sauce!"); 39 | review.setUserName("joeblow"); 40 | return Arrays.asList(review); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | 4 | zuul: 5 | proxy: 6 | mapping: /api 7 | addProxyHeaders: true 8 | route: 9 | reviews-service: /reviews 10 | catalog-service: /catalog 11 | recommendations-service: /recommendations 12 | 13 | --- 14 | 15 | spring: 16 | profiles: cloud 17 | eureka: 18 | instance: 19 | hostname: ms-api-gateway-service.cfapps.io 20 | nonSecurePort: 80 21 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: api-gateway-service 4 | eureka: 5 | client: 6 | serviceUrl: 7 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 8 | -------------------------------------------------------------------------------- /api-gateway/gateway/src/test/java/io/pivotal/microservices/apigateway/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.apigateway; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.test.context.web.WebAppConfiguration; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = Application.class) 11 | @WebAppConfiguration 12 | public class ApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /api-gateway/hystrixDashboard/hitIt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for i in `seq 1 $2`; 4 | do 5 | curl $1 6 | done 7 | -------------------------------------------------------------------------------- /api-gateway/hystrixDashboard/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: ms-hystrix-dashboard 4 | memory: 512M 5 | instances: 1 6 | path: target/microservices-hystrix-dashboard-0.0.1-SNAPSHOT.jar 7 | -------------------------------------------------------------------------------- /api-gateway/hystrixDashboard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-hystrix-dashboard 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-hystrix-dashboard 12 | Hystrix Dashboard 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-hystrix-dashboard 25 | 26 | 27 | 28 | 29 | UTF-8 30 | io.pivotal.microservices.hystrixdashboard.Application 31 | 1.8 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-maven-plugin 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-parent 48 | 1.0.0.M3 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | spring-snapshots 58 | Spring Snapshots 59 | https://repo.spring.io/snapshot 60 | 61 | true 62 | 63 | 64 | 65 | spring-milestones 66 | Spring Milestones 67 | https://repo.spring.io/milestone 68 | 69 | false 70 | 71 | 72 | 73 | 74 | 75 | spring-snapshots 76 | Spring Snapshots 77 | https://repo.spring.io/snapshot 78 | 79 | true 80 | 81 | 82 | 83 | spring-milestones 84 | Spring Milestones 85 | https://repo.spring.io/milestone 86 | 87 | false 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /api-gateway/hystrixDashboard/src/main/java/io/pivotal/microservices/hystrixdashboard/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.hystrixdashboard; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | 11 | @Configuration 12 | @ComponentScan 13 | @EnableAutoConfiguration 14 | @EnableHystrixDashboard 15 | @Controller 16 | public class Application { 17 | 18 | @RequestMapping("/") 19 | public String home() { 20 | return "forward:/hystrix"; 21 | } 22 | 23 | public static void main(String[] args) { 24 | SpringApplication.run(Application.class, args); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /api-gateway/hystrixDashboard/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 7979 -------------------------------------------------------------------------------- /bootcamp/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: bootcamp 4 | memory: 512M 5 | instances: 1 6 | path: target/demo-0.0.1-SNAPSHOT.war 7 | env: 8 | SPRING_PROFILES_ACTIVE: cloud 9 | services: 10 | - bootcamp-db 11 | - session-replication 12 | 13 | -------------------------------------------------------------------------------- /bootcamp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.test 7 | demo 8 | 0.0.1-SNAPSHOT 9 | war 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 1.1.9.RELEASE 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-tomcat 21 | provided 22 | 23 | 24 | com.h2database 25 | h2 26 | 27 | 28 | postgresql 29 | postgresql 30 | 9.1-901-1.jdbc4 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jpa 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-data-rest 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-thymeleaf 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-actuator 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-remote-shell 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-test 60 | test 61 | 62 | 63 | org.springframework.cloud 64 | spring-cloud-cloudfoundry-connector 65 | 1.1.0.RELEASE 66 | 67 | 68 | org.springframework.cloud 69 | spring-cloud-spring-service-connector 70 | 1.1.0.RELEASE 71 | 72 | 73 | 74 | 75 | UTF-8 76 | bootiful.Application 77 | 1.8 78 | 79 | 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-maven-plugin 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /bootcamp/src/main/java/bootiful/Application.java: -------------------------------------------------------------------------------- 1 | package bootiful; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.actuate.health.Health; 7 | import org.springframework.boot.actuate.health.HealthIndicator; 8 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.ComponentScan; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.data.jpa.repository.JpaRepository; 13 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.Id; 21 | import java.util.Arrays; 22 | import java.util.Collection; 23 | 24 | @Configuration 25 | @ComponentScan 26 | @EnableAutoConfiguration 27 | public class Application { 28 | 29 | @Bean 30 | HealthIndicator healthIndicator() { 31 | return () -> Health.status("I <3 RWX & Ft. Lauderdale").build(); 32 | } 33 | 34 | @Bean 35 | CommandLineRunner commandLineRunner(ReservationRepository reservationRepository) { 36 | return args -> { 37 | Arrays.asList("mstine", "jlong").forEach(s -> reservationRepository.save(new Reservation(s))); 38 | reservationRepository.findAll().forEach(System.out::println); 39 | }; 40 | } 41 | 42 | public static void main(String[] args) { 43 | SpringApplication.run(Application.class, args); 44 | } 45 | } 46 | 47 | @Controller 48 | class ReservationMvcController { 49 | 50 | @Autowired 51 | private ReservationRepository reservationRepository; 52 | 53 | @RequestMapping("/reservations.php") 54 | String string(Model model) { 55 | model.addAttribute("reservations", this.reservationRepository.findAll()); 56 | return "reservations"; // src/main/resources/templates + $X + .html 57 | } 58 | 59 | @RequestMapping("/die.php") 60 | void die() { 61 | System.exit(1); 62 | } 63 | } 64 | 65 | @RepositoryRestResource 66 | interface ReservationRepository extends JpaRepository { 67 | Collection findByReservationName(String rn); 68 | } 69 | 70 | @Entity 71 | class Reservation { 72 | 73 | @Id 74 | @GeneratedValue 75 | private Long id; 76 | private String reservationName; 77 | 78 | Reservation() { // why JPA why? 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "Reservation{" + 84 | "id=" + id + 85 | ", reservationName='" + reservationName + '\'' + 86 | '}'; 87 | } 88 | 89 | public Reservation(String reservationName) { 90 | this.reservationName = reservationName; 91 | } 92 | 93 | public Long getId() { 94 | return id; 95 | } 96 | 97 | public String getReservationName() { 98 | return reservationName; 99 | } 100 | } -------------------------------------------------------------------------------- /bootcamp/src/main/java/bootiful/BootServletInitializer.java: -------------------------------------------------------------------------------- 1 | package bootiful; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.context.web.SpringBootServletInitializer; 5 | 6 | public class BootServletInitializer extends SpringBootServletInitializer { 7 | 8 | @Override 9 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 10 | return application.sources(Application.class); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bootcamp/src/main/java/bootiful/CloudConfiguration.java: -------------------------------------------------------------------------------- 1 | package bootiful; 2 | 3 | import org.springframework.cloud.config.java.AbstractCloudConfig; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Profile; 7 | 8 | import javax.sql.DataSource; 9 | 10 | @Configuration 11 | @Profile("cloud") 12 | public class CloudConfiguration extends AbstractCloudConfig { 13 | 14 | @Bean 15 | public DataSource dataSource() { 16 | return connectionFactory().dataSource(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bootcamp/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port 8000 2 | management.port 9000 3 | management.contextPath /admin 4 | spring.jpa.database POSTGRESQL 5 | spring.jpa.hibernate.ddl-auto: create-drop -------------------------------------------------------------------------------- /bootcamp/src/main/resources/templates/reservations.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Reservations 7 | 8 | 9 | 10 | 11 |

Reservations 12 |

13 |
14 |
15 | ID - 16 | Reservation Name 17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /bootcamp/src/test/java/bootiful/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package bootiful; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.test.context.web.WebAppConfiguration; 7 | import org.springframework.boot.test.SpringApplicationConfiguration; 8 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 | 10 | @RunWith(SpringJUnit4ClassRunner.class) 11 | @SpringApplicationConfiguration(classes = Application.class) 12 | @WebAppConfiguration 13 | public class ApplicationTests { 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /configuration/code/.gitignore: -------------------------------------------------------------------------------- 1 | *iml 2 | target 3 | .idea 4 | .gitignore~ -------------------------------------------------------------------------------- /configuration/code/README.asc: -------------------------------------------------------------------------------- 1 | # Configuring Micorservices 2 | 3 | This lab will look at the various approaches to configuring a Spring based application. 4 | 5 | == Spring Framework 6 | We'll start with the http://spring.io/projects/spring-framework[Spring Framework], which provides the `Environment` abstraction, a concept of _profiles_ and the `@PropertySource` annotation. 7 | 8 | You can see them in action in the `basics` module. 9 | 10 | * the Spring `Environment` abstraction provides a way to let Spring ask questions about their environment. You can see it working in `basics/src/main/java/boot/env/Application.java`. It's a little bit of indirection between a running Spring app and it's environment. It provides a way to read configuration values from the environment and property values from _property sources_. 11 | 12 | == Spring Boot 13 | 14 | Spring Boot provides next-level support for configuration. Spring Boot normalizes external configuration (like `-D` arguments and environment variables) and even goes as far as canonicalizing external configuration properties such that, for example, the shell variable `SERVER_PORT` is the same as `-Dserver.port`, which is the same as putting `server.port` in a `.properties` file. Spring Boot also adds novel support for `.yml` configuration files. Spring Boot will, by convention, look for `src/main/resources/application.properties` and `src/main/resources/application.yml`, too. 15 | 16 | * you can see it working in the `basics/src/main/java/boot/Application.java`. 17 | 18 | == Spring Cloud 19 | Then, we look at how Spring Cloud supports cloud-native applications and operational requirements by supporting journaled, centralized configuration for multiple applications and services through the Spring Cloud config server. The Spring Cloud config server is backed by a http://en.wikipedia.org/wiki/Git_%28software%29[Git]. This promotes traceability and easier updates to configuration since there's no need to re-push an application just to change its configuration. Additionally, Spring Cloud supports _refreshing_ configuration for beans _in-situ_ through the `/refresh` Actuator endpoint and through the Spring Cloud event bus (which uses RabbitMQ as a message bus to propagate configuration changes to all connected applications or services). 20 | 21 | * you can see it working in the `config-client` and `config-server` modules. 22 | * Start the `config-server` first. It will proxy configuration from a Git repository and make it available to other services that have the `org.springframework.cloud:spring-cloud-config-client` on their CLASSPATH. 23 | * you can see that working in the `config-client` where we depend on configuration. 24 | -------------------------------------------------------------------------------- /configuration/code/basics/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | configuration 8 | root 9 | 0.0.1-SNAPSHOT 10 | 11 | basics 12 | jar 13 | 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-actuator 23 | 24 | 25 | 26 | 27 | 28 | boot.Application 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /configuration/code/basics/src/main/java/boot/Application.java: -------------------------------------------------------------------------------- 1 | package boot; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.stereotype.Component; 12 | 13 | 14 | 15 | 16 | // -D and env vars work too! 17 | @SpringBootApplication 18 | public class Application { 19 | 20 | @Autowired 21 | void setConfigurationProjectProperties(ConfigurationProjectProperties cp) { 22 | System.out.println( "configurationProjectProperties.projectName = " + cp.getProjectName()); 23 | } 24 | 25 | public static void main(String[] args) { 26 | SpringApplication.run(Application.class); 27 | } 28 | 29 | } 30 | 31 | @Component 32 | @ConfigurationProperties("configuration") 33 | class ConfigurationProjectProperties { 34 | 35 | private String projectName; 36 | 37 | public String getProjectName() { 38 | return projectName; 39 | } 40 | 41 | public void setProjectName(String projectName) { 42 | this.projectName = projectName; 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /configuration/code/basics/src/main/java/env/Application.java: -------------------------------------------------------------------------------- 1 | package env; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.*; 5 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 6 | import org.springframework.core.env.Environment; 7 | 8 | @Configuration 9 | @ComponentScan 10 | @PropertySource("simple.properties") 11 | public class Application { 12 | 13 | @Bean 14 | public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { 15 | return new PropertySourcesPlaceholderConfigurer(); 16 | } 17 | 18 | @Value("${configuration.projectName}") 19 | void setProjectName(String projectName) { 20 | System.out.println("setProjectName('" + projectName + ");"); 21 | } 22 | 23 | public static void main(String args[]) throws Throwable { 24 | AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Application.class); 25 | Environment environment = ac.getBean(Environment.class); 26 | System.out.println("env=" + environment.getProperty("configuration.projectName")); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /configuration/code/basics/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | configuration: 2 | projectName : Spring Boot 3 | 4 | -------------------------------------------------------------------------------- /configuration/code/basics/src/main/resources/simple.properties: -------------------------------------------------------------------------------- 1 | configuration.projectName = Spring framework -------------------------------------------------------------------------------- /configuration/code/cf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # 5 | # CloudFoundry installer 6 | # 7 | 8 | 9 | CLOUD_DOMAIN=${DOMAIN:-run.pivotal.io} 10 | CLOUD_TARGET=api.${DOMAIN} 11 | 12 | function login(){ 13 | cf api | grep ${CLOUD_TARGET} || cf api ${CLOUD_TARGET} --skip-ssl-validation 14 | cf a | grep OK || cf login 15 | } 16 | 17 | function app_domain(){ 18 | D=`cf apps | grep $1 | tr -s ' ' | cut -d' ' -f 6 | cut -d, -f1` 19 | echo $D 20 | } 21 | 22 | function deploy_app(){ 23 | APP_NAME=$1 24 | cd $APP_NAME 25 | cf push $APP_NAME --no-start 26 | APPLICATION_DOMAIN=`app_domain $APP_NAME` 27 | echo determined that application_domain for $APP_NAME is $APPLICATION_DOMAIN. 28 | cf env $APP_NAME | grep APPLICATION_DOMAIN || cf set-env $APP_NAME APPLICATION_DOMAIN $APPLICATION_DOMAIN 29 | cf restart $APP_NAME 30 | cd .. 31 | } 32 | 33 | function deploy_service(){ 34 | N=$1 35 | D=`app_domain $N` 36 | JSON='{"uri":"http://'$D'"}' 37 | echo cf cups $N -p $JSON 38 | cf cups $N -p $JSON 39 | } 40 | 41 | 42 | function deploy_config_server(){ 43 | NAME=config-server 44 | deploy_app $NAME 45 | deploy_service $NAME 46 | } 47 | 48 | function deploy_config_client(){ 49 | NAME=config-client 50 | deploy_app $NAME 51 | } 52 | 53 | # 54 | # function deploy_doge(){ 55 | # cf cs mongolab sandbox doge-mongo 56 | # deploy_app doge-service 57 | # } 58 | # 59 | # function deploy_account(){ 60 | # cf cs elephantsql turtle doge-postgresql 61 | # deploy_app account-service 62 | # } 63 | # 64 | # function deploy_hystrix(){ 65 | # deploy_app hystrix-service 66 | # } 67 | # 68 | # function deploy_webapp(){ 69 | # deploy_app webapp 70 | # } 71 | 72 | function reset(){ 73 | cf d config-server 74 | #cf d doge-service 75 | cf ds config-server 76 | cf delete-orphaned-routes 77 | } 78 | 79 | 80 | ### INSTALLATION STEPS 81 | ### feel free to comment out all the steps that you don't need 82 | ### and selectively uncomment them if the script in total encounters 83 | ### IO errors and such. 84 | 85 | mvn -DskipTests=true clean install 86 | 87 | #login 88 | #reset 89 | #deploy_config_server 90 | deploy_config_client 91 | -------------------------------------------------------------------------------- /configuration/code/config-client/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | ### Maven template 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | release.properties 9 | -------------------------------------------------------------------------------- /configuration/code/config-client/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: config-client 4 | memory: 512M 5 | instances: 1 6 | host: config-client-${random-word} 7 | domain: cfapps.io 8 | path: target/config-client.jar 9 | services: 10 | - config-server 11 | env: 12 | SPRING_PROFILES_ACTIVE: cloud 13 | DEBUG: "true" 14 | debug: "true" 15 | -------------------------------------------------------------------------------- /configuration/code/config-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | configuration 8 | root 9 | 0.0.1-SNAPSHOT 10 | 11 | config-client 12 | jar 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-config-client 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-web 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-actuator 26 | 27 | 28 | 29 | 30 | 31 | cloud.client.Application 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /configuration/code/config-client/src/main/java/cloud/client/Application.java: -------------------------------------------------------------------------------- 1 | package cloud.client; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.context.config.annotation.RefreshScope; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @SpringBootApplication 11 | @RestController 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | } 18 | 19 | @RefreshScope 20 | @RestController 21 | class ProjectRestController { 22 | 23 | @Value("${configuration.projectName}") 24 | private String projectName; 25 | 26 | @RequestMapping("/project-name") 27 | String projectName() { 28 | return this.projectName; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /configuration/code/config-client/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: config-client 4 | cloud: 5 | config: 6 | uri: ${vcap.services.config-server.credentials.uri:http://localhost:8888} 7 | -------------------------------------------------------------------------------- /configuration/code/config-server/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: config-server 4 | memory: 512M 5 | instances: 1 6 | # host: config-service 7 | host: config-server-${random-word} 8 | domain: cfapps.io 9 | path: target/config-server.jar 10 | env: 11 | SPRING_PROFILES_ACTIVE: cloud 12 | DEBUG: "true" 13 | debug: "true" 14 | -------------------------------------------------------------------------------- /configuration/code/config-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | configuration 8 | root 9 | 0.0.1-SNAPSHOT 10 | 11 | config-server 12 | jar 13 | 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-config-server 19 | 20 | 21 | 22 | 23 | cloud.server.Application 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /configuration/code/config-server/src/main/java/cloud/server/Application.java: -------------------------------------------------------------------------------- 1 | package cloud.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.config.server.EnableConfigServer; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @SpringBootApplication 11 | @EnableConfigServer 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /configuration/code/config-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8888 3 | 4 | conf : /Users/jlong/Desktop/microservices-lab-configuration 5 | 6 | spring: 7 | cloud: 8 | config: 9 | server: 10 | git : 11 | uri: ${conf:https://github.com/joshlong/microservices-lab-configuration} 12 | -------------------------------------------------------------------------------- /configuration/code/config-server/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: cloud-server 4 | -------------------------------------------------------------------------------- /configuration/code/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 1.2.0.RC2 10 | 11 | 12 | configuration 13 | root 14 | 0.0.1-SNAPSHOT 15 | pom 16 | 17 | 18 | basics 19 | config-client 20 | config-server 21 | 22 | 23 | 24 | UTF-8 25 | 1.8 26 | 27 | 28 | 29 | 30 | ${project.artifactId} 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-maven-plugin 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-parent 45 | 1.0.0.M3 46 | pom 47 | import 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | spring-snapshots 56 | Spring Snapshots 57 | http://repo.spring.io/libs-snapshot-local 58 | 59 | true 60 | 61 | 62 | 63 | spring-milestones 64 | Spring Milestones 65 | http://repo.spring.io/libs-milestone-local 66 | 67 | false 68 | 69 | 70 | 71 | spring-releases 72 | Spring Releases 73 | http://repo.spring.io/libs-release-local 74 | 75 | false 76 | 77 | 78 | 79 | 80 | 81 | 82 | spring-snapshots 83 | Spring Snapshots 84 | http://repo.spring.io/libs-snapshot-local 85 | 86 | true 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | http://repo.spring.io/libs-milestone-local 93 | 94 | false 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /configuration/deck.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-lab/03153fee15b7d13691b031ba4af2ad95700cb25d/configuration/deck.key -------------------------------------------------------------------------------- /polyglot-persistence/catalog/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: ms-catalog-service 4 | memory: 512M 5 | instances: 1 6 | path: target/microservices-catalog-0.0.1-SNAPSHOT.jar 7 | env: 8 | SPRING_PROFILES_ACTIVE: cloud 9 | services: 10 | - catalog-db 11 | - eureka-service 12 | 13 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-catalog 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-catalog 12 | Movie Catalog Microservice 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-actuator 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | org.springframework.cloud 41 | spring-cloud-cloudfoundry-connector 42 | 1.1.0.RELEASE 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-spring-service-connector 47 | 1.1.0.RELEASE 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-eureka 52 | 53 | 54 | org.hsqldb 55 | hsqldb 56 | 57 | 58 | postgresql 59 | postgresql 60 | 9.1-901-1.jdbc4 61 | 62 | 63 | 64 | 65 | UTF-8 66 | io.pivotal.microservices.catalog.Application 67 | 1.8 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.springframework.cloud 83 | spring-cloud-starter-parent 84 | 1.0.0.M3 85 | pom 86 | import 87 | 88 | 89 | 90 | 91 | 92 | 93 | spring-snapshots 94 | Spring Snapshots 95 | https://repo.spring.io/snapshot 96 | 97 | true 98 | 99 | 100 | 101 | spring-milestones 102 | Spring Milestones 103 | https://repo.spring.io/milestone 104 | 105 | false 106 | 107 | 108 | 109 | 110 | 111 | spring-snapshots 112 | Spring Snapshots 113 | https://repo.spring.io/snapshot 114 | 115 | true 116 | 117 | 118 | 119 | spring-milestones 120 | Spring Milestones 121 | https://repo.spring.io/milestone 122 | 123 | false 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/java/io/pivotal/microservices/catalog/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog; 2 | 3 | import com.netflix.discovery.converters.Auto; 4 | import io.pivotal.microservices.catalog.models.Genre; 5 | import io.pivotal.microservices.catalog.models.Movie; 6 | import io.pivotal.microservices.catalog.repositories.GenreRepository; 7 | import io.pivotal.microservices.catalog.repositories.MovieRepository; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 11 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 12 | import org.springframework.context.annotation.ComponentScan; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 15 | import org.springframework.web.bind.annotation.PathVariable; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | @Configuration 21 | @ComponentScan 22 | @EnableAutoConfiguration 23 | @EnableJpaRepositories 24 | @EnableEurekaClient 25 | @RestController 26 | public class Application { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(Application.class, args); 30 | } 31 | 32 | @Autowired 33 | MovieRepository movieRepository; 34 | 35 | @Autowired 36 | GenreRepository genreRepository; 37 | 38 | @RequestMapping(value = "/catalog/movies", method = RequestMethod.GET) 39 | public Iterable movies() { 40 | return movieRepository.findAll(); 41 | } 42 | 43 | @RequestMapping(value = "/catalog/movies/{mlId}", method = RequestMethod.GET) 44 | public Movie movie(@PathVariable String mlId) { 45 | return movieRepository.findByMlId(mlId); 46 | } 47 | 48 | @RequestMapping(value = "/catalog/genres", method = RequestMethod.GET) 49 | public Iterable genres() { 50 | return genreRepository.findAll(); 51 | } 52 | 53 | @RequestMapping(value = "/catalog/genres/{mlId}", method = RequestMethod.GET) 54 | public Genre genre(@PathVariable String mlId) { 55 | return genreRepository.findByMlId(mlId); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/java/io/pivotal/microservices/catalog/config/CloudConfig.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog.config; 2 | 3 | import org.springframework.cloud.config.java.AbstractCloudConfig; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Profile; 7 | 8 | import javax.sql.DataSource; 9 | 10 | @Configuration 11 | @Profile("cloud") 12 | public class CloudConfig extends AbstractCloudConfig { 13 | 14 | @Bean 15 | public DataSource dataSource() { 16 | return connectionFactory().dataSource(); 17 | } 18 | } -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/java/io/pivotal/microservices/catalog/models/Genre.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | 6 | import javax.persistence.*; 7 | import java.util.List; 8 | 9 | @Entity 10 | @Table(name = "genres") 11 | public class Genre { 12 | 13 | @Id 14 | @GeneratedValue 15 | private Long id; 16 | 17 | @Column(nullable = false) 18 | private String mlId; 19 | 20 | @Column(nullable = false) 21 | private String name; 22 | 23 | @ManyToMany(mappedBy = "genres") 24 | @JsonIgnore 25 | private List movies; 26 | 27 | public Long getId() { 28 | return id; 29 | } 30 | 31 | public void setId(Long id) { 32 | this.id = id; 33 | } 34 | 35 | public String getMlId() { 36 | return mlId; 37 | } 38 | 39 | public void setMlId(String mlId) { 40 | this.mlId = mlId; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public List getMovies() { 52 | return movies; 53 | } 54 | 55 | public void setMovies(List movies) { 56 | this.movies = movies; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/java/io/pivotal/microservices/catalog/models/Movie.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog.models; 2 | 3 | import javax.persistence.*; 4 | import java.util.List; 5 | 6 | @Entity 7 | @Table(name = "movies") 8 | public class Movie { 9 | 10 | @Id 11 | @GeneratedValue 12 | private Long id; 13 | 14 | @Column(nullable = false) 15 | private String title; 16 | 17 | @Column(nullable = false) 18 | private String mlId; 19 | 20 | @ManyToMany(cascade = CascadeType.ALL) 21 | @JoinTable(name = "movies_genres", 22 | joinColumns = {@JoinColumn(name="movie_id", referencedColumnName = "id")}, 23 | inverseJoinColumns = {@JoinColumn(name="genre_id", referencedColumnName = "id")}) 24 | private List genres; 25 | 26 | @Column(nullable = false) 27 | private int numberInStock; 28 | 29 | public Long getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Long id) { 34 | this.id = id; 35 | } 36 | 37 | public String getTitle() { 38 | return title; 39 | } 40 | 41 | public void setTitle(String title) { 42 | this.title = title; 43 | } 44 | 45 | public List getGenres() { 46 | return genres; 47 | } 48 | 49 | public void setGenres(List genres) { 50 | this.genres = genres; 51 | } 52 | 53 | public String getMlId() { 54 | return mlId; 55 | } 56 | 57 | public void setMlId(String mlId) { 58 | this.mlId = mlId; 59 | } 60 | 61 | public int getNumberInStock() { 62 | return numberInStock; 63 | } 64 | 65 | public void setNumberInStock(int numberInStock) { 66 | this.numberInStock = numberInStock; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/java/io/pivotal/microservices/catalog/repositories/GenreRepository.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog.repositories; 2 | 3 | import io.pivotal.microservices.catalog.models.Genre; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | public interface GenreRepository extends CrudRepository { 7 | Genre findByMlId(String mlId); 8 | } 9 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/java/io/pivotal/microservices/catalog/repositories/MovieRepository.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog.repositories; 2 | 3 | import io.pivotal.microservices.catalog.models.Movie; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | public interface MovieRepository extends CrudRepository { 7 | public Movie findByMlId(String mlId); 8 | } 9 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | jpa: 3 | hibernate: 4 | ddl-auto: create-drop 5 | --- 6 | 7 | spring: 8 | profiles: default 9 | jpa: 10 | database: HSQL 11 | 12 | --- 13 | 14 | spring: 15 | profiles: cloud 16 | jpa: 17 | database: POSTGRESQL 18 | eureka: 19 | instance: 20 | hostname: ms-catalog-service.cfapps.io 21 | nonSecurePort: 80 -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: catalog-service 4 | 5 | eureka: 6 | client: 7 | serviceUrl: 8 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 9 | -------------------------------------------------------------------------------- /polyglot-persistence/catalog/src/test/java/io/pivotal/microservices/catalog/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.catalog; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.test.context.web.WebAppConfiguration; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = Application.class) 11 | @WebAppConfiguration 12 | public class ApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: ms-recommendations-service 4 | memory: 512M 5 | instances: 1 6 | path: target/microservices-recommendations-0.0.1-SNAPSHOT.jar 7 | env: 8 | SPRING_PROFILES_ACTIVE: cloud 9 | services: 10 | - recommendations-db 11 | - eureka-service 12 | 13 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-recommendations 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-recommendations 12 | Movie Recommendations Microservice 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | org.springframework.data 28 | spring-data-neo4j-rest 29 | 3.2.0.RELEASE 30 | 31 | 32 | org.hibernate 33 | hibernate-validator 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-cloudfoundry-connector 38 | 1.1.0.RELEASE 39 | 40 | 41 | org.springframework.cloud 42 | spring-cloud-spring-service-connector 43 | 1.1.0.RELEASE 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-eureka 48 | 49 | 50 | 51 | 52 | UTF-8 53 | io.pivotal.microservices.recommendations.Application 54 | 1.8 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.springframework.cloud 70 | spring-cloud-starter-parent 71 | 1.0.0.M3 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | spring-snapshots 81 | Spring Snapshots 82 | https://repo.spring.io/snapshot 83 | 84 | true 85 | 86 | 87 | 88 | spring-milestones 89 | Spring Milestones 90 | https://repo.spring.io/milestone 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | spring-snapshots 99 | Spring Snapshots 100 | https://repo.spring.io/snapshot 101 | 102 | true 103 | 104 | 105 | 106 | spring-milestones 107 | Spring Milestones 108 | https://repo.spring.io/milestone 109 | 110 | false 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/scripts/createGrapheneService.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | URI="your graphenedb uri" 3 | USERNAME="your graphenedb username" 4 | PASSWORD="your graphenedb password" 5 | cf cups recommendations-dba -p '{"neo4jUri":"${URI}","neo4jUsername":"${USERNAME}","neo4jPassword":"${PASSWORD}"}' -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/scripts/loadGraph.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./loadPeople.sh 3 | ./loadMovies.sh 4 | ./loadLikes.sh -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/scripts/loadLikes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ROUTE=${ROUTE:-localhost:8082} 3 | curl -X POST ${ROUTE}/recommendations/mstine/likes/1 4 | curl -X POST ${ROUTE}/recommendations/mstine/likes/2 5 | curl -X POST ${ROUTE}/recommendations/starbuxman/likes/2 6 | curl -X POST ${ROUTE}/recommendations/starbuxman/likes/4 7 | curl -X POST ${ROUTE}/recommendations/starbuxman/likes/5 8 | curl -X POST ${ROUTE}/recommendations/littleidea/likes/2 9 | curl -X POST ${ROUTE}/recommendations/littleidea/likes/3 10 | curl -X POST ${ROUTE}/recommendations/littleidea/likes/5 11 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/scripts/loadMovies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ROUTE=${ROUTE:-localhost:8082} 3 | curl ${ROUTE}/recommendations/movies -X POST -d '{"mlId":"1","title":"Toy Story (1995)"}' -H "Content-Type: application/json" 4 | curl ${ROUTE}/recommendations/movies -X POST -d '{"mlId":"2","title":"GoldenEye (1995)"}' -H "Content-Type: application/json" 5 | curl ${ROUTE}/recommendations/movies -X POST -d '{"mlId":"3","title":"Four Rooms (1995)"}' -H "Content-Type: application/json" 6 | curl ${ROUTE}/recommendations/movies -X POST -d '{"mlId":"4","title":"Get Shorty (1995)"}' -H "Content-Type: application/json" 7 | curl ${ROUTE}/recommendations/movies -X POST -d '{"mlId":"5","title":"Copycat (1995)"}' -H "Content-Type: application/json" -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/scripts/loadPeople.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ROUTE=${ROUTE:-localhost:8082} 3 | curl ${ROUTE}/recommendations/people -X POST -d '{"userName":"mstine","firstName":"Matt","lastName":"Stine"}' -H "Content-Type: application/json" 4 | curl ${ROUTE}/recommendations/people -X POST -d '{"userName":"starbuxman","firstName":"Josh","lastName":"Long"}' -H "Content-Type: application/json" 5 | curl ${ROUTE}/recommendations/people -X POST -d '{"userName":"littleidea","firstName":"Andrew","lastName":"Shafer"}' -H "Content-Type: application/json" -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations; 2 | 3 | import io.pivotal.microservices.recommendations.repositories.LikesRepository; 4 | import io.pivotal.microservices.recommendations.repositories.MovieRepository; 5 | import io.pivotal.microservices.recommendations.repositories.PersonRepository; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.CommandLineRunner; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 10 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 11 | import org.springframework.context.annotation.ComponentScan; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.data.neo4j.config.EnableNeo4jRepositories; 14 | 15 | @Configuration 16 | @ComponentScan 17 | @EnableAutoConfiguration 18 | @EnableNeo4jRepositories(basePackages = "io.pivotal.microservices.recommendations.repositories") 19 | @EnableEurekaClient 20 | public class Application implements CommandLineRunner { 21 | public static void main(String[] args) { 22 | SpringApplication.run(Application.class, args); 23 | } 24 | 25 | @Autowired 26 | MovieRepository movieRepository; 27 | @Autowired 28 | PersonRepository personRepository; 29 | @Autowired 30 | LikesRepository likesRepository; 31 | 32 | @Override 33 | public void run(String... strings) throws Exception { 34 | movieRepository.deleteAll(); 35 | personRepository.deleteAll(); 36 | likesRepository.deleteAll(); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/config/CloudConfig.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.config; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.springframework.cloud.config.java.AbstractCloudConfig; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Profile; 8 | 9 | @Configuration 10 | @Profile("cloud") 11 | public class CloudConfig extends AbstractCloudConfig { 12 | 13 | @Bean 14 | GraphDatabaseService graphDatabaseService() { 15 | return connectionFactory().service(GraphDatabaseService.class); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/config/LocalConfig.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.config; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Profile; 8 | import org.springframework.data.neo4j.rest.SpringRestGraphDatabase; 9 | 10 | @Configuration 11 | @Profile("default") 12 | public class LocalConfig { 13 | 14 | @Bean 15 | public GraphDatabaseService graphDatabaseService() { 16 | return new SpringRestGraphDatabase("http://movies.sb02.stations.graphenedb.com:24789/db/data/", 17 | "movies","jj5hGwRfAhaKJjSluJge"); 18 | // return new GraphDatabaseFactory().newEmbeddedDatabase("/tmp/recommendations"); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/config/Neo4jConfig.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.data.neo4j.config.Neo4jConfiguration; 6 | import org.springframework.transaction.PlatformTransactionManager; 7 | import org.springframework.transaction.support.TransactionTemplate; 8 | 9 | @Configuration 10 | public class Neo4jConfig extends Neo4jConfiguration { 11 | public Neo4jConfig() { 12 | setBasePackage("io.pivotal.microservices.recommendations.model"); 13 | } 14 | 15 | @Bean 16 | public TransactionTemplate transactionTemplate(PlatformTransactionManager ptm) { 17 | return new TransactionTemplate(ptm); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/controllers/ApiController.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.controllers; 2 | 3 | import io.pivotal.microservices.recommendations.model.Likes; 4 | import io.pivotal.microservices.recommendations.model.Movie; 5 | import io.pivotal.microservices.recommendations.model.Person; 6 | import io.pivotal.microservices.recommendations.repositories.LikesRepository; 7 | import io.pivotal.microservices.recommendations.repositories.MovieRepository; 8 | import io.pivotal.microservices.recommendations.repositories.PersonRepository; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | @RestController 15 | public class ApiController { 16 | 17 | @Autowired 18 | MovieRepository movieRepository; 19 | @Autowired 20 | PersonRepository personRepository; 21 | @Autowired 22 | LikesRepository likesRepository; 23 | 24 | @RequestMapping(value = "/recommendations/movies", method = RequestMethod.GET) 25 | public Iterable movies() { 26 | return movieRepository.findAll(); 27 | } 28 | 29 | @RequestMapping(value = "/recommendations/movies", method = RequestMethod.POST) 30 | public ResponseEntity createMovie(@RequestBody Movie movie) { 31 | movieRepository.save(movie); 32 | return new ResponseEntity<>(movie, HttpStatus.CREATED); 33 | } 34 | 35 | @RequestMapping(value = "/recommendations/people", method = RequestMethod.GET) 36 | public Iterable people() { 37 | return personRepository.findAll(); 38 | } 39 | 40 | @RequestMapping(value = "/recommendations/people", method = RequestMethod.POST) 41 | public ResponseEntity createPerson(@RequestBody Person person) { 42 | personRepository.save(person); 43 | return new ResponseEntity<>(person, HttpStatus.CREATED); 44 | } 45 | 46 | @RequestMapping(value = "/recommendations/likes", method = RequestMethod.GET) 47 | public Iterable likes() { 48 | return likesRepository.findAll(); 49 | } 50 | 51 | @RequestMapping(value = "/recommendations/{userName}/likes/{mlId}", method = RequestMethod.POST) 52 | public ResponseEntity createPersonMovieLink(@PathVariable String userName, 53 | @PathVariable String mlId) { 54 | Person person = personRepository.findByUserName(userName); 55 | Movie movie = movieRepository.findByMlId(mlId); 56 | 57 | Likes likes = new Likes(); 58 | likes.setPerson(person); 59 | likes.setMovie(movie); 60 | likesRepository.save(likes); 61 | 62 | return new ResponseEntity<>(likes, HttpStatus.CREATED); 63 | } 64 | 65 | @RequestMapping(value = "/recommendations/forUser/{userName}", method = RequestMethod.GET) 66 | public Iterable recommendedMoviesForUser(@PathVariable String userName) { 67 | return movieRepository.recommendedMoviesFor(userName); 68 | } 69 | 70 | @RequestMapping(value = "/recommendations/forMovie/{mlId}", method = RequestMethod.GET) 71 | public Iterable recommendedMoviesForMovie(@PathVariable String mlId) { 72 | return movieRepository.moviesLikedByPeopleWhoLiked(mlId); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/model/Likes.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.model; 2 | 3 | import org.springframework.data.neo4j.annotation.EndNode; 4 | import org.springframework.data.neo4j.annotation.GraphId; 5 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 6 | import org.springframework.data.neo4j.annotation.StartNode; 7 | 8 | @RelationshipEntity(type = "LIKES") 9 | public class Likes { 10 | 11 | @GraphId 12 | private Long id; 13 | 14 | @StartNode 15 | private Person person; 16 | 17 | @EndNode 18 | private Movie movie; 19 | 20 | public Long getId() { 21 | return id; 22 | } 23 | 24 | public void setId(Long id) { 25 | this.id = id; 26 | } 27 | 28 | public Person getPerson() { 29 | return person; 30 | } 31 | 32 | public void setPerson(Person person) { 33 | this.person = person; 34 | } 35 | 36 | public Movie getMovie() { 37 | return movie; 38 | } 39 | 40 | public void setMovie(Movie movie) { 41 | this.movie = movie; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Likes{" + 47 | "id=" + id + 48 | ", person=" + person + 49 | ", movie=" + movie + 50 | '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/model/Movie.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.model; 2 | 3 | import org.springframework.data.neo4j.annotation.GraphId; 4 | import org.springframework.data.neo4j.annotation.Indexed; 5 | import org.springframework.data.neo4j.annotation.NodeEntity; 6 | 7 | @NodeEntity 8 | public class Movie { 9 | 10 | @GraphId 11 | private Long id; 12 | 13 | @Override 14 | public String toString() { 15 | return "Movie{" + 16 | "id=" + id + 17 | ", mlId='" + mlId + '\'' + 18 | ", title='" + title + '\'' + 19 | '}'; 20 | } 21 | 22 | @Indexed(unique = true) 23 | private String mlId; 24 | private String title; 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public String getMlId() { 35 | return mlId; 36 | } 37 | 38 | public void setMlId(String mlId) { 39 | this.mlId = mlId; 40 | } 41 | 42 | public String getTitle() { 43 | return title; 44 | } 45 | 46 | public void setTitle(String title) { 47 | this.title = title; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/model/Person.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.model; 2 | 3 | import org.springframework.data.neo4j.annotation.GraphId; 4 | import org.springframework.data.neo4j.annotation.NodeEntity; 5 | 6 | @NodeEntity 7 | public class Person { 8 | 9 | @GraphId 10 | private Long id; 11 | private String userName; 12 | private String firstName; 13 | private String lastName; 14 | 15 | @Override 16 | public String toString() { 17 | return "Person{" + 18 | "id=" + id + 19 | ", userName='" + userName + '\'' + 20 | ", firstName='" + firstName + '\'' + 21 | ", lastName='" + lastName + '\'' + 22 | '}'; 23 | } 24 | 25 | public Long getId() { 26 | return id; 27 | } 28 | 29 | public void setId(Long id) { 30 | this.id = id; 31 | } 32 | 33 | public String getUserName() { 34 | return userName; 35 | } 36 | 37 | public void setUserName(String userName) { 38 | this.userName = userName; 39 | } 40 | 41 | public String getFirstName() { 42 | return firstName; 43 | } 44 | 45 | public void setFirstName(String firstName) { 46 | this.firstName = firstName; 47 | } 48 | 49 | public String getLastName() { 50 | return lastName; 51 | } 52 | 53 | public void setLastName(String lastName) { 54 | this.lastName = lastName; 55 | } 56 | } -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/repositories/LikesRepository.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.repositories; 2 | 3 | import io.pivotal.microservices.recommendations.model.Likes; 4 | import org.springframework.data.neo4j.repository.GraphRepository; 5 | 6 | public interface LikesRepository extends GraphRepository { 7 | } 8 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/repositories/MovieRepository.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.repositories; 2 | 3 | import io.pivotal.microservices.recommendations.model.Movie; 4 | import org.springframework.data.neo4j.annotation.Query; 5 | import org.springframework.data.neo4j.repository.GraphRepository; 6 | 7 | public interface MovieRepository extends GraphRepository { 8 | Movie findByMlId(String mlId); 9 | 10 | @Query("MATCH (p:Person) WHERE p.userName = {0} MATCH p-[:LIKES]->movie<-[:LIKES]-slm-[:LIKES]->recommendations " + 11 | "WHERE not(p = slm) and not (p--recommendations) return recommendations") 12 | Iterable recommendedMoviesFor(String userName); 13 | 14 | @Query("MATCH (movie:Movie) WHERE movie.mlId = {0} MATCH movie<-[:LIKES]-slm-[:LIKES]->recommendations " + 15 | "RETURN distinct recommendations") 16 | Iterable moviesLikedByPeopleWhoLiked(String mlId); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/io/pivotal/microservices/recommendations/repositories/PersonRepository.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.recommendations.repositories; 2 | 3 | import io.pivotal.microservices.recommendations.model.Person; 4 | import org.springframework.data.neo4j.repository.GraphRepository; 5 | 6 | public interface PersonRepository extends GraphRepository { 7 | Person findByUserName(String userName); 8 | } 9 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/org/springframework/cloud/neo4j/GraphDatabaseServiceConnectorCreator.java: -------------------------------------------------------------------------------- 1 | package org.springframework.cloud.neo4j; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.springframework.cloud.service.AbstractServiceConnectorCreator; 5 | import org.springframework.cloud.service.ServiceConnectorConfig; 6 | import org.springframework.data.neo4j.rest.SpringRestGraphDatabase; 7 | 8 | public class GraphDatabaseServiceConnectorCreator extends AbstractServiceConnectorCreator { 9 | @Override 10 | public GraphDatabaseService create(GraphDatabaseServiceInfo neo4JServiceInfo, ServiceConnectorConfig serviceConnectorConfig) { 11 | return new SpringRestGraphDatabase(neo4JServiceInfo.getUri(), 12 | neo4JServiceInfo.getUsername(), 13 | neo4JServiceInfo.getPassword()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/org/springframework/cloud/neo4j/GraphDatabaseServiceInfo.java: -------------------------------------------------------------------------------- 1 | package org.springframework.cloud.neo4j; 2 | 3 | import org.springframework.cloud.service.BaseServiceInfo; 4 | 5 | public class GraphDatabaseServiceInfo extends BaseServiceInfo { 6 | 7 | private String uri; 8 | private String username; 9 | private String password; 10 | 11 | public GraphDatabaseServiceInfo(String id, String uri, String username, String password) { 12 | super(id); 13 | this.uri = uri; 14 | this.username = username; 15 | this.password = password; 16 | } 17 | 18 | @ServiceProperty 19 | public String getUri() { 20 | return uri; 21 | } 22 | 23 | @ServiceProperty 24 | public String getUsername() { 25 | return username; 26 | } 27 | 28 | @ServiceProperty 29 | public String getPassword() { 30 | return password; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/java/org/springframework/cloud/neo4j/GraphDatabaseServiceInfoCreator.java: -------------------------------------------------------------------------------- 1 | package org.springframework.cloud.neo4j; 2 | 3 | import org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator; 4 | import org.springframework.cloud.cloudfoundry.Tags; 5 | 6 | import java.util.Map; 7 | 8 | public class GraphDatabaseServiceInfoCreator extends CloudFoundryServiceInfoCreator { 9 | public GraphDatabaseServiceInfoCreator() { 10 | super(new Tags()); 11 | } 12 | 13 | @Override 14 | public boolean accept(Map serviceData) { 15 | @SuppressWarnings("unchecked") 16 | Map credentials = (Map) serviceData.get("credentials"); 17 | 18 | String uri = getStringFromCredentials(credentials, "neo4jUri"); 19 | String username = getStringFromCredentials(credentials, "neo4jUsername"); 20 | String password = getStringFromCredentials(credentials, "neo4jPassword"); 21 | 22 | return uri != null && !uri.isEmpty() && 23 | username != null && !username.isEmpty() && 24 | password != null && !password.isEmpty(); 25 | } 26 | 27 | @Override 28 | public GraphDatabaseServiceInfo createServiceInfo(Map serviceData) { 29 | @SuppressWarnings("unchecked") 30 | Map credentials = (Map) serviceData.get("credentials"); 31 | 32 | String id = (String) serviceData.get("name"); 33 | String uri = getStringFromCredentials(credentials, "neo4jUri"); 34 | String username = getStringFromCredentials(credentials, "neo4jUsername"); 35 | String password = getStringFromCredentials(credentials, "neo4jPassword"); 36 | 37 | return new GraphDatabaseServiceInfo(id, uri, username, password); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/resources/META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator: -------------------------------------------------------------------------------- 1 | org.springframework.cloud.neo4j.GraphDatabaseServiceInfoCreator -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/resources/META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator: -------------------------------------------------------------------------------- 1 | org.springframework.cloud.neo4j.GraphDatabaseServiceConnectorCreator -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | --- 5 | 6 | spring: 7 | profiles: cloud 8 | eureka: 9 | instance: 10 | hostname: ms-recommendations-service.cfapps.io 11 | nonSecurePort: 80 -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: recommendations-service 4 | eureka: 5 | client: 6 | serviceUrl: 7 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 8 | -------------------------------------------------------------------------------- /polyglot-persistence/recommendations/test: -------------------------------------------------------------------------------- 1 | {"user-provided":[{"name":"graphene","label":"user-provided","tags":[],"credentials":{"neo4jPassword":"wZ05wKOKdVMfcAH9gABi","neo4jUri":"http://matttest.sb02.stations.graphenedb.com:24789/db/data/","neo4jUsername":"matt_test"}}]} 2 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: ms-reviews-service 4 | memory: 512M 5 | instances: 1 6 | path: target/microservices-reviews-0.0.1-SNAPSHOT.jar 7 | env: 8 | SPRING_PROFILES_ACTIVE: cloud 9 | services: 10 | - reviews-db 11 | - eureka-service 12 | 13 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.pivotal.microservices 7 | microservices-reviews 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | microservices-rewviews 12 | Movie Recommendations Microservice 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.0.RC2 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-mongodb 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-actuator 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | org.springframework.cloud 41 | spring-cloud-cloudfoundry-connector 42 | 1.1.0.RELEASE 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-spring-service-connector 47 | 1.1.0.RELEASE 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-eureka 52 | 53 | 54 | 55 | 56 | UTF-8 57 | io.pivotal.microservices.reviews.Application 58 | 1.8 59 | 60 | 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.cloud 74 | spring-cloud-starter-parent 75 | 1.0.0.M3 76 | pom 77 | import 78 | 79 | 80 | 81 | 82 | 83 | 84 | spring-snapshots 85 | Spring Snapshots 86 | https://repo.spring.io/snapshot 87 | 88 | true 89 | 90 | 91 | 92 | spring-milestones 93 | Spring Milestones 94 | https://repo.spring.io/milestone 95 | 96 | false 97 | 98 | 99 | 100 | 101 | 102 | spring-snapshots 103 | Spring Snapshots 104 | https://repo.spring.io/snapshot 105 | 106 | true 107 | 108 | 109 | 110 | spring-milestones 111 | Spring Milestones 112 | https://repo.spring.io/milestone 113 | 114 | false 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/scripts/loadReviews.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ROUTE=${ROUTE:-localhost:8081} 3 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"mstine","mlId":"1","title":"Toy Story (1995)","review":"Great movie!","rating":"5"}' -H "Content-Type: application/json" 4 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"mstine","mlId":"2","title":"GoldenEye (1995)","review":"Pretty good...","rating":"3"}' -H "Content-Type: application/json" 5 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"starbuxman","mlId":"2","title":"GoldenEye (1995)","review":"BOND BOND BOND!","rating":"5"}' -H "Content-Type: application/json" 6 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"starbuxman","mlId":"4","title":"Get Shorty (1995)","review":"Meh","rating":"3" }}' -H "Content-Type: application/json" 7 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"starbuxman","mlId":"5","title":"Copycat (1995)","review":"Nicely done!","rating":"4"}' -H "Content-Type: application/json" 8 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"littleidea","mlId":"2","title":"GoldenEye (1995)","review":"Good show!","rating":"4"}' -H "Content-Type: application/json" 9 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"littleidea","mlId":"3","title":"Four Rooms (1995)","review":"Could have been better...","rating":"3"}' -H "Content-Type: application/json" 10 | curl ${ROUTE}/reviews/reviews -X POST -d '{"userName":"littleidea","mlId":"5","title":"Copycat (1995)","review":"Nicely done!","rating":"4"}' -H "Content-Type: application/json" -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/main/java/io/pivotal/microservices/reviews/Application.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.reviews; 2 | 3 | import io.pivotal.microservices.reviews.models.Review; 4 | import io.pivotal.microservices.reviews.repositories.ReviewRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 9 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 10 | import org.springframework.context.annotation.ComponentScan; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | @Configuration 18 | @ComponentScan 19 | @EnableAutoConfiguration 20 | @EnableMongoRepositories 21 | @EnableEurekaClient 22 | @RestController 23 | public class Application implements CommandLineRunner { 24 | 25 | @Autowired 26 | ReviewRepository reviewRepository; 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(Application.class, args); 30 | } 31 | 32 | @Override 33 | public void run(String... strings) throws Exception { 34 | reviewRepository.deleteAll(); 35 | } 36 | 37 | @RequestMapping(value = "/reviews/reviews", method = RequestMethod.GET) 38 | public Iterable reviews() { 39 | return reviewRepository.findAll(); 40 | } 41 | 42 | @RequestMapping(value = "/reviews/reviews/{mlId}", method = RequestMethod.GET) 43 | public Iterable reviews(@PathVariable String mlId) { 44 | return reviewRepository.findByMlId(mlId); 45 | } 46 | 47 | @RequestMapping(value = "/reviews/reviews", method = RequestMethod.POST) 48 | public ResponseEntity createReview(@RequestBody Review review) { 49 | reviewRepository.save(review); 50 | return new ResponseEntity<>(review, HttpStatus.CREATED); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/main/java/io/pivotal/microservices/reviews/config/CloudConfig.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.reviews.config; 2 | 3 | import org.springframework.cloud.config.java.AbstractCloudConfig; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Profile; 7 | import org.springframework.data.mongodb.MongoDbFactory; 8 | 9 | @Configuration 10 | @Profile("cloud") 11 | public class CloudConfig extends AbstractCloudConfig { 12 | 13 | @Bean 14 | public MongoDbFactory mongoDbFactory() { 15 | return connectionFactory().mongoDbFactory(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/main/java/io/pivotal/microservices/reviews/models/Review.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.reviews.models; 2 | 3 | import org.springframework.data.annotation.Id; 4 | import org.springframework.data.mongodb.core.index.Indexed; 5 | 6 | public class Review { 7 | @Id 8 | private String id; 9 | 10 | @Indexed 11 | private String mlId; 12 | 13 | @Indexed 14 | private String userName; 15 | 16 | private String title; 17 | 18 | private String review; 19 | 20 | private int rating; 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public void setId(String id) { 27 | this.id = id; 28 | } 29 | 30 | public String getMlId() { 31 | return mlId; 32 | } 33 | 34 | public void setMlId(String mlId) { 35 | this.mlId = mlId; 36 | } 37 | 38 | public String getUserName() { 39 | return userName; 40 | } 41 | 42 | public void setUserName(String userName) { 43 | this.userName = userName; 44 | } 45 | 46 | public String getTitle() { 47 | return title; 48 | } 49 | 50 | public void setTitle(String title) { 51 | this.title = title; 52 | } 53 | 54 | public String getReview() { 55 | return review; 56 | } 57 | 58 | public void setReview(String review) { 59 | this.review = review; 60 | } 61 | 62 | public int getRating() { 63 | return rating; 64 | } 65 | 66 | public void setRating(int rating) { 67 | this.rating = rating; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/main/java/io/pivotal/microservices/reviews/repositories/ReviewRepository.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.reviews.repositories; 2 | 3 | import io.pivotal.microservices.reviews.models.Review; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | public interface ReviewRepository extends MongoRepository { 7 | 8 | Iterable findByMlId(String mlId); 9 | } 10 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | 4 | --- 5 | 6 | spring: 7 | profiles: cloud 8 | eureka: 9 | instance: 10 | hostname: ms-reviews-service.cfapps.io 11 | nonSecurePort: 80 -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: reviews-service 4 | 5 | eureka: 6 | client: 7 | serviceUrl: 8 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 9 | -------------------------------------------------------------------------------- /polyglot-persistence/reviews/src/test/java/io/pivotal/microservices/reviews/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.pivotal.microservices.reviews; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.test.context.web.WebAppConfiguration; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = Application.class) 11 | @WebAppConfiguration 12 | public class ApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /security/code/.gitignore: -------------------------------------------------------------------------------- 1 | *iml 2 | target 3 | .idea 4 | .gitignore~ -------------------------------------------------------------------------------- /security/code/README.asc: -------------------------------------------------------------------------------- 1 | = Configuration 2 | 3 | 4 | This section of the lab looks at how to configure applications. 5 | 6 | == Spring Framework 7 | We'll start with a look at the http://spring.io/projects/spring-framework[Spring Framework], which provides the `Environment` abstraction and a concept of _profiles_ and the `@PropertySource` annotation. 8 | 9 | The `Environment` abstraction is a Spring application's gateway to the environment around it. Inject it in any object and then use it to ask questions like "what profiles are active" and "what's the value for this property?" A property, in this case, refers to a key and a value. Spring can read properties from any `PropertySource` in a chain of configured `PropertySource` instances. 10 | 11 | == Spring Boot 12 | 13 | 14 | Then, it looks at how Spring Boot's next-level support for configuration. Spring Boot normalizes external configuration (like `-D` arguments and environment variables) and even goes as far as canonicalizing external configuration properties such that, for example, the shell variable `SERVER_PORT` is the same as `-Dserver.port`, which is the same as putting `server.port` in a `.properties` file. Spring Boot also adds novel support for `.yml` configuration files. Spring Boot will, by convention, look for `src/main/resources/application.properties` and `src/main/resources/application.yml`, too. 15 | 16 | == Spring Cloud 17 | Then, we look at how Spring Cloud supports cloud-native applications and operational requirements by supporting journaled, centralized configuration for multiple applications and services through the Spring Cloud config server. The Spring Cloud config server is backed by a http://en.wikipedia.org/wiki/Git_%28software%29[Git]. This promotes traceability and easier updates to configuration since there's no need to re-push an application just to change its configuration. Additionally, Spring Cloud supports _refreshing_ configuration for beans _in-situ_ through the `/refresh` Actuator endpoint and through the Spring Cloud event bus (which uses RabbitMQ as a message bus to propagate configuration changes to all connected applications or services). 18 | -------------------------------------------------------------------------------- /security/code/auth-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | service-discovery 7 | root 8 | 0.0.1-SNAPSHOT 9 | 10 | 11 | auth-service 12 | jar 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-starter-eureka 18 | 19 | 20 | 21 | com.h2database 22 | h2 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-jdbc 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-security 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.security.oauth 38 | spring-security-oauth2 39 | 2.0.3.RELEASE 40 | 41 | 42 | 43 | auth.Application 44 | 45 | 46 | -------------------------------------------------------------------------------- /security/code/auth-service/src/main/java/auth/Application.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.jdbc.core.JdbcTemplate; 10 | import org.springframework.jdbc.core.RowMapper; 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.config.annotation.ObjectPostProcessor; 13 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 14 | import org.springframework.security.core.authority.AuthorityUtils; 15 | import org.springframework.security.core.userdetails.User; 16 | import org.springframework.security.core.userdetails.UserDetailsService; 17 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 18 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 19 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 20 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 21 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 22 | import org.springframework.security.oauth2.provider.ClientDetails; 23 | import org.springframework.security.oauth2.provider.ClientDetailsService; 24 | import org.springframework.security.oauth2.provider.client.BaseClientDetails; 25 | 26 | /** 27 | * Easy to retrieve an access token using: 28 | * {@code curl -X POST -vu acme:acmesecret http://localhost:8002/auth/oauth/token -H "Accept: application/json" -d "password=password&username=jlong&grant_type=password&scope=read&client_secret=acmesecret&client_id=acme" } 29 | * 30 | * Then, send the access token to an OAuth2 secured REST resource using: 31 | * {@code curl http://localhost:8080/api -H "Authorization: Bearer _INSERT TOKEN_} 32 | * 33 | * @author Josh Long 34 | */ 35 | @SpringBootApplication 36 | @EnableEurekaClient 37 | public class Application { 38 | 39 | public static void main(String[] args) { 40 | SpringApplication.run(Application.class, args); 41 | } 42 | 43 | @Bean 44 | UserDetailsService userDetailsService(JdbcTemplate jdbcTemplate) { 45 | 46 | RowMapper userRowMapper = (rs, i) -> 47 | new User( 48 | rs.getString("ACCOUNT_NAME"), 49 | rs.getString("PASSWORD"), 50 | rs.getBoolean("ENABLED"), 51 | rs.getBoolean("ENABLED"), 52 | rs.getBoolean("ENABLED"), 53 | rs.getBoolean("ENABLED"), 54 | AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN")); 55 | 56 | return username -> jdbcTemplate.queryForObject( 57 | "select * from ACCOUNT where ACCOUNT_NAME = ?", userRowMapper, username); 58 | } 59 | 60 | @Bean 61 | ClientDetailsService clientDetailsService(JdbcTemplate jdbcTemplate) { 62 | 63 | RowMapper clientDetailsRowMapper = (rs, i) -> { 64 | BaseClientDetails baseClientDetails = new BaseClientDetails( 65 | rs.getString("CLIENT_ID"), 66 | rs.getString("RESOURCE_IDS"), 67 | rs.getString("SCOPES"), 68 | rs.getString("GRANT_TYPES"), 69 | rs.getString("AUTHORITIES")); 70 | baseClientDetails.setClientSecret(rs.getString("CLIENT_SECRET")); 71 | return baseClientDetails; 72 | }; 73 | return clientId -> jdbcTemplate.queryForObject( 74 | "select * from CLIENT_DETAILS where CLIENT_ID=?", clientDetailsRowMapper, clientId); 75 | } 76 | 77 | @Bean 78 | AuthenticationManager authenticationManager( 79 | ObjectPostProcessor objectPostProcessor, 80 | UserDetailsService userDetailsService) throws Exception { 81 | AuthenticationManagerBuilder authenticationManagerBuilder = new AuthenticationManagerBuilder(objectPostProcessor); 82 | authenticationManagerBuilder.userDetailsService(userDetailsService); 83 | return authenticationManagerBuilder.getOrBuild(); 84 | } 85 | 86 | @Configuration 87 | @EnableAuthorizationServer 88 | static class OAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { 89 | 90 | @Autowired 91 | private AuthenticationManager authenticationManager; 92 | 93 | @Autowired 94 | private ClientDetailsService clientDetailsService; 95 | 96 | @Override 97 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 98 | clients.withClientDetails(clientDetailsService); 99 | } 100 | 101 | @Override 102 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 103 | endpoints.authenticationManager(authenticationManager); 104 | } 105 | 106 | @Override 107 | public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { 108 | oauthServer.checkTokenAccess("permitAll()"); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /security/code/auth-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port : 8002 3 | contextPath: /auth 4 | -------------------------------------------------------------------------------- /security/code/auth-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: auth-service -------------------------------------------------------------------------------- /security/code/auth-service/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | -- users in system 2 | insert into account(account_name , password) values('jlong', 'password'); 3 | insert into account(account_name , password) values('mstine', 'password'); 4 | 5 | 6 | -- oauth client details 7 | insert into client_details( client_id, client_secret, resource_ids, scopes, grant_types, authorities) 8 | values( 'acme' , 'acmesecret', null, 'read', 'authorization_code,refresh_token,password', null ); 9 | -------------------------------------------------------------------------------- /security/code/auth-service/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | create table account ( ACCOUNT_NAME varchar(255) not null, 2 | PASSWORD varchar(255 ) not null, 3 | ID serial, 4 | ENABLED bool default true); 5 | 6 | create table client_details( 7 | CLIENT_ID VARCHAR (255) not null unique , 8 | CLIENT_SECRET VARCHAR (255) not null , 9 | RESOURCE_IDS VARCHAR (255) null , 10 | SCOPES VARCHAR (255) null , 11 | GRANT_TYPES VARCHAR (255) null , 12 | AUTHORITIES VARCHAR (255) null , 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /security/code/bin/baby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-lab/03153fee15b7d13691b031ba4af2ad95700cb25d/security/code/bin/baby.jpg -------------------------------------------------------------------------------- /security/code/bin/download_photo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | at="`./oauth.sh $1 $2`" 4 | curl http://localhost:8060/photo -H "Authorization: Bearer $at" 5 | -------------------------------------------------------------------------------- /security/code/bin/oauth.sh: -------------------------------------------------------------------------------- 1 | at=`curl -X POST -u acme:acmesecret http://localhost:8002/auth/oauth/token -H "Accept: application/json" -d "password=$2&username=$1&grant_type=password&scope=read&client_secret=acmesecret&client_id=acme" | cut -d\" -f4 ` 2 | echo $at 3 | -------------------------------------------------------------------------------- /security/code/bin/upload_photo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | at="`./oauth.sh $1 $2`" 4 | curl http://localhost:8060/photo -F "multipartFile=@$3" -H "Authorization: Bearer $at" 5 | -------------------------------------------------------------------------------- /security/code/bookmark-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | service-discovery 7 | root 8 | 0.0.1-SNAPSHOT 9 | 10 | bookmark-service 11 | jar 12 | 13 | 14 | 15 | org.springframework.cloud 16 | spring-cloud-starter-security 17 | 18 | 19 | org.springframework.security.oauth 20 | spring-security-oauth2 21 | 2.0.3.RELEASE 22 | 23 | 24 | org.springframework.social 25 | spring-social-core 26 | 27 | 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-eureka 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-data-jpa 36 | 37 | 38 | com.h2database 39 | h2 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /security/code/bookmark-service/src/main/java/bookmarks/Application.java: -------------------------------------------------------------------------------- 1 | package bookmarks; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8 | import org.springframework.cloud.security.oauth2.resource.EnableOAuth2Resource; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.data.jpa.repository.JpaRepository; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.persistence.Entity; 14 | import javax.persistence.GeneratedValue; 15 | import javax.persistence.Id; 16 | import java.security.Principal; 17 | import java.util.Arrays; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | interface BookmarkRepository extends JpaRepository { 22 | 23 | Bookmark findByUserIdAndId(String userId, Long id); 24 | 25 | List findByUserId(String userId); 26 | } 27 | 28 | @SpringBootApplication 29 | @EnableEurekaClient 30 | @EnableOAuth2Resource 31 | public class Application { 32 | 33 | public static void main(String[] args) { 34 | SpringApplication.run(Application.class, args); 35 | } 36 | 37 | @Bean 38 | CommandLineRunner init(BookmarkRepository bookmarkRepository) { 39 | return args -> 40 | Arrays.asList("mstine", "jlong").forEach(n -> 41 | bookmarkRepository.save(new Bookmark(n, 42 | "http://some-other-host" + n + ".com/", 43 | "A description for " + n + "'s link", 44 | n))); 45 | } 46 | } 47 | 48 | @RestController 49 | @RequestMapping("/bookmarks") 50 | class BookmarkRestController { 51 | 52 | @Autowired 53 | private BookmarkRepository bookmarkRepository; 54 | 55 | @RequestMapping(method = RequestMethod.GET) 56 | Collection getBookmarks(Principal principal) { 57 | String userId = principal.getName(); 58 | return this.bookmarkRepository.findByUserId(userId); 59 | } 60 | 61 | @RequestMapping(value = "/{bookmarkId}", method = RequestMethod.GET) 62 | Bookmark getBookmark(Principal principal, @PathVariable Long bookmarkId) { 63 | String userId = principal.getName(); 64 | return this.bookmarkRepository.findByUserIdAndId(userId, bookmarkId); 65 | } 66 | 67 | @RequestMapping(method = RequestMethod.POST) 68 | Bookmark createBookmark(Principal principal, @RequestBody Bookmark bookmark) { 69 | String userId = principal.getName(); 70 | Bookmark bookmarkInstance = new Bookmark(userId, bookmark.getHref(), 71 | bookmark.getDescription(), bookmark.getLabel()); 72 | return this.bookmarkRepository.save(bookmarkInstance); 73 | } 74 | 75 | } 76 | 77 | @Entity 78 | class Bookmark { 79 | 80 | @Id 81 | @GeneratedValue 82 | private Long id; 83 | 84 | private String userId; 85 | 86 | private String href; 87 | 88 | private String description; 89 | private String label; 90 | 91 | Bookmark() { 92 | } 93 | 94 | public Bookmark(String userId, String href, 95 | String description, String label) { 96 | this.userId = userId; 97 | this.href = href; 98 | this.description = description; 99 | this.label = label; 100 | } 101 | 102 | public String getLabel() { 103 | return label; 104 | } 105 | 106 | public String getUserId() { 107 | return userId; 108 | } 109 | 110 | public Long getId() { 111 | return id; 112 | } 113 | 114 | public String getHref() { 115 | return href; 116 | } 117 | 118 | public String getDescription() { 119 | return description; 120 | } 121 | } -------------------------------------------------------------------------------- /security/code/bookmark-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | 5 | security: 6 | ignored: / 7 | 8 | oauth2: 9 | sso: 10 | loginPath: /dashboard/login 11 | 12 | client: 13 | tokenUri: http://localhost:8002/auth/oauth/token 14 | authorizationUri: http://localhost:8002/auth/oauth/authorize 15 | clientId: acme 16 | clientSecret: acmesecret 17 | resource: 18 | tokenInfoUri: http://localhost:8002/auth/oauth/check_token 19 | id: openid 20 | serviceId: ${PREFIX:}resource 21 | 22 | logging: 23 | level: 24 | org.springframework.security: DEBUG -------------------------------------------------------------------------------- /security/code/bookmark-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: bookmark-service -------------------------------------------------------------------------------- /security/code/eureka/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | 8 | service-discovery 9 | root 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | 14 | eureka 15 | jar 16 | 17 | 18 | 19 | 20 | org.springframework.cloud 21 | spring-cloud-starter-eureka-server 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /security/code/eureka/src/main/java/registry/Application.java: -------------------------------------------------------------------------------- 1 | package registry; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /security/code/eureka/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8761 3 | 4 | eureka: 5 | client: 6 | registerWithEureka: false 7 | fetchRegistry: false 8 | server: 9 | waitTimeInMsWhenSyncEmpty: 0 10 | 11 | -------------------------------------------------------------------------------- /security/code/passport-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | 8 | service-discovery 9 | root 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | passport-service 14 | jar 15 | 16 | 17 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-hystrix 23 | 24 | 27 | 28 | 29 | com.netflix.feign 30 | feign-core 31 | 32 | 33 | com.netflix.feign 34 | feign-ribbon 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-eureka 39 | 40 | 41 | commons-lang 42 | commons-lang 43 | 2.6 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /security/code/passport-service/src/main/java/passport/Application.java: -------------------------------------------------------------------------------- 1 | package passport; 2 | 3 | import com.netflix.appinfo.InstanceInfo; 4 | import org.apache.commons.lang.builder.ToStringBuilder; 5 | import org.apache.commons.lang.builder.ToStringStyle; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 10 | import org.springframework.cloud.netflix.feign.FeignConfigurer; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.core.ParameterizedTypeReference; 13 | import org.springframework.http.HttpMethod; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.PathVariable; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.RestController; 19 | import org.springframework.web.client.RestTemplate; 20 | 21 | import java.util.List; 22 | 23 | interface BookmarkClient { 24 | 25 | @RequestMapping(method = RequestMethod.GET, value = "/{userId}/bookmarks") 26 | List getBookmarks(@PathVariable("userId") String userId); 27 | } 28 | 29 | @SpringBootApplication 30 | @EnableEurekaClient 31 | public class Application extends FeignConfigurer { 32 | 33 | public static void main(String[] args) { 34 | SpringApplication.run(Application.class, args); 35 | } 36 | 37 | @Bean 38 | BookmarkClient bookmarkClient() { 39 | return loadBalance(BookmarkClient.class, "http://bookmark-service"); 40 | } 41 | } 42 | 43 | class Bookmark { 44 | private Long id; 45 | private String href, label, description, userId; 46 | 47 | public Bookmark() { 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "Bookmark{" + 53 | "id=" + id + 54 | ", href='" + href + '\'' + 55 | ", label='" + label + '\'' + 56 | ", description='" + description + '\'' + 57 | ", userId='" + userId + '\'' + 58 | '}'; 59 | } 60 | 61 | public Long getId() { 62 | return id; 63 | } 64 | 65 | public String getHref() { 66 | return href; 67 | } 68 | 69 | public String getLabel() { 70 | return label; 71 | } 72 | 73 | public String getDescription() { 74 | return description; 75 | } 76 | 77 | public String getUserId() { 78 | return userId; 79 | } 80 | } 81 | 82 | 83 | @RestController 84 | class Client { 85 | 86 | @Autowired 87 | private com.netflix.discovery.DiscoveryClient discoveryClient; 88 | 89 | @Autowired 90 | private RestTemplate restTemplate; 91 | 92 | @Autowired 93 | private BookmarkClient bookmarkClient; 94 | 95 | // TODO NB: don't call this until about 30s after it's started up!!! 96 | // the load balancers need to refresh their list of servers otherwise they'll fail. 97 | @RequestMapping("/connect") 98 | public void connect() throws Exception { 99 | 100 | // get the info directly from the Eureka DiscoveryClient 101 | InstanceInfo photoServiceInstanceInfo = discoveryClient.getNextServerFromEureka( 102 | "photo-service", false); 103 | System.out.println("photoService: " + ToStringBuilder.reflectionToString(photoServiceInstanceInfo, ToStringStyle.MULTI_LINE_STYLE)); 104 | 105 | InstanceInfo bookmarkServiceInstanceInfo = discoveryClient.getNextServerFromEureka( 106 | "bookmark-service", false); 107 | System.out.println("bookmarkService: " + ToStringBuilder.reflectionToString( 108 | bookmarkServiceInstanceInfo, ToStringStyle.MULTI_LINE_STYLE)); 109 | 110 | InstanceInfo.InstanceStatus bookmarkStatus = bookmarkServiceInstanceInfo.getStatus(); 111 | System.out.println("bookmark status: " + bookmarkStatus); 112 | 113 | InstanceInfo.InstanceStatus photoStatus = photoServiceInstanceInfo.getStatus(); 114 | System.out.println("photo status: " + photoStatus); 115 | 116 | // use the "smart" Eureka-aware RestTemplate 117 | ResponseEntity> exchange = this.restTemplate.exchange( 118 | "http://bookmark-service/{userId}/bookmarks", HttpMethod.GET, null, 119 | new ParameterizedTypeReference>() { 120 | }, (Object) "mstine"); 121 | exchange.getBody().forEach(System.out::println); 122 | 123 | // use the smart Eureka-aware Feign support 124 | bookmarkClient.getBookmarks("jlong").forEach(System.out::println); 125 | } 126 | 127 | 128 | } -------------------------------------------------------------------------------- /security/code/passport-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8030 3 | eureka: 4 | client: 5 | registryFetchIntervalSeconds : 10 -------------------------------------------------------------------------------- /security/code/passport-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: passport-service -------------------------------------------------------------------------------- /security/code/photo-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | service-discovery 7 | root 8 | 0.0.1-SNAPSHOT 9 | 10 | photo-service 11 | jar 12 | 13 | 14 | 15 | org.springframework.cloud 16 | spring-cloud-starter-security 17 | 18 | 19 | org.springframework.security.oauth 20 | spring-security-oauth2 21 | 2.0.3.RELEASE 22 | 23 | 24 | org.springframework.social 25 | spring-social-core 26 | 27 | 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-eureka 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-data-mongodb 36 | 37 | 38 | com.h2database 39 | h2 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /security/code/photo-service/src/main/java/photos/Application.java: -------------------------------------------------------------------------------- 1 | package photos; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 | import org.springframework.cloud.security.oauth2.resource.EnableOAuth2Resource; 8 | import org.springframework.core.io.Resource; 9 | import org.springframework.data.mongodb.gridfs.GridFsTemplate; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.multipart.MultipartFile; 19 | import org.springframework.web.util.UriComponentsBuilder; 20 | 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.net.URI; 24 | import java.security.Principal; 25 | 26 | @SpringBootApplication 27 | @EnableEurekaClient 28 | @EnableOAuth2Resource 29 | public class Application { 30 | 31 | 32 | public static void main(String[] args) { 33 | SpringApplication.run(Application.class, args); 34 | } 35 | } 36 | 37 | @RestController 38 | @RequestMapping("/photo") 39 | class PhotoRestController { 40 | 41 | @Autowired 42 | private GridFsTemplate gridFsTemplate; 43 | 44 | @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT}) 45 | ResponseEntity set(Principal principal, 46 | @RequestParam MultipartFile multipartFile, 47 | UriComponentsBuilder uriBuilder) throws IOException { 48 | 49 | String userId = principal.getName() ; 50 | 51 | 52 | try (InputStream inputStream = multipartFile.getInputStream()) { 53 | this.gridFsTemplate.store(inputStream, userId); 54 | } 55 | URI uri = uriBuilder.path("/photo").buildAndExpand(userId).toUri(); 56 | HttpHeaders headers = new HttpHeaders(); 57 | headers.setLocation(uri); 58 | return new ResponseEntity<>(headers, HttpStatus.CREATED); 59 | } 60 | 61 | @RequestMapping(method = RequestMethod.GET) 62 | ResponseEntity get(Principal principal ) { 63 | String userId = principal.getName(); 64 | HttpHeaders httpHeaders = new HttpHeaders(); 65 | httpHeaders.setContentType(MediaType.IMAGE_JPEG); 66 | return new ResponseEntity<>( 67 | this.gridFsTemplate.getResource(userId), httpHeaders, HttpStatus.OK); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /security/code/photo-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8060 3 | 4 | 5 | 6 | security: 7 | ignored: / 8 | 9 | oauth2: 10 | sso: 11 | loginPath: /dashboard/login 12 | 13 | client: 14 | tokenUri: http://localhost:8002/auth/oauth/token 15 | authorizationUri: http://localhost:8002/auth/oauth/authorize 16 | clientId: acme 17 | clientSecret: acmesecret 18 | resource: 19 | tokenInfoUri: http://localhost:8002/auth/oauth/check_token 20 | id: openid 21 | serviceId: ${PREFIX:}resource 22 | 23 | logging: 24 | level: 25 | org.springframework.security: DEBUG -------------------------------------------------------------------------------- /security/code/photo-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: photo-service -------------------------------------------------------------------------------- /security/code/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 1.2.0.RC2 10 | 11 | 12 | 13 | security 14 | root 15 | 0.0.1-SNAPSHOT 16 | pom 17 | 18 | photo-service 19 | passport-service 20 | eureka 21 | bookmark-service 22 | auth-service 23 | 24 | 25 | 26 | 27 | UTF-8 28 | 1.8 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-starter-parent 47 | 1.0.0.M3 48 | pom 49 | import 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | spring-snapshots 58 | Spring Snapshots 59 | http://repo.spring.io/libs-snapshot-local 60 | 61 | true 62 | 63 | 64 | 65 | spring-milestones 66 | Spring Milestones 67 | http://repo.spring.io/libs-milestone-local 68 | 69 | false 70 | 71 | 72 | 73 | spring-releases 74 | Spring Releases 75 | http://repo.spring.io/libs-release-local 76 | 77 | false 78 | 79 | 80 | 81 | 82 | 83 | 84 | spring-snapshots 85 | Spring Snapshots 86 | http://repo.spring.io/libs-snapshot-local 87 | 88 | true 89 | 90 | 91 | 92 | spring-milestones 93 | Spring Milestones 94 | http://repo.spring.io/libs-milestone-local 95 | 96 | false 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /security/deck.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-lab/03153fee15b7d13691b031ba4af2ad95700cb25d/security/deck.key -------------------------------------------------------------------------------- /service-registry/README.asc: -------------------------------------------------------------------------------- 1 | = Service Registration & Discovery 2 | 3 | 4 | This section of the lab looks at how using a service registry (like Netflix's Eureka) can decouple services. It's easiest to think of Eureka as a phone-book or a directory for your services: as they spin up and claim a host and port, they register themselves with the directory and any service that wants to _discover_ or route to them need only ask the directory. 5 | 6 | If a given service is _not_ listed in the directory then a client is at least empowered to react accordingly instead of blindly attempting a service-to-service call. 7 | 8 | Services may also handle their own routing and load-balancing if they have the ability to ask questions about the state and topology of the system. After all, a client knows best what priorities should be applied to its requests and its also better positioned to change those priorities as needed. If a client can ask questions about the topology of the system, it can react to changes in that topology accordingly. 9 | 10 | Spring Cloud supports Netflix's Eureka and the https://github.com/hashicorp/consul[Hashicorp Consul] project as service registries. The Consul integration is being actively developed so for the purposes of this lab, we'll https://github.com/Netflix/eureka[look at Netflix's Eureka]. 11 | 12 | NOTE: **A Word About the Directory Structure**: All projects will inherit the mostly common and boilerplate build configuration from the root `pom.xml`. Start there, and then inspect the module-specific `pom.xml` files. 13 | 14 | NOTE: **A Word About the Cloud Foundry Support**: All of these projects are designed to be run on Cloud Foundry. Each module describes (_declares_) the runtime requirements for each module in the adjacent `manifest.yml` file. There's an _installer script_ in the root of the project, `cf.sh`. 15 | 16 | == Standing Up the Eureka Service Registry 17 | Eureka's distributed as a Spring Boot auto-configuration. You layer Eureka as a facility on top of a Spring Boot application and then deploy it. 18 | 19 | * the important code is in `eureka-service/src/main/java/registry/Application.java`. It demonstrates how easy it is to standup the Netflix Eureka service registry. It boils down, basically, to one annotation (`@EnableEurekaServer`). 20 | * Eureka requires very little additional configuration. Notably, `eureka-service/src/main/resources/application.yml` does little but go out of its way to tell itself not to register with itself. Other than that, it's a standard, plain vanilla Spring Cloud module. 21 | * The `org.springframework.cloud:spring-cloud-starter-eureka-server` dependency _activates_ the registry. 22 | * Note that because we want all services to be able to find the registry, it's recommended that it be put behind a fixed hostname and optionally made highly available. We make sure that all services can find it by exposing its location as a Cloud Foundry _user provided service_ in the `cf.sh` installer script. Thus all other services can find the registry using the same consistent access patterns they use to talk to, for example, a backend message queue or database. 23 | * To start the registry, run the `eureka-service` module and then visit `http://localhost:8761`. Keep that page handy as we'll need it in the next section. 24 | 25 | 26 | == Registering a Service with the Registry 27 | Both `bookmark-service` and `photo-service` _register_ themselves with the registry. The serivices themselves aren't so interesting themselves. They're just simple, singly-focused REST APIs that may live or come down as demand requires. Any service can be made to automatically register with the registry if it has the `org.springframework.cloud:spring-cloud-starter-eureka` dependency on the CLASSPATH and are annotated with `@@EnableEurekaClient`. This Spring Boot auto-configuration will assume that it is to register itself at `http://127.0.0.1:8761` absent any specific configuration. 28 | 29 | * We can provide and override specific configuration, like so. 30 | 31 | ``` 32 | eureka: 33 | client: 34 | serviceUrl: 35 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 36 | ``` 37 | 38 | NOTE: This syntax uses Spring's property placeholder rseolution synax to either resolve the host by either dereferencing the value implied by the property path `vcap.services.eureka-service.credentials.uri` or defaulting to `http://127.0.0.1:8761`. 39 | 40 | * Services are registered with the registry by their `spring.application.name` value. That value is by default expected to live in `bootstrap.yml` or `bootstrap.properties`. If a service's `spring.application.name` is `foo`, then it will be listed as `FOO` in the registry. 41 | 42 | == Flexible Topologies with Client Side Load-Balancing and the Eureka Service Registry 43 | 44 | * Start up both the `bookmark-service` and the `photo-service`, then wait about 30 seconds, then check the Eureka page (`http://127.0.0.1:8761`), you will see both of them listed in Eureka under the _applications_ header. Mouse over the links to see the host and port on which the services live. You'll see their status (`UP`, `DOWN`, etc.), and if you click on the link you'll be taken to the service's _info_ page. 45 | 46 | A Java client can now ask where services live (which host and port) and how many of them there are. It can employ load balancing strategies (the default is round-robin). There are a few natural ways to do this. A few of them are demonstrated in the `passport-service` example application. Open the `passport-service/src/main/java/passport/Application.java` and then run it. 47 | 48 | * the first is the Netflix Eureka `DiscoverClient`. The `DiscoveryClient` is an API. Use this to interrogate the registry directly and in a more low-level fashion. It's useful if you want to get and analyze information about the state of the registered services directly. 49 | * if all you want to do is route to a resolved, available service instance when making an HTTP REST client call, then simply inject a `RestTemplate`. Spring Cloud adds an interceptor that dynamically replaces the URI of the `RestTemplate` request with one resolved by checking with the registry. Thus, an HTTP request targeted at `http://foo-service/` would first resolve a host and port for the service named `foo-service` in Eureka, _then_ would execute the call. Other than that, the use of the `RestTemplate` should be pretty familiar. 50 | * You can take this approach even further using Spring Cloud's fantastic integration with Feign and Ribbon. In our example, we describe the remote service's contract using a standard client-side interface with methods annotted using Spring MVC's annotations. Spring Cloud and Feign will provide a synthesized proxy that clietns can use in a type-safe manner. The configuration is done in the `Application` class and it is there that, again, a URI string is input with a service ID in place of the host and port. This again is resolved by way of Eureaka before requests are made. 51 | -------------------------------------------------------------------------------- /service-registry/bin/baby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-lab/03153fee15b7d13691b031ba4af2ad95700cb25d/service-registry/bin/baby.jpg -------------------------------------------------------------------------------- /service-registry/bin/download_photo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | curl http://localhost:8060/$1/photo 4 | -------------------------------------------------------------------------------- /service-registry/bin/upload_photo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | curl http://localhost:8060/$1/photo -F "multipartFile=@$2" 4 | -------------------------------------------------------------------------------- /service-registry/bookmark-service/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: bookmark-service 4 | memory: 512M 5 | instances: 1 6 | host: bookmark-service-${random-word} 7 | domain: cfapps.io 8 | path: target/bookmark-service.jar 9 | services: 10 | - eureka-service 11 | - bookmark-service-postgresql 12 | env: 13 | SPRING_PROFILES_ACTIVE: cloud 14 | DEBUG: "true" 15 | debug: "true" 16 | -------------------------------------------------------------------------------- /service-registry/bookmark-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | service-discovery 7 | root 8 | 0.0.1-SNAPSHOT 9 | 10 | bookmark-service 11 | jar 12 | 13 | 14 | org.springframework.cloud 15 | spring-cloud-starter-eureka 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-data-jpa 20 | 21 | 22 | com.h2database 23 | h2 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /service-registry/bookmark-service/src/main/java/bookmarks/Application.java: -------------------------------------------------------------------------------- 1 | package bookmarks; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.data.jpa.repository.JpaRepository; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.persistence.Entity; 15 | import javax.persistence.GeneratedValue; 16 | import javax.persistence.Id; 17 | import java.util.Arrays; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | @ComponentScan 22 | @Configuration 23 | @EnableAutoConfiguration 24 | @EnableEurekaClient 25 | public class Application { 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(Application.class, args); 29 | } 30 | 31 | @Bean 32 | CommandLineRunner init(BookmarkRepository bookmarkRepository) { 33 | return args -> { 34 | bookmarkRepository.deleteAll(); 35 | 36 | Arrays.asList("mstine", "jlong").forEach(n -> 37 | bookmarkRepository.save(new Bookmark(n, 38 | "http://some-other-host" + n + ".com/", 39 | "A description for " + n + "'s link", 40 | n))); 41 | }; 42 | } 43 | } 44 | 45 | @RestController 46 | @RequestMapping("/{userId}/bookmarks") 47 | class BookmarkRestController { 48 | 49 | @RequestMapping(method = RequestMethod.GET) 50 | Collection getBookmarks(@PathVariable String userId) { 51 | return this.bookmarkRepository.findByUserId(userId); 52 | } 53 | 54 | @RequestMapping(value = "/{bookmarkId}", method = RequestMethod.GET) 55 | Bookmark getBookmark(@PathVariable String userId, @PathVariable Long bookmarkId) { 56 | return this.bookmarkRepository.findByUserIdAndId(userId, bookmarkId); 57 | } 58 | 59 | @RequestMapping(method = RequestMethod.POST) 60 | Bookmark createBookmark(@PathVariable String userId, @RequestBody Bookmark bookmark) { 61 | Bookmark bookmarkInstance = new Bookmark(userId, bookmark.getHref(), 62 | bookmark.getDescription(), bookmark.getLabel()); 63 | return this.bookmarkRepository.save(bookmarkInstance); 64 | } 65 | 66 | @Autowired 67 | private BookmarkRepository bookmarkRepository; 68 | 69 | } 70 | 71 | 72 | interface BookmarkRepository extends JpaRepository { 73 | 74 | Bookmark findByUserIdAndId(String userId, Long id); 75 | 76 | List findByUserId(String userId); 77 | } 78 | 79 | @Entity 80 | class Bookmark { 81 | 82 | private String userId; 83 | 84 | @Id 85 | @GeneratedValue 86 | private Long id; 87 | 88 | private String href; 89 | 90 | private String description; 91 | 92 | Bookmark() { 93 | } 94 | 95 | public Bookmark(String userId, String href, 96 | String description, String label) { 97 | this.userId = userId; 98 | this.href = href; 99 | this.description = description; 100 | this.label = label; 101 | } 102 | 103 | public String getLabel() { 104 | return label; 105 | } 106 | 107 | public String getUserId() { 108 | return userId; 109 | } 110 | 111 | public Long getId() { 112 | return id; 113 | } 114 | 115 | public String getHref() { 116 | return href; 117 | } 118 | 119 | public String getDescription() { 120 | return description; 121 | } 122 | 123 | private String label; 124 | } -------------------------------------------------------------------------------- /service-registry/bookmark-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | server: 3 | port: ${vcap.application.port:9098} 4 | 5 | spring: 6 | jpa: 7 | generate-ddl: true 8 | 9 | 10 | eureka: 11 | client: 12 | serviceUrl: 13 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 14 | 15 | --- 16 | spring: 17 | profiles: cloud 18 | eureka: 19 | instance: 20 | hostname: ${APPLICATION_DOMAIN} 21 | nonSecurePort: 80 22 | -------------------------------------------------------------------------------- /service-registry/bookmark-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: bookmark-service -------------------------------------------------------------------------------- /service-registry/cf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # 5 | # the big CloudFoundry installer 6 | # 7 | 8 | CLOUD_DOMAIN=${DOMAIN:-run.pivotal.io} 9 | CLOUD_TARGET=api.${DOMAIN} 10 | 11 | function login(){ 12 | cf api | grep ${CLOUD_TARGET} || cf api ${CLOUD_TARGET} --skip-ssl-validation 13 | cf a | grep OK || cf login 14 | } 15 | 16 | function app_domain(){ 17 | D=`cf apps | grep $1 | tr -s ' ' | cut -d' ' -f 6 | cut -d, -f1` 18 | echo $D 19 | } 20 | 21 | function deploy_app(){ 22 | 23 | APP_NAME=$1 24 | cd $APP_NAME 25 | cf push $APP_NAME --no-start 26 | APPLICATION_DOMAIN=`app_domain $APP_NAME` 27 | echo determined that application_domain for $APP_NAME is $APPLICATION_DOMAIN. 28 | cf env $APP_NAME | grep APPLICATION_DOMAIN || cf set-env $APP_NAME APPLICATION_DOMAIN $APPLICATION_DOMAIN 29 | cf restart $APP_NAME 30 | cd .. 31 | } 32 | 33 | function deploy_service(){ 34 | N=$1 35 | D=`app_domain $N` 36 | JSON='{"uri":"http://'$D'"}' 37 | echo cf cups $N -p $JSON 38 | cf cups $N -p $JSON 39 | } 40 | 41 | function deploy_eureka() { 42 | NAME=eureka-service 43 | deploy_app $NAME 44 | deploy_service $NAME 45 | } 46 | 47 | function deploy_photo_service(){ 48 | cf cs mongolab sandbox photo-service-mongodb 49 | deploy_app photo-service 50 | } 51 | 52 | function deploy_bookmark_service(){ 53 | cf cs elephantsql turtle bookmark-service-postgresql 54 | deploy_app bookmark-service 55 | } 56 | 57 | function deploy_passport_service(){ 58 | deploy_app passport-service 59 | } 60 | 61 | function reset(){ 62 | cf d eureka-service 63 | cf d photo-service 64 | cf d passport-service 65 | cf d bookmark-service 66 | cf ds photo-service-mongodb 67 | cf ds bookmark-service-postgresql 68 | cf ds eureka-service 69 | cf delete-orphaned-routes 70 | } 71 | 72 | mvn -DskipTests=true clean install 73 | 74 | login 75 | reset 76 | deploy_eureka 77 | deploy_photo_service 78 | deploy_bookmark_service 79 | deploy_passport_service 80 | -------------------------------------------------------------------------------- /service-registry/eureka-service/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: eureka-service 4 | memory: 512M 5 | instances: 1 6 | host: eureka-service-${random-word} 7 | domain: cfapps.io 8 | path: target/eureka-service.jar 9 | env: 10 | SPRING_PROFILES_ACTIVE: cloud 11 | DEBUG: "true" 12 | debug: "true" 13 | -------------------------------------------------------------------------------- /service-registry/eureka-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | 8 | service-discovery 9 | root 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | 14 | eureka-service 15 | jar 16 | 17 | 18 | 19 | 20 | org.springframework.cloud 21 | spring-cloud-starter-eureka-server 22 | 23 | 24 | 25 | 26 | 27 | registry.Application 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /service-registry/eureka-service/src/main/java/registry/Application.java: -------------------------------------------------------------------------------- 1 | package registry; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @SpringBootApplication 11 | @EnableEurekaServer 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service-registry/eureka-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | server: 4 | port: ${vcap.application.port:8761} 5 | 6 | 7 | 8 | eureka: 9 | client: 10 | registerWithEureka: false 11 | fetchRegistry: false 12 | server: 13 | waitTimeInMsWhenSyncEmpty: 0 14 | -------------------------------------------------------------------------------- /service-registry/passport-service/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: passport-service 4 | memory: 512M 5 | instances: 1 6 | host: passport-service-${random-word} 7 | domain: cfapps.io 8 | path: target/passport-service.jar 9 | services: 10 | - eureka-service 11 | env: 12 | SPRING_PROFILES_ACTIVE: cloud 13 | DEBUG: "true" 14 | debug: "true" 15 | -------------------------------------------------------------------------------- /service-registry/passport-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | 8 | service-discovery 9 | root 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | passport-service 14 | jar 15 | 16 | 17 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-hystrix 23 | 24 | 27 | 28 | 29 | com.netflix.feign 30 | feign-core 31 | 32 | 33 | com.netflix.feign 34 | feign-ribbon 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-eureka 39 | 40 | 41 | commons-lang 42 | commons-lang 43 | 2.6 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /service-registry/passport-service/src/main/java/passport/Application.java: -------------------------------------------------------------------------------- 1 | package passport; 2 | 3 | import com.netflix.appinfo.InstanceInfo; 4 | import org.apache.commons.lang.builder.ToStringBuilder; 5 | import org.apache.commons.lang.builder.ToStringStyle; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 9 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 10 | import org.springframework.cloud.netflix.feign.FeignConfigurer; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.ComponentScan; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.core.ParameterizedTypeReference; 15 | import org.springframework.http.HttpMethod; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.web.bind.annotation.PathVariable; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RequestMethod; 20 | import org.springframework.web.bind.annotation.RestController; 21 | import org.springframework.web.client.RestTemplate; 22 | 23 | import java.util.List; 24 | 25 | @ComponentScan 26 | @Configuration 27 | @EnableAutoConfiguration 28 | @EnableEurekaClient 29 | public class Application extends FeignConfigurer { 30 | 31 | public static void main(String[] args) { 32 | SpringApplication.run(Application.class, args); 33 | } 34 | 35 | @Bean 36 | BookmarkClient bookmarkClient() { 37 | return loadBalance(BookmarkClient.class, "http://bookmark-service"); 38 | } 39 | } 40 | 41 | @RestController 42 | class Client { 43 | 44 | @Autowired 45 | private com.netflix.discovery.DiscoveryClient discoveryClient; 46 | 47 | @Autowired 48 | private RestTemplate restTemplate; 49 | 50 | @Autowired 51 | private BookmarkClient bookmarkClient; 52 | 53 | // TODO NB: don't call this until about 30s after it's started up!!! 54 | // the load balancers need to refresh their list of servers otherwise they'll fail. 55 | @RequestMapping("/connect") 56 | public void connect() throws Exception { 57 | 58 | // get the info directly from the Eureka DiscoveryClient 59 | InstanceInfo photoServiceInstanceInfo = discoveryClient.getNextServerFromEureka( 60 | "photo-service", false); 61 | System.out.println("photoService: " + ToStringBuilder.reflectionToString(photoServiceInstanceInfo, ToStringStyle.MULTI_LINE_STYLE)); 62 | 63 | InstanceInfo bookmarkServiceInstanceInfo = discoveryClient.getNextServerFromEureka( 64 | "bookmark-service", false); 65 | System.out.println("bookmarkService: " + ToStringBuilder.reflectionToString( 66 | bookmarkServiceInstanceInfo, ToStringStyle.MULTI_LINE_STYLE)); 67 | 68 | InstanceInfo.InstanceStatus bookmarkStatus = bookmarkServiceInstanceInfo.getStatus(); 69 | System.out.println("bookmark status: " + bookmarkStatus); 70 | 71 | InstanceInfo.InstanceStatus photoStatus = photoServiceInstanceInfo.getStatus(); 72 | System.out.println("photo status: " + photoStatus); 73 | 74 | // use the "smart" Eureka-aware RestTemplate 75 | ResponseEntity> exchange = 76 | this.restTemplate.exchange( 77 | "http://bookmark-service/{userId}/bookmarks", 78 | HttpMethod.GET, 79 | null, 80 | new ParameterizedTypeReference>() {}, 81 | (Object) "mstine"); 82 | exchange.getBody().forEach(System.out::println); 83 | 84 | // use the smart Eureka-aware Feign support 85 | bookmarkClient.getBookmarks("jlong").forEach(System.out::println); 86 | } 87 | 88 | 89 | } 90 | 91 | 92 | 93 | 94 | interface BookmarkClient { 95 | 96 | @RequestMapping(method = RequestMethod.GET, value = "/{userId}/bookmarks") 97 | List getBookmarks(@PathVariable("userId") String userId); 98 | } 99 | 100 | class Bookmark { 101 | private Long id; 102 | private String href, label, description, userId; 103 | 104 | @Override 105 | public String toString() { 106 | return "Bookmark{" + 107 | "id=" + id + 108 | ", href='" + href + '\'' + 109 | ", label='" + label + '\'' + 110 | ", description='" + description + '\'' + 111 | ", userId='" + userId + '\'' + 112 | '}'; 113 | } 114 | 115 | public Bookmark() { 116 | } 117 | 118 | public Long getId() { 119 | return id; 120 | } 121 | 122 | public String getHref() { 123 | return href; 124 | } 125 | 126 | public String getLabel() { 127 | return label; 128 | } 129 | 130 | public String getDescription() { 131 | return description; 132 | } 133 | 134 | public String getUserId() { 135 | return userId; 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /service-registry/passport-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | server: 3 | port: ${vcap.application.port:8050} 4 | 5 | 6 | eureka: 7 | client: 8 | serviceUrl: 9 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 10 | 11 | --- 12 | spring: 13 | profiles: cloud 14 | eureka: 15 | instance: 16 | hostname: ${APPLICATION_DOMAIN} 17 | nonSecurePort: 80 18 | -------------------------------------------------------------------------------- /service-registry/passport-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: passport-service -------------------------------------------------------------------------------- /service-registry/photo-service/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: photo-service 4 | memory: 512M 5 | instances: 1 6 | host: photo-service-${random-word} 7 | domain: cfapps.io 8 | path: target/photo-service.jar 9 | services: 10 | - eureka-service 11 | - photo-service-mongodb 12 | env: 13 | SPRING_PROFILES_ACTIVE: cloud 14 | DEBUG: "true" 15 | debug: "true" 16 | -------------------------------------------------------------------------------- /service-registry/photo-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | service-discovery 7 | root 8 | 0.0.1-SNAPSHOT 9 | 10 | photo-service 11 | jar 12 | 13 | 14 | org.springframework.cloud 15 | spring-cloud-starter-eureka 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-data-mongodb 20 | 21 | 22 | com.h2database 23 | h2 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /service-registry/photo-service/src/main/java/photos/Application.java: -------------------------------------------------------------------------------- 1 | package photos; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.core.io.Resource; 10 | import org.springframework.data.mongodb.gridfs.GridFsTemplate; 11 | import org.springframework.http.HttpHeaders; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.MediaType; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestMethod; 17 | import org.springframework.web.bind.annotation.RequestParam; 18 | import org.springframework.web.bind.annotation.RestController; 19 | import org.springframework.web.multipart.MultipartFile; 20 | import org.springframework.web.util.UriComponentsBuilder; 21 | 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.net.URI; 25 | 26 | @ComponentScan 27 | @Configuration 28 | @EnableAutoConfiguration 29 | @EnableEurekaClient 30 | public class Application { 31 | 32 | public static void main(String[] args) { 33 | SpringApplication.run(Application.class, args); 34 | } 35 | } 36 | 37 | @RestController 38 | @RequestMapping("/{userId}/photo") 39 | class PhotoRestController { 40 | 41 | @Autowired 42 | private GridFsTemplate gridFsTemplate; 43 | 44 | @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT}) 45 | ResponseEntity set(String userId, 46 | @RequestParam MultipartFile multipartFile, 47 | UriComponentsBuilder uriBuilder) throws IOException { 48 | 49 | try (InputStream inputStream = multipartFile.getInputStream()) { 50 | this.gridFsTemplate.store(inputStream, userId); 51 | } 52 | URI uri = uriBuilder.path("/{userId}/photo").buildAndExpand(userId).toUri(); 53 | HttpHeaders headers = new HttpHeaders(); 54 | headers.setLocation(uri); 55 | return new ResponseEntity<>(headers, HttpStatus.CREATED); 56 | } 57 | 58 | @RequestMapping(method = RequestMethod.GET) 59 | ResponseEntity get(String userId) { 60 | HttpHeaders httpHeaders = new HttpHeaders(); 61 | httpHeaders.setContentType(MediaType.IMAGE_JPEG); 62 | return new ResponseEntity( 63 | this.gridFsTemplate.getResource(userId), httpHeaders, HttpStatus.OK); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /service-registry/photo-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | server: 3 | port: ${vcap.application.port:8060} 4 | 5 | 6 | eureka: 7 | client: 8 | serviceUrl: 9 | defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/ 10 | 11 | --- 12 | spring: 13 | profiles: cloud 14 | eureka: 15 | instance: 16 | hostname: ${APPLICATION_DOMAIN} 17 | nonSecurePort: 80 18 | -------------------------------------------------------------------------------- /service-registry/photo-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: photo-service -------------------------------------------------------------------------------- /service-registry/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 1.2.0.RC2 10 | 11 | 12 | 13 | service-discovery 14 | root 15 | 0.0.1-SNAPSHOT 16 | pom 17 | 18 | eureka-service 19 | photo-service 20 | 21 | bookmark-service 22 | 23 | passport-service 24 | 25 | 26 | 27 | 28 | 29 | UTF-8 30 | 1.8 31 | 32 | 33 | 34 | 35 | ${project.artifactId} 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-maven-plugin 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-starter-parent 50 | 1.0.0.M3 51 | pom 52 | import 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | spring-snapshots 61 | Spring Snapshots 62 | http://repo.spring.io/libs-snapshot-local 63 | 64 | true 65 | 66 | 67 | 68 | spring-milestones 69 | Spring Milestones 70 | http://repo.spring.io/libs-milestone-local 71 | 72 | false 73 | 74 | 75 | 76 | spring-releases 77 | Spring Releases 78 | http://repo.spring.io/libs-release-local 79 | 80 | false 81 | 82 | 83 | 84 | 85 | 86 | 87 | spring-snapshots 88 | Spring Snapshots 89 | http://repo.spring.io/libs-snapshot-local 90 | 91 | true 92 | 93 | 94 | 95 | spring-milestones 96 | Spring Milestones 97 | http://repo.spring.io/libs-milestone-local 98 | 99 | false 100 | 101 | 102 | 103 | 104 | 105 | --------------------------------------------------------------------------------