├── exercise ├── 03-netflix │ ├── eureka-server │ │ ├── src │ │ │ ├── test │ │ │ │ ├── java │ │ │ │ │ └── demo │ │ │ │ │ │ └── .gitkeep │ │ │ │ └── resources │ │ │ │ │ └── .gitkeep │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ ├── bootstrap.yml │ │ │ │ └── application.yml │ │ │ │ └── java │ │ │ │ └── demo │ │ │ │ └── EurekaServer.java │ │ └── pom.xml │ ├── urlshortener-ui │ │ ├── src │ │ │ ├── test │ │ │ │ └── resources │ │ │ │ │ └── .gitkeep │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ ├── bootstrap.yml │ │ │ │ ├── application.yml │ │ │ │ └── templates │ │ │ │ │ └── index.html │ │ │ │ └── java │ │ │ │ └── demo │ │ │ │ └── App.java │ │ └── pom.xml │ ├── urlshortener │ │ ├── src │ │ │ ├── test │ │ │ │ ├── resources │ │ │ │ │ └── .gitkeep │ │ │ │ └── java │ │ │ │ │ └── demo │ │ │ │ │ └── UrlShortenerTest.java │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ ├── bootstrap.yml │ │ │ │ └── application.yml │ │ │ │ └── java │ │ │ │ └── demo │ │ │ │ └── UrlShortener.java │ │ └── pom.xml │ ├── hystrix-dashboard │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ ├── bootstrap.yml │ │ │ │ ├── log4j.properties │ │ │ │ └── application.yml │ │ │ │ └── java │ │ │ │ └── hystrixdashboard │ │ │ │ ├── HystrixDashboardApplication.java │ │ │ │ └── stream │ │ │ │ └── MockStreamServlet.java │ │ ├── README.md │ │ └── pom.xml │ └── configserver │ │ ├── src │ │ └── main │ │ │ └── resources │ │ │ └── bootstrap.yml │ │ └── pom.xml ├── 01-urlshortener │ └── urlshortener │ │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ └── .gitkeep │ │ │ └── java │ │ │ │ └── demo │ │ │ │ └── UrlShortenerTest.java │ │ └── main │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── demo │ │ │ └── UrlShortener.java │ │ └── pom.xml └── 02-distributed-config │ ├── urlshortener │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ └── .gitkeep │ │ │ └── java │ │ │ │ └── demo │ │ │ │ └── UrlShortenerTest.java │ │ └── main │ │ │ ├── resources │ │ │ ├── bootstrap.yml │ │ │ └── application.yml │ │ │ └── java │ │ │ └── demo │ │ │ └── UrlShortener.java │ └── pom.xml │ └── configserver │ ├── src │ └── main │ │ └── resources │ │ └── bootstrap.yml │ └── pom.xml ├── instruction ├── images │ ├── logo.png │ ├── exercise00-01.png │ ├── exercise00-02.png │ ├── exercise00-03.png │ ├── exercise00-04.png │ ├── exercise01-01.png │ ├── exercise01-02.png │ ├── exercise01-03.png │ ├── exercise02-01.png │ ├── exercise02-02.png │ ├── exercise02-03.png │ ├── exercise02-04.png │ ├── exercise02-05.png │ ├── exercise02-06.png │ ├── exercise02-07.png │ ├── exercise02-08.png │ ├── exercise02-09.png │ ├── exercise02-10.png │ ├── exercise02-11.png │ ├── exercise03-01.png │ ├── exercise03-02.png │ ├── exercise03-03.png │ ├── exercise03-04.png │ ├── exercise03-05.png │ ├── exercise03-06.png │ ├── exercise03-07.png │ ├── exercise03-08.png │ ├── exercise03-09.png │ ├── exercise03-10.png │ ├── exercise03-11.png │ ├── exercise03-12.png │ ├── exercise03-13.png │ ├── exercise03-14.png │ ├── exercise03-15.png │ ├── exercise03-16.png │ ├── exercise03-17.png │ ├── import-exercise01-01.png │ ├── import-exercise01-02.png │ ├── import-exercise01-03.png │ ├── import-exercise01-04.png │ ├── import-exercise01-05.png │ ├── import-exercise01-06.png │ ├── import-exercise01-07.png │ ├── import-exercise01-08.png │ ├── import-exercise01-09.png │ ├── import-exercise02-01.png │ ├── import-exercise02-02.png │ ├── import-exercise03-01.png │ ├── import-exercise03-02.png │ ├── system-exercise03-01.png │ ├── system-exercise03-02.png │ ├── system-exercise03-03.png │ ├── system-exercise03-04.png │ ├── system-exercise03-05.png │ └── system-exercise03-06.png ├── Makefile ├── make.bat ├── conf.py └── readme.rst ├── .gitignore └── .gitmodules /exercise/03-netflix/eureka-server/src/test/java/demo/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exercise/03-netflix/eureka-server/src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener-ui/src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener/src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exercise/01-urlshortener/urlshortener/src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /exercise/02-distributed-config/urlshortener/src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /instruction/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/logo.png -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: urlshortener -------------------------------------------------------------------------------- /exercise/03-netflix/eureka-server/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: eureka-service -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: hystrixdashboard -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener-ui/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: urlshortener-ui -------------------------------------------------------------------------------- /exercise/02-distributed-config/urlshortener/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: urlshortener -------------------------------------------------------------------------------- /instruction/images/exercise00-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-01.png -------------------------------------------------------------------------------- /instruction/images/exercise00-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-02.png -------------------------------------------------------------------------------- /instruction/images/exercise00-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-03.png -------------------------------------------------------------------------------- /instruction/images/exercise00-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-04.png -------------------------------------------------------------------------------- /instruction/images/exercise01-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise01-01.png -------------------------------------------------------------------------------- /instruction/images/exercise01-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise01-02.png -------------------------------------------------------------------------------- /instruction/images/exercise01-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise01-03.png -------------------------------------------------------------------------------- /instruction/images/exercise02-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-01.png -------------------------------------------------------------------------------- /instruction/images/exercise02-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-02.png -------------------------------------------------------------------------------- /instruction/images/exercise02-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-03.png -------------------------------------------------------------------------------- /instruction/images/exercise02-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-04.png -------------------------------------------------------------------------------- /instruction/images/exercise02-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-05.png -------------------------------------------------------------------------------- /instruction/images/exercise02-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-06.png -------------------------------------------------------------------------------- /instruction/images/exercise02-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-07.png -------------------------------------------------------------------------------- /instruction/images/exercise02-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-08.png -------------------------------------------------------------------------------- /instruction/images/exercise02-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-09.png -------------------------------------------------------------------------------- /instruction/images/exercise02-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-10.png -------------------------------------------------------------------------------- /instruction/images/exercise02-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-11.png -------------------------------------------------------------------------------- /instruction/images/exercise03-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-01.png -------------------------------------------------------------------------------- /instruction/images/exercise03-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-02.png -------------------------------------------------------------------------------- /instruction/images/exercise03-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-03.png -------------------------------------------------------------------------------- /instruction/images/exercise03-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-04.png -------------------------------------------------------------------------------- /instruction/images/exercise03-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-05.png -------------------------------------------------------------------------------- /instruction/images/exercise03-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-06.png -------------------------------------------------------------------------------- /instruction/images/exercise03-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-07.png -------------------------------------------------------------------------------- /instruction/images/exercise03-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-08.png -------------------------------------------------------------------------------- /instruction/images/exercise03-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-09.png -------------------------------------------------------------------------------- /instruction/images/exercise03-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-10.png -------------------------------------------------------------------------------- /instruction/images/exercise03-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-11.png -------------------------------------------------------------------------------- /instruction/images/exercise03-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-12.png -------------------------------------------------------------------------------- /instruction/images/exercise03-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-13.png -------------------------------------------------------------------------------- /instruction/images/exercise03-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-14.png -------------------------------------------------------------------------------- /instruction/images/exercise03-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-15.png -------------------------------------------------------------------------------- /instruction/images/exercise03-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-16.png -------------------------------------------------------------------------------- /instruction/images/exercise03-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-17.png -------------------------------------------------------------------------------- /exercise/03-netflix/configserver/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring.cloud.config.server.uri: http://localhost:8080/git/root/config-repo.git -------------------------------------------------------------------------------- /instruction/images/import-exercise01-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-01.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-02.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-03.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-04.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-05.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-06.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-07.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-08.png -------------------------------------------------------------------------------- /instruction/images/import-exercise01-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-09.png -------------------------------------------------------------------------------- /instruction/images/import-exercise02-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise02-01.png -------------------------------------------------------------------------------- /instruction/images/import-exercise02-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise02-02.png -------------------------------------------------------------------------------- /instruction/images/import-exercise03-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise03-01.png -------------------------------------------------------------------------------- /instruction/images/import-exercise03-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise03-02.png -------------------------------------------------------------------------------- /instruction/images/system-exercise03-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-01.png -------------------------------------------------------------------------------- /instruction/images/system-exercise03-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-02.png -------------------------------------------------------------------------------- /instruction/images/system-exercise03-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-03.png -------------------------------------------------------------------------------- /instruction/images/system-exercise03-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-04.png -------------------------------------------------------------------------------- /instruction/images/system-exercise03-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-05.png -------------------------------------------------------------------------------- /instruction/images/system-exercise03-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-06.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | .idea 3 | target 4 | .DS_Store 5 | .classpath 6 | .project 7 | .settings 8 | *-diff.txt 9 | Note.txt 10 | *~ 11 | *.iml -------------------------------------------------------------------------------- /exercise/02-distributed-config/configserver/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring.cloud.config.server.uri: http://localhost:8080/git/root/config-repo.git -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "repository"] 2 | path = repository 3 | url = https://github.com/making/repository.git 4 | [submodule "software"] 5 | path = software 6 | url = https://github.com/making/software.git 7 | -------------------------------------------------------------------------------- /exercise/01-urlshortener/urlshortener/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 2 | spring: 3 | thymeleaf.cache: false 4 | main.show-banner: false -------------------------------------------------------------------------------- /exercise/02-distributed-config/urlshortener/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 2 | spring: 3 | thymeleaf.cache: false 4 | main.show-banner: false -------------------------------------------------------------------------------- /exercise/03-netflix/eureka-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8761 3 | 4 | eureka: 5 | instance: 6 | hostname: localhost 7 | client: 8 | registerWithEureka: false 9 | fetchRegistry: false 10 | serviceUrl: 11 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, FILE 2 | log4j.appender.FILE=org.apache.log4j.ConsoleAppender 3 | log4j.appender.FILE.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %C:%L [%C{1}] [%M]: %m%n 5 | 6 | log4j.appender.FILE.httpclient=ERROR 7 | -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | info: 2 | component: Hystrix Dashboard 3 | 4 | endpoints: 5 | restart: 6 | enabled: true 7 | shutdown: 8 | enabled: true 9 | 10 | server: 11 | port: 7979 12 | 13 | eureka: 14 | client: 15 | serviceUrl: 16 | defaultZone: http://localhost:8761/eureka/ 17 | instance: 18 | hostname: ${APPLICATION_DOMAIN:127.0.0.1} 19 | nonSecurePort: ${server.port} 20 | -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener-ui/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 2 | spring: 3 | thymeleaf.cache: false 4 | main.show-banner: false 5 | 6 | server.port: 9999 7 | 8 | eureka: 9 | client: 10 | serviceUrl: 11 | defaultZone: http://localhost:8761/eureka/ 12 | instance: 13 | hostname: ${APPLICATION_DOMAIN:127.0.0.1} 14 | nonSecurePort: ${server.port} -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 2 | spring: 3 | thymeleaf.cache: false 4 | main.show-banner: false 5 | 6 | server.port: 8081 7 | 8 | eureka: 9 | client: 10 | serviceUrl: 11 | defaultZone: http://localhost:8761/eureka/ 12 | instance: 13 | hostname: ${APPLICATION_DOMAIN:127.0.0.1} 14 | nonSecurePort: ${server.port} -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/README.md: -------------------------------------------------------------------------------- 1 | # Hystrix Dashboard 2 | 3 | Run this app as a normal Spring Boot app and then go to the home page 4 | in a browser. If you run from this project it will be on port 7979 5 | (per the `application.yml`). On the home page is a form where you can 6 | enter the URL for an event stream to monitor, for example (the 7 | customers service running locally): 8 | `http://localhost:9000/hystrix.stream`. Any app that uses 9 | `@EnableHystrix` will expose the stream. 10 | 11 | To aggregate many streams together you can use the 12 | [Turbine sample](https://github.com/spring-cloud-samples/turbine). 13 | -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener-ui/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UrlShortener 6 | 7 | 8 |

Shorten! http://goo.gl/aaaaa

9 | 10 |
11 | URL: 12 | error! 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /exercise/03-netflix/eureka-server/src/main/java/demo/EurekaServer.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @EnableAutoConfiguration 11 | @ComponentScan 12 | @RestController 13 | @EnableEurekaServer 14 | @EnableEurekaClient 15 | public class EurekaServer { 16 | public static void main(String[] args) { 17 | SpringApplication.run(EurekaServer.class, args); 18 | } 19 | } -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/src/main/java/hystrixdashboard/HystrixDashboardApplication.java: -------------------------------------------------------------------------------- 1 | package hystrixdashboard; 2 | 3 | import hystrixdashboard.stream.MockStreamServlet; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.boot.context.embedded.ServletRegistrationBean; 7 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 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.stereotype.Controller; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | 15 | @Configuration 16 | @ComponentScan 17 | @EnableAutoConfiguration 18 | @Controller 19 | @EnableHystrixDashboard 20 | @EnableEurekaClient 21 | public class HystrixDashboardApplication { 22 | 23 | public static void main(String[] args) { 24 | SpringApplication.run(HystrixDashboardApplication.class, args); 25 | } 26 | 27 | @RequestMapping("/") 28 | public String home() { 29 | return "forward:/hystrix/index.html"; 30 | } 31 | 32 | @Bean 33 | public ServletRegistrationBean mockStreamServlet() { 34 | return new ServletRegistrationBean(new MockStreamServlet(), "/mock.stream"); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /exercise/01-urlshortener/urlshortener/src/main/java/demo/UrlShortener.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.apache.commons.validator.routines.UrlValidator; 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.context.annotation.ComponentScan; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | @EnableAutoConfiguration 16 | @ComponentScan 17 | @RestController 18 | public class UrlShortener { 19 | public static void main(String[] args) { 20 | SpringApplication.run(UrlShortener.class, args); 21 | } 22 | 23 | @Value("${urlshorten.url:http://localhost:${server.port:8080}}") 24 | String urlShortenUrl; 25 | 26 | final ConcurrentHashMap urlMap = new ConcurrentHashMap<>(); 27 | final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"}); 28 | 29 | /** 30 | * curl -v -X POST http://localhost:8080 -d "url=http://google.com" 31 | */ 32 | @RequestMapping(value = "/", method = RequestMethod.POST) 33 | ResponseEntity save(@RequestParam String url) { 34 | if (urlValidator.isValid(url)) { 35 | String hash = ""/* TODO (1) URLをハッシュ化。ハッシュアルゴリズムには 32-bit murmur3 algorithm を使用する。 */; 36 | // ヒント: com.google.common.hash.Hashing.murmur3_32()を使う 37 | // TODO (2) urlMapにhashに紐づくURLを追加する。 38 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK); 39 | } else { 40 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST); 41 | } 42 | } 43 | 44 | /** 45 | * curl -v -X GET http://localhost:8080/58f3ae21 46 | */ 47 | @RequestMapping(value = "{hash}", method = RequestMethod.GET) 48 | ResponseEntity get(@PathVariable String hash) { 49 | String url = urlMap.get(hash); 50 | // 本当はリダイレクトするのだが、今回はレスポンスボディに入れて200を返す 51 | if (url != null) { 52 | return new ResponseEntity<>(url, HttpStatus.OK); 53 | } else { 54 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener/src/test/java/demo/UrlShortenerTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.jayway.restassured.RestAssured; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.test.IntegrationTest; 9 | import org.springframework.boot.test.SpringApplicationConfiguration; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | import org.springframework.test.context.web.WebAppConfiguration; 12 | 13 | import static com.jayway.restassured.RestAssured.given; 14 | import static org.hamcrest.Matchers.is; 15 | 16 | @RunWith(SpringJUnit4ClassRunner.class) 17 | @SpringApplicationConfiguration(classes = UrlShortener.class) 18 | @WebAppConfiguration 19 | @IntegrationTest({"server.port:0"}) 20 | public class UrlShortenerTest { 21 | @Value("${local.server.port}") 22 | int port; 23 | 24 | @Before 25 | public void setUp() { 26 | RestAssured.port = port; 27 | } 28 | 29 | @Test 30 | public void testSaveAndGet() throws Exception { 31 | given().log().all() 32 | .when() 33 | .body("url=http://google.com") 34 | .contentType("application/x-www-form-urlencoded") 35 | .post() 36 | .then() 37 | .log().all() 38 | .statusCode(200) 39 | .body(is("http://localhost:0/58f3ae21")); 40 | 41 | given().log().all() 42 | .when() 43 | .get("/58f3ae21") 44 | .then() 45 | .log().all() 46 | .statusCode(200) 47 | .body(is("http://google.com")); 48 | } 49 | 50 | @Test 51 | public void testInvalidUrl() throws Exception { 52 | given().log().all() 53 | .when() 54 | .body("url=hoge") 55 | .contentType("application/x-www-form-urlencoded") 56 | .post() 57 | .then() 58 | .log().all() 59 | .statusCode(400); 60 | } 61 | 62 | @Test 63 | public void testNotExistHash() throws Exception { 64 | given().log().all() 65 | .when() 66 | .get("/hoge") 67 | .then() 68 | .log().all() 69 | .statusCode(404); 70 | } 71 | } -------------------------------------------------------------------------------- /exercise/01-urlshortener/urlshortener/src/test/java/demo/UrlShortenerTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.jayway.restassured.RestAssured; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.test.IntegrationTest; 9 | import org.springframework.boot.test.SpringApplicationConfiguration; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | import org.springframework.test.context.web.WebAppConfiguration; 12 | 13 | import static com.jayway.restassured.RestAssured.given; 14 | import static org.hamcrest.Matchers.is; 15 | 16 | @RunWith(SpringJUnit4ClassRunner.class) 17 | @SpringApplicationConfiguration(classes = UrlShortener.class) 18 | @WebAppConfiguration 19 | @IntegrationTest({"server.port:0"}) 20 | public class UrlShortenerTest { 21 | @Value("${local.server.port}") 22 | int port; 23 | 24 | @Before 25 | public void setUp() { 26 | RestAssured.port = port; 27 | } 28 | 29 | @Test 30 | public void testSaveAndGet() throws Exception { 31 | given().log().all() 32 | .when() 33 | .body("url=http://google.com") 34 | .contentType("application/x-www-form-urlencoded") 35 | .post() 36 | .then() 37 | .log().all() 38 | .statusCode(200) 39 | .body(is("http://localhost:0/58f3ae21")); 40 | 41 | given().log().all() 42 | .when() 43 | .get("/58f3ae21") 44 | .then() 45 | .log().all() 46 | .statusCode(200) 47 | .body(is("http://google.com")); 48 | } 49 | 50 | @Test 51 | public void testInvalidUrl() throws Exception { 52 | given().log().all() 53 | .when() 54 | .body("url=hoge") 55 | .contentType("application/x-www-form-urlencoded") 56 | .post() 57 | .then() 58 | .log().all() 59 | .statusCode(400); 60 | } 61 | 62 | @Test 63 | public void testNotExistHash() throws Exception { 64 | given().log().all() 65 | .when() 66 | .get("/hoge") 67 | .then() 68 | .log().all() 69 | .statusCode(404); 70 | } 71 | } -------------------------------------------------------------------------------- /exercise/02-distributed-config/urlshortener/src/test/java/demo/UrlShortenerTest.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.jayway.restassured.RestAssured; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.test.IntegrationTest; 9 | import org.springframework.boot.test.SpringApplicationConfiguration; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | import org.springframework.test.context.web.WebAppConfiguration; 12 | 13 | import static com.jayway.restassured.RestAssured.given; 14 | import static org.hamcrest.Matchers.is; 15 | 16 | @RunWith(SpringJUnit4ClassRunner.class) 17 | @SpringApplicationConfiguration(classes = UrlShortener.class) 18 | @WebAppConfiguration 19 | @IntegrationTest({"server.port:0"}) 20 | public class UrlShortenerTest { 21 | @Value("${local.server.port}") 22 | int port; 23 | 24 | @Before 25 | public void setUp() { 26 | RestAssured.port = port; 27 | } 28 | 29 | @Test 30 | public void testSaveAndGet() throws Exception { 31 | given().log().all() 32 | .when() 33 | .body("url=http://google.com") 34 | .contentType("application/x-www-form-urlencoded") 35 | .post() 36 | .then() 37 | .log().all() 38 | .statusCode(200) 39 | .body(is("http://localhost:0/58f3ae21")); 40 | 41 | given().log().all() 42 | .when() 43 | .get("/58f3ae21") 44 | .then() 45 | .log().all() 46 | .statusCode(200) 47 | .body(is("http://google.com")); 48 | } 49 | 50 | @Test 51 | public void testInvalidUrl() throws Exception { 52 | given().log().all() 53 | .when() 54 | .body("url=hoge") 55 | .contentType("application/x-www-form-urlencoded") 56 | .post() 57 | .then() 58 | .log().all() 59 | .statusCode(400); 60 | } 61 | 62 | @Test 63 | public void testNotExistHash() throws Exception { 64 | given().log().all() 65 | .when() 66 | .get("/hoge") 67 | .then() 68 | .log().all() 69 | .statusCode(404); 70 | } 71 | } -------------------------------------------------------------------------------- /exercise/02-distributed-config/urlshortener/src/main/java/demo/UrlShortener.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.apache.commons.validator.routines.UrlValidator; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.cloud.context.config.annotation.RefreshScope; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.data.redis.core.StringRedisTemplate; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import java.nio.charset.StandardCharsets; 16 | 17 | @EnableAutoConfiguration 18 | @ComponentScan 19 | @RestController 20 | //@RefreshScope 21 | public class UrlShortener { 22 | public static void main(String[] args) { 23 | SpringApplication.run(UrlShortener.class, args); 24 | } 25 | 26 | @Value("${urlshorten.url:http://localhost:${server.port:8080}}") 27 | String urlShortenUrl; 28 | 29 | @Autowired 30 | StringRedisTemplate redisTemplate; 31 | 32 | final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"}); 33 | 34 | /** 35 | * curl -v -X POST http://localhost:8080 -d "url=http://google.com" 36 | */ 37 | @RequestMapping(value = "/", method = RequestMethod.POST) 38 | ResponseEntity save(@RequestParam String url) { 39 | System.out.println(url); 40 | if (urlValidator.isValid(url)) { 41 | String hash = com.google.common.hash.Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString(); 42 | redisTemplate.opsForValue().set(hash, url); 43 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK); 44 | } else { 45 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST); 46 | } 47 | } 48 | 49 | /** 50 | * curl -v -X GET http://localhost:8080/58f3ae21 51 | */ 52 | @RequestMapping(value = "{hash}", method = RequestMethod.GET) 53 | ResponseEntity get(@PathVariable String hash) { 54 | String url = redisTemplate.opsForValue().get(hash); 55 | // 本当はリダイレクトするのだが、今回はレスポンスボディに入れて200を返す 56 | if (url != null) { 57 | return new ResponseEntity<>(url, HttpStatus.OK); 58 | } else { 59 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener/src/main/java/demo/UrlShortener.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.apache.commons.validator.routines.UrlValidator; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.cloud.context.config.annotation.RefreshScope; 9 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 10 | import org.springframework.context.annotation.ComponentScan; 11 | import org.springframework.data.redis.core.StringRedisTemplate; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.nio.charset.StandardCharsets; 17 | 18 | @EnableAutoConfiguration 19 | @ComponentScan 20 | @RestController 21 | @RefreshScope 22 | @EnableEurekaClient 23 | public class UrlShortener { 24 | public static void main(String[] args) { 25 | SpringApplication.run(UrlShortener.class, args); 26 | } 27 | 28 | @Value("${urlshorten.url:http://localhost:${server.port:8080}}") 29 | String urlShortenUrl; 30 | 31 | @Autowired 32 | StringRedisTemplate redisTemplate; 33 | 34 | final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"}); 35 | 36 | /** 37 | * curl -v -X POST http://localhost:8080 -d "url=http://google.com" 38 | */ 39 | @RequestMapping(value = "/", method = RequestMethod.POST) 40 | ResponseEntity save(@RequestParam String url) { 41 | System.out.println(url); 42 | if (urlValidator.isValid(url)) { 43 | String hash = com.google.common.hash.Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString(); 44 | redisTemplate.opsForValue().set(hash, url); 45 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK); 46 | } else { 47 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST); 48 | } 49 | } 50 | 51 | /** 52 | * curl -v -X GET http://localhost:8080/58f3ae21 53 | */ 54 | @RequestMapping(value = "{hash}", method = RequestMethod.GET) 55 | ResponseEntity get(@PathVariable String hash) { 56 | String url = redisTemplate.opsForValue().get(hash); 57 | // 本当はリダイレクトするのだが、今回はレスポンスボディに入れて200を返す 58 | if (url != null) { 59 | return new ResponseEntity<>(url, HttpStatus.OK); 60 | } else { 61 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /exercise/01-urlshortener/urlshortener/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise01-urlshortener 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exercise01-urlshortener 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.1.8.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-redis 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | com.google.guava 33 | guava 34 | 18.0 35 | 36 | 37 | commons-validator 38 | commons-validator 39 | 1.4.0 40 | 41 | 42 | com.jayway.restassured 43 | rest-assured 44 | 2.3.2 45 | test 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | 54 | 55 | UTF-8 56 | demo.UrlShortener 57 | 1.8 58 | 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-maven-plugin 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /exercise/03-netflix/configserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise03-configserver 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exercise03-configserver 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.1.8.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starters 27 | 1.0.0.M2 28 | pom 29 | import 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-config-server 38 | 39 | 40 | 41 | 42 | UTF-8 43 | org.springframework.cloud.config.server.ConfigServerApplication 44 | 1.8 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | 55 | 56 | 57 | 58 | spring-milestones 59 | Spring Milestones 60 | http://repo.spring.io/milestone 61 | 62 | false 63 | 64 | 65 | 66 | spring-snapshots 67 | Spring Snapshots 68 | http://repo.spring.io/libs-snapshot-local 69 | 70 | true 71 | 72 | 73 | 74 | 75 | 76 | spring-milestones 77 | Spring Milestones 78 | http://repo.spring.io/milestone 79 | 80 | false 81 | 82 | 83 | 84 | spring-snapshots 85 | Spring Snapshots 86 | http://repo.spring.io/libs-snapshot-local 87 | 88 | true 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /exercise/02-distributed-config/configserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise02-configserver 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exercise02-configserver 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.1.8.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starters 27 | 1.0.0.M2 28 | pom 29 | import 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-config-server 38 | 39 | 40 | 41 | 42 | UTF-8 43 | org.springframework.cloud.config.server.ConfigServerApplication 44 | 1.8 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | 55 | 56 | 57 | 58 | spring-milestones 59 | Spring Milestones 60 | http://repo.spring.io/milestone 61 | 62 | false 63 | 64 | 65 | 66 | spring-snapshots 67 | Spring Snapshots 68 | http://repo.spring.io/libs-snapshot-local 69 | 70 | true 71 | 72 | 73 | 74 | 75 | 76 | spring-milestones 77 | Spring Milestones 78 | http://repo.spring.io/milestone 79 | 80 | false 81 | 82 | 83 | 84 | spring-snapshots 85 | Spring Snapshots 86 | http://repo.spring.io/libs-snapshot-local 87 | 88 | true 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.demo 7 | exercise03-hystrix-dashboard 8 | jar 9 | exercise03-hystrix-dashboard 10 | 1.0.0.BUILD-SNAPSHOT 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 1.1.8.RELEASE 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-maven-plugin 25 | 26 | 27 | 28 | maven-deploy-plugin 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 1.0.0.M2 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starters 45 | ${spring-cloud-config.version} 46 | pom 47 | import 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.cloud 54 | spring-cloud-starter-hystrix-dashboard 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 | spring-snapshots 87 | Spring Snapshots 88 | http://repo.spring.io/libs-snapshot-local 89 | 90 | true 91 | 92 | 93 | 94 | spring-milestones 95 | Spring Milestones 96 | http://repo.spring.io/libs-milestone-local 97 | 98 | false 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /exercise/03-netflix/eureka-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise03-eureka-server 8 | 1.0-SNAPSHOT 9 | jar 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 1.1.8.RELEASE 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starters 23 | 1.0.0.M2 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | UTF-8 32 | demo.EurekaServer 33 | 1.8 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-eureka-server 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | spring-milestones 49 | Spring Milestones 50 | http://repo.spring.io/milestone 51 | 52 | false 53 | 54 | 55 | 56 | spring-snapshots 57 | Spring Snapshots 58 | http://repo.spring.io/libs-snapshot-local 59 | 60 | true 61 | 62 | 63 | 64 | 65 | 66 | spring-milestones 67 | Spring Milestones 68 | http://repo.spring.io/milestone 69 | 70 | false 71 | 72 | 73 | 74 | spring-snapshots 75 | Spring Snapshots 76 | http://repo.spring.io/libs-snapshot-local 77 | 78 | true 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 89 | 90 | 91 | 92 | com.netflix.eureka 93 | eureka-core 94 | 95 | 96 | com.netflix.eureka 97 | eureka-client 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /exercise/03-netflix/hystrix-dashboard/src/main/java/hystrixdashboard/stream/MockStreamServlet.java: -------------------------------------------------------------------------------- 1 | package hystrixdashboard.stream; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.*; 11 | import java.nio.charset.Charset; 12 | 13 | /** 14 | * Simulate an event stream URL by retrieving pre-canned data instead of going to live servers. 15 | */ 16 | public class MockStreamServlet extends HttpServlet { 17 | private static final long serialVersionUID = 1L; 18 | private static final Logger logger = LoggerFactory.getLogger(MockStreamServlet.class); 19 | 20 | public MockStreamServlet() { 21 | super(); 22 | } 23 | 24 | /** 25 | * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) 26 | */ 27 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 28 | String filename = request.getParameter("file"); 29 | if (filename == null) { 30 | // default to using hystrix.stream 31 | filename = "hystrix.stream"; 32 | } else { 33 | // strip any .. / characters to avoid security problems 34 | filename = filename.replaceAll("\\.\\.", ""); 35 | filename = filename.replaceAll("/", ""); 36 | } 37 | int delay = 500; 38 | String delayArg = request.getParameter("delay"); 39 | if (delayArg != null) { 40 | delay = Integer.parseInt(delayArg); 41 | } 42 | 43 | int batch = 1; 44 | String batchArg = request.getParameter("batch"); 45 | if (batchArg != null) { 46 | batch = Integer.parseInt(batchArg); 47 | } 48 | 49 | String data = getFileFromPackage(filename); 50 | String lines[] = data.split("\n"); 51 | 52 | response.setContentType("text/event-stream"); 53 | response.setCharacterEncoding("UTF-8"); 54 | 55 | int batchCount = 0; 56 | // loop forever unless the user closes the connection 57 | for (;;) { 58 | for (String s : lines) { 59 | s = s.trim(); 60 | if (s.length() > 0) { 61 | try { 62 | response.getWriter().println(s); 63 | response.getWriter().println(""); // a newline is needed after each line for the events to trigger 64 | response.getWriter().flush(); 65 | batchCount++; 66 | } catch (Exception e) { 67 | logger.warn("Exception writing mock data to output.", e); 68 | // most likely the user closed the connection 69 | return; 70 | } 71 | if (batchCount == batch) { 72 | // we insert the delay whenever we finish a batch 73 | try { 74 | // simulate the delays we get from the real feed 75 | Thread.sleep(delay); 76 | } catch (InterruptedException e) { 77 | // ignore 78 | } 79 | // reset 80 | batchCount = 0; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | private String getFileFromPackage(String filename) { 88 | try { 89 | String file = "/" + this.getClass().getPackage().getName().replace('.', '/') + "/" + filename; 90 | InputStream is = this.getClass().getResourceAsStream(file); 91 | try { 92 | /* this is FAR too much work just to get a string from a file */ 93 | BufferedReader in = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); 94 | StringWriter s = new StringWriter(); 95 | int c = -1; 96 | while ((c = in.read()) > -1) { 97 | s.write(c); 98 | } 99 | return s.toString(); 100 | } finally { 101 | is.close(); 102 | } 103 | } catch (Exception e) { 104 | throw new RuntimeException("Could not find file: " + filename, e); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /exercise/02-distributed-config/urlshortener/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise02-urlshortener 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exercise02-urlshortener 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.1.8.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starters 27 | 1.0.0.M2 28 | pom 29 | import 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-redis 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 43 | 44 | org.springframework.cloud 45 | spring-cloud-starter 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-actuator 50 | 51 | 52 | com.google.guava 53 | guava 54 | 18.0 55 | 56 | 57 | commons-validator 58 | commons-validator 59 | 1.4.0 60 | 61 | 62 | com.jayway.restassured 63 | rest-assured 64 | 2.3.2 65 | test 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-test 70 | test 71 | 72 | 73 | 74 | 75 | UTF-8 76 | demo.UrlShortener 77 | 1.8 78 | 79 | 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-maven-plugin 85 | 86 | 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | http://repo.spring.io/milestone 93 | 94 | false 95 | 96 | 97 | 98 | spring-snapshots 99 | Spring Snapshots 100 | http://repo.spring.io/libs-snapshot-local 101 | 102 | true 103 | 104 | 105 | 106 | 107 | 108 | spring-milestones 109 | Spring Milestones 110 | http://repo.spring.io/milestone 111 | 112 | false 113 | 114 | 115 | 116 | spring-snapshots 117 | Spring Snapshots 118 | http://repo.spring.io/libs-snapshot-local 119 | 120 | true 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener-ui/src/main/java/demo/App.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | import org.hibernate.validator.constraints.URL; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 12 | import org.springframework.cloud.context.config.annotation.RefreshScope; 13 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 14 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 15 | import org.springframework.context.annotation.ComponentScan; 16 | import org.springframework.context.annotation.Configuration; 17 | import org.springframework.http.HttpEntity; 18 | import org.springframework.http.HttpHeaders; 19 | import org.springframework.http.MediaType; 20 | import org.springframework.stereotype.Component; 21 | import org.springframework.stereotype.Controller; 22 | import org.springframework.util.LinkedMultiValueMap; 23 | import org.springframework.util.MultiValueMap; 24 | import org.springframework.validation.BindingResult; 25 | import org.springframework.validation.annotation.Validated; 26 | import org.springframework.web.bind.annotation.ModelAttribute; 27 | import org.springframework.web.bind.annotation.PathVariable; 28 | import org.springframework.web.bind.annotation.RequestMapping; 29 | import org.springframework.web.bind.annotation.RequestMethod; 30 | import org.springframework.web.client.RestTemplate; 31 | import org.springframework.web.servlet.mvc.support.RedirectAttributes; 32 | 33 | import java.io.IOException; 34 | 35 | import static java.util.Collections.singletonMap; 36 | 37 | @EnableAutoConfiguration 38 | @ComponentScan 39 | @Controller 40 | @Configuration 41 | @EnableEurekaClient 42 | @EnableHystrix 43 | public class App { 44 | @Autowired 45 | RemoteCommand remoteCommand; 46 | 47 | public static void main(String[] args) { 48 | SpringApplication.run(App.class, args); 49 | } 50 | 51 | @ModelAttribute 52 | ShortenForm setupForm() { 53 | return new ShortenForm(); 54 | } 55 | 56 | @RequestMapping("/") 57 | String home() { 58 | return "index"; 59 | } 60 | 61 | @RequestMapping(value = "shorten", method = RequestMethod.POST) 62 | String shorten(@Validated ShortenForm form, BindingResult result, RedirectAttributes attributes) throws IOException { 63 | if (result.hasErrors()) { 64 | return "index"; 65 | } 66 | String shortenUrl = remoteCommand.shorten(form.getUrl()); 67 | attributes.addFlashAttribute("shortenUrl", shortenUrl); 68 | return "redirect:/"; 69 | } 70 | 71 | @RequestMapping(value = "{hash}", method = RequestMethod.GET) 72 | String redirect(@PathVariable String hash) { 73 | String url = remoteCommand.getUrl(hash); 74 | return "redirect:" + url; 75 | } 76 | } 77 | 78 | class ShortenForm { 79 | @NotEmpty 80 | @URL 81 | private String url; 82 | 83 | public void setUrl(String url) { 84 | this.url = url; 85 | } 86 | 87 | public String getUrl() { 88 | return url; 89 | } 90 | } 91 | 92 | @Component 93 | @RefreshScope 94 | class RemoteCommand { 95 | @Autowired 96 | RestTemplate restTemplate; 97 | @Value("${urlshorten.api.url:http://localhost:8081}") 98 | String apiUrl; 99 | Logger logger = LoggerFactory.getLogger(RemoteCommand.class); 100 | 101 | @HystrixCommand(fallbackMethod = "defaultShorten") 102 | public String shorten(String url) { 103 | logger.info("calling shorten {}", url); 104 | HttpHeaders headers = new HttpHeaders(); 105 | headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 106 | MultiValueMap body = new LinkedMultiValueMap<>(); 107 | body.add("url", url); 108 | HttpEntity> request = new HttpEntity<>(body, headers); 109 | return restTemplate.postForEntity(apiUrl, request, String.class).getBody(); 110 | } 111 | 112 | public String defaultShorten(String url) { 113 | logger.info("failed shorten {}", url); 114 | return "failed!"; 115 | } 116 | 117 | @HystrixCommand(fallbackMethod = "defaultGetUrl") 118 | public String getUrl(String hash) { 119 | logger.info("calling getUrl {}", hash); 120 | return restTemplate.getForObject(apiUrl + "/{hash}", String.class, singletonMap("hash", hash)); 121 | } 122 | 123 | public String defaultGetUrl(String hash) { 124 | logger.info("failed getUrl {}", hash); 125 | return "failed!"; 126 | } 127 | } -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise03-urlshortener 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exercise03-urlshortener 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.1.8.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starters 27 | 1.0.0.M2 28 | pom 29 | import 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-redis 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 43 | 44 | org.springframework.cloud 45 | spring-cloud-starter 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-starter-eureka 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-actuator 54 | 55 | 56 | com.google.guava 57 | guava 58 | 18.0 59 | 60 | 61 | commons-validator 62 | commons-validator 63 | 1.4.0 64 | 65 | 66 | com.jayway.restassured 67 | rest-assured 68 | 2.3.2 69 | test 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-test 74 | test 75 | 76 | 77 | 78 | 79 | UTF-8 80 | demo.UrlShortener 81 | 1.8 82 | 83 | 84 | 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-maven-plugin 89 | 90 | 91 | 92 | 93 | 94 | spring-milestones 95 | Spring Milestones 96 | http://repo.spring.io/milestone 97 | 98 | false 99 | 100 | 101 | 102 | spring-snapshots 103 | Spring Snapshots 104 | http://repo.spring.io/libs-snapshot-local 105 | 106 | true 107 | 108 | 109 | 110 | 111 | 112 | spring-milestones 113 | Spring Milestones 114 | http://repo.spring.io/milestone 115 | 116 | false 117 | 118 | 119 | 120 | spring-snapshots 121 | Spring Snapshots 122 | http://repo.spring.io/libs-snapshot-local 123 | 124 | true 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /exercise/03-netflix/urlshortener-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | demo 7 | exercise03-urlshortener-ui 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exercise03-urlshortener-ui 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.1.8.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starters 27 | 1.0.0.M2 28 | pom 29 | import 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-redis 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-thymeleaf 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-starter 50 | 51 | 52 | org.springframework.cloud 53 | spring-cloud-starter-eureka 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-starter-hystrix 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-actuator 62 | 63 | 64 | com.google.guava 65 | guava 66 | 17.0 67 | 68 | 69 | commons-validator 70 | commons-validator 71 | 1.4.0 72 | 73 | 74 | com.jayway.restassured 75 | rest-assured 76 | 2.3.2 77 | test 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-test 82 | test 83 | 84 | 85 | 86 | 87 | UTF-8 88 | demo.App 89 | 1.8 90 | 91 | 92 | 93 | 94 | 95 | org.springframework.boot 96 | spring-boot-maven-plugin 97 | 98 | 99 | 100 | 101 | 102 | spring-milestones 103 | Spring Milestones 104 | http://repo.spring.io/milestone 105 | 106 | false 107 | 108 | 109 | 110 | spring-snapshots 111 | Spring Snapshots 112 | http://repo.spring.io/libs-snapshot-local 113 | 114 | true 115 | 116 | 117 | 118 | 119 | 120 | spring-milestones 121 | Spring Milestones 122 | http://repo.spring.io/milestone 123 | 124 | false 125 | 126 | 127 | 128 | spring-snapshots 129 | Spring Snapshots 130 | http://repo.spring.io/libs-snapshot-local 131 | 132 | true 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /instruction/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /instruction/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\TERASOLUNAGlobalFrameworkDevelopmentPolicy.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\TERASOLUNAGlobalFrameworkDevelopmentPolicy.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /instruction/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # TERASOLUNA Global Framework Development Policy documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Apr 17 19:06:14 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.pngmath', 'sphinx.ext.todo' 33 | ] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ['_templates'] 37 | 38 | # The suffix of source filenames. 39 | source_suffix = '.rst' 40 | 41 | # The encoding of source files. 42 | #source_encoding = 'utf-8-sig' 43 | 44 | # The master toctree document. 45 | master_doc = 'readme' 46 | 47 | # General information about the project. 48 | project = u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書' 49 | copyright = u'2014, making' 50 | 51 | # The version info for the project you're documenting, acts as replacement for 52 | # |version| and |release|, also used in various other places throughout the 53 | # built documents. 54 | # 55 | # The short X.Y version. 56 | version = '1.0.0' 57 | # The full version, including alpha/beta/rc tags. 58 | release = '20141111' 59 | 60 | # The language for content autogenerated by Sphinx. Refer to documentation 61 | # for a list of supported languages. 62 | #language = None 63 | 64 | # There are two options for replacing |today|: either, you set today to some 65 | # non-false value, then it is used: 66 | #today = '' 67 | # Else, today_fmt is used as the format for a strftime call. 68 | #today_fmt = '%B %d, %Y' 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | exclude_patterns = ['_build'] 73 | 74 | # The reST default role (used for this markup: `text`) to use for all 75 | # documents. 76 | #default_role = None 77 | 78 | # If true, '()' will be appended to :func: etc. cross-reference text. 79 | #add_function_parentheses = True 80 | 81 | # If true, the current module name will be prepended to all description 82 | # unit titles (such as .. function::). 83 | #add_module_names = True 84 | 85 | # If true, sectionauthor and moduleauthor directives will be shown in the 86 | # output. They are ignored by default. 87 | #show_authors = False 88 | 89 | # The name of the Pygments (syntax highlighting) style to use. 90 | pygments_style = 'sphinx' 91 | 92 | # A list of ignored prefixes for module index sorting. 93 | #modindex_common_prefix = [] 94 | 95 | # If true, keep warnings as "system message" paragraphs in the built documents. 96 | #keep_warnings = False 97 | 98 | 99 | # -- Options for HTML output ---------------------------------------------- 100 | 101 | # The theme to use for HTML and HTML Help pages. See the documentation for 102 | # a list of builtin themes. 103 | html_theme = 'default' 104 | 105 | # Theme options are theme-specific and customize the look and feel of a theme 106 | # further. For a list of options available for each theme, see the 107 | # documentation. 108 | #html_theme_options = {} 109 | 110 | # Add any paths that contain custom themes here, relative to this directory. 111 | #html_theme_path = [] 112 | 113 | # The name for this set of Sphinx documents. If None, it defaults to 114 | # " v documentation". 115 | #html_title = None 116 | 117 | # A shorter title for the navigation bar. Default is the same as html_title. 118 | #html_short_title = None 119 | 120 | # The name of an image file (relative to this directory) to place at the top 121 | # of the sidebar. 122 | #html_logo = None 123 | 124 | # The name of an image file (within the static path) to use as favicon of the 125 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 126 | # pixels large. 127 | #html_favicon = None 128 | 129 | # Add any paths that contain custom static files (such as style sheets) here, 130 | # relative to this directory. They are copied after the builtin static files, 131 | # so a file named "default.css" will overwrite the builtin "default.css". 132 | html_static_path = ['_static'] 133 | 134 | # Add any extra paths that contain custom files (such as robots.txt or 135 | # .htaccess) here, relative to this directory. These files are copied 136 | # directly to the root of the documentation. 137 | #html_extra_path = [] 138 | 139 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 140 | # using the given strftime format. 141 | #html_last_updated_fmt = '%b %d, %Y' 142 | 143 | # If true, SmartyPants will be used to convert quotes and dashes to 144 | # typographically correct entities. 145 | #html_use_smartypants = True 146 | 147 | # Custom sidebar templates, maps document names to template names. 148 | #html_sidebars = {} 149 | 150 | # Additional templates that should be rendered to pages, maps page names to 151 | # template names. 152 | #html_additional_pages = {} 153 | 154 | # If false, no module index is generated. 155 | #html_domain_indices = True 156 | 157 | # If false, no index is generated. 158 | #html_use_index = True 159 | 160 | # If true, the index is split into individual pages for each letter. 161 | #html_split_index = False 162 | 163 | # If true, links to the reST sources are added to the pages. 164 | #html_show_sourcelink = True 165 | 166 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 167 | #html_show_sphinx = True 168 | 169 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 170 | #html_show_copyright = True 171 | 172 | # If true, an OpenSearch description file will be output, and all pages will 173 | # contain a tag referring to it. The value of this option must be the 174 | # base URL from which the finished HTML is served. 175 | #html_use_opensearch = '' 176 | 177 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 178 | #html_file_suffix = None 179 | 180 | # Output file base name for HTML help builder. 181 | htmlhelp_basename = 'ShpinxBlankProject' 182 | 183 | 184 | # -- Options for LaTeX output --------------------------------------------- 185 | language = 'ja' 186 | latex_docclass = {'manual': 'jsbook'} 187 | latex_elements = { 188 | # The paper size ('letterpaper' or 'a4paper'). 189 | #'papersize': 'letterpaper', 190 | 'papersize': 'a4paper', 191 | 'classoptions': ',dvipdfmx,openany,oneside,bookmarks=true,bookmarksnumbered=true', 192 | 'preamble': ''' 193 | \\definecolor{VerbatimColor}{rgb}{0.95,0.95,0.95} 194 | \\setcounter{tocdepth}{4} 195 | ''', 196 | # The font size ('10pt', '11pt' or '12pt'). 197 | #'pointsize': '10pt', 198 | 199 | # Additional stuff for the LaTeX preamble. 200 | #'preamble': '', 201 | } 202 | 203 | # Grouping the document tree into LaTeX files. List of tuples 204 | # (source start file, target name, title, 205 | # author, documentclass [howto, manual, or own class]). 206 | latex_documents = [ 207 | ('readme', 'SphinxBlankProject.tex', u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書', 208 | u'making', 'manual'), 209 | ] 210 | 211 | # The name of an image file (relative to this directory) to place at the top of 212 | # the title page. 213 | #latex_logo = None 214 | 215 | # For "manual" documents, if this is true, then toplevel headings are parts, 216 | # not chapters. 217 | #latex_use_parts = False 218 | 219 | # If true, show page references after internal links. 220 | #latex_show_pagerefs = False 221 | 222 | # If true, show URL addresses after external links. 223 | #latex_show_urls = False 224 | 225 | # Documents to append as an appendix to all manuals. 226 | #latex_appendices = [] 227 | 228 | # If false, no module index is generated. 229 | #latex_domain_indices = True 230 | 231 | 232 | # -- Options for manual page output --------------------------------------- 233 | 234 | # One entry per manual page. List of tuples 235 | # (source start file, name, description, authors, manual section). 236 | man_pages = [ 237 | ('readme', 'SphinxBlankProject', u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書', 238 | [u'making'], 1) 239 | ] 240 | 241 | # If true, show URL addresses after external links. 242 | #man_show_urls = False 243 | 244 | 245 | # -- Options for Texinfo output ------------------------------------------- 246 | 247 | # Grouping the document tree into Texinfo files. List of tuples 248 | # (source start file, target name, title, author, 249 | # dir menu entry, description, category) 250 | texinfo_documents = [ 251 | ('readme', 'SphinxBlankProject', u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書', 252 | u'making', 'SphinxBlankProject', 'One line description of project.', 253 | 'Miscellaneous'), 254 | ] 255 | 256 | # Documents to append as an appendix to all manuals. 257 | #texinfo_appendices = [] 258 | 259 | # If false, no module index is generated. 260 | #texinfo_domain_indices = True 261 | 262 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 263 | #texinfo_show_urls = 'footnote' 264 | 265 | # If true, do not generate a @detailmenu in the "Top" node's menu. 266 | #texinfo_no_detailmenu = False 267 | -------------------------------------------------------------------------------- /instruction/readme.rst: -------------------------------------------------------------------------------- 1 | JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書 #jjug_ccc #ccc_r53 2 | 3 | .. contents:: 目次 4 | :depth: 2 5 | 6 | 7 | 事前準備 8 | ================================================================================ 9 | ハンズオン資材は\ http://bit.ly/1uYCKF0\ からダウンロードしてください。 10 | 11 | 12 | 必要なソフトウェアのインストール 13 | -------------------------------------------------------------------------------- 14 | 15 | Mac/Windowsユーザー向けに記述しています。Linuxで実施する場合は同等の手順を実施してください。 16 | 17 | Java SE 8 18 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 19 | http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html\ からJava SE Development Kit 8u25 (8以上であればおそらくOK)をダウンロードして、 20 | インストールしてください。 21 | 22 | 環境変数\ ``JAVA_HOME``\ の設定と\ ``PATH``\ の追加を必ず行ってください。 23 | 24 | Maven 25 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 26 | http://ftp.meisei-u.ac.jp/mirror/apache/dist/maven/maven-3/3.2.3/binaries/apache-maven-3.2.3-bin.tar.gz\ からMavenをダウンロードして、 27 | 展開したディレクトリのbinフォルダを環境変数\ ``PATH``\ に追加してください。 28 | 29 | 尚、(ハンズオン資材のルートフォルダ)/software/apache-maven-3.2.3-bin.tar.gzにダウンロード済みです。 30 | 31 | Git Bash (Windowsの場合) 32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 33 | Windowsの場合、 34 | 35 | http://git-scm.com/download/win\ からGitをダウンロードしてインストールしてください。 36 | 37 | 以下で実行するコマンドは全て\ **Gitに付属しているGit Bashを用いて実行してください**\ 。 38 | 39 | jq (オプション) 40 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 41 | 必須ではないですが、JSON出力の整形用に\ http://stedolan.github.io/jq/\ をインストールしておくと良いです。 42 | 43 | Redis 44 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 45 | 46 | Macの場合は、以下を実行してください。(要:XCode) 47 | 48 | .. code-block:: bash 49 | 50 | $ cd (ハンズオン資材のルートフォルダ)/software/redis-2.8.17 51 | $ tar xzvf redis-2.8.17.tar.gz 52 | $ cd redis-2.8.17 53 | $ make 54 | 55 | Windows 64ビットの場合は、(ハンズオン資材のルートフォルダ)/software/redis-2.8.17/redis-2.8.17.zipを展開してください。 56 | 57 | Windows 32ビットの場合は、(ハンズオン資材のルートフォルダ)/software/redis-2.8.17/edisbin.zipを展開してください。 58 | 59 | 60 | 61 | Gitbucket 62 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 63 | https://github.com/takezoe/gitbucket/releases/download/2.5/gitbucket.war\ より、Gitbucketをダウンロードしてください。 64 | 65 | 尚、(ハンズオン資材のルートフォルダ)/software/gitbucket.warにダウンロード済みです。 66 | 67 | IntelliJ IDEA 68 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 69 | 70 | https://www.jetbrains.com/idea/download/\ より、IntelliJ IDEA 14のCommunity EditionまたはUltimate Editionをダウンロードしてインストールしてください。 71 | 72 | Spring Tool SuiteやEclipseを使用してもハンズオンを実施できますが、ハンズオンの説明はIntelliJ IDEAを用いて行います。 73 | 74 | 75 | Mavenリポジトリのコピー 76 | -------------------------------------------------------------------------------- 77 | ハンズオンはオフライン環境で行います。 78 | 79 | 必要なライブラリをインターネットからダウンロードせず直接Mavenリポジトリにコピーします。 80 | 81 | (ハンズオン資材のルートフォルダ)/repository以下を(ホームディレクトリ)/.m2/repository以下にコピーしてください。 82 | 83 | .. code-block:: bash 84 | 85 | $ cp -rf (ハンズオン資材のルートフォルダ)/repository/* ~/.m2/repository/ 86 | 87 | \ ``overwrite /Users/maki/.m2/repository/antlr/antlr/2.7.2/_maven.repositories? (y/n [n])``\ というように上書きするかどうか聞かれる場合は 88 | 89 | 90 | .. code-block:: bash 91 | 92 | $ \cp -rf (ハンズオン資材のルートフォルダ)/repository/* ~/.m2/repository/ 93 | 94 | を実行してください。 95 | 96 | 演習の全体像 97 | ================================================================================ 98 | 99 | 本演習で「URL短縮サービス」を題材にマイクロサービスアーキテクチャを構築します。 100 | 101 | 最終的に構築するアーキテクチャを以下に示します。 102 | 103 | .. figure:: ./images/exercise00-01.png 104 | :width: 80% 105 | 106 | 演習1ではSpring Bootを用いて単一の「URL短縮サービス」を作成します。 107 | 108 | .. figure:: ./images/exercise00-02.png 109 | :width: 80% 110 | 111 | 演習2ではSpring Cloud Configを用いて「URL短縮サービス」に動的コンフィギュレーションを追加します。 112 | 113 | .. figure:: ./images/exercise00-03.png 114 | :width: 80% 115 | 116 | 演習3では「URL短縮サービス」のUIを追加し、Spring Cloud Netflixを用いて「URL短縮サービス」にマイクロサービスアーキテクチャのための様々なパターンを追加します。 117 | 118 | .. figure:: ./images/exercise00-04.png 119 | :width: 80% 120 | 121 | 122 | 本来は複数のマシンを用いて構築しますが、本演習では1つのマシン上で全てのサービスを起動します。 123 | 124 | 演習1 Spring Bootで「URL短縮サービス」を作る 125 | ================================================================================ 126 | 127 | 演習1ではマイクロサービス界でのFizzBuzz問題である、「URL短縮サービス」を作ります。 128 | 129 | 課題1で\ ``ConcurrentHashMap``\ を使った実装。課題2でRedisを使った実装を行います。 130 | 131 | インポートするプロジェクトにほとんどのコードが実装されているので、課題で実装するコードはほんの数行です。 132 | 133 | 演習プロジェクトの作成・インポート 134 | -------------------------------------------------------------------------------- 135 | 136 | 新規プロジェクト作成 137 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 138 | 139 | IntelliJ IDEAを開いて「New Project」で新規プロジェクトを作成します。 140 | 以下のように「Empty Project」を選択してください。 141 | 142 | .. figure:: ./images/import-exercise01-01.png 143 | :width: 80% 144 | 145 | 以下の設定値を入力してください。\ **デフォルト値から変更するので注意してください** \ 。 146 | 147 | .. tabularcolumns:: |p{0.30\linewidth}|p{0.70\linewidth}| 148 | .. list-table:: 149 | :stub-columns: 1 150 | :widths: 30 70 151 | 152 | * - | Project name 153 | - | jjugccc-handson 154 | * - | Project location 155 | - | (ハンズオン資材のルートフォルダ)/exercise 156 | 157 | 158 | .. figure:: ./images/import-exercise01-02.png 159 | :width: 80% 160 | 161 | 「Project Structure」で以下の設定値を入力してください。 162 | 163 | .. tabularcolumns:: |p{0.30\linewidth}|p{0.70\linewidth}| 164 | .. list-table:: 165 | :stub-columns: 1 166 | :widths: 30 70 167 | 168 | * - | Project SDK 169 | - | 1.8 170 | * - | Project language level 171 | - | 8 172 | 173 | 174 | .. figure:: ./images/import-exercise01-03.png 175 | :width: 80% 176 | 177 | 178 | JDKが未設定の場合は、「New」を押してJDKを設定してください。JAVA_HOMEに相当するフォルダを選択すれば良いです。 179 | 180 | 181 | .. figure:: ./images/import-exercise01-04.png 182 | :width: 40% 183 | 184 | 演習プロジェクトのインポート 185 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 186 | 「File」->「Import Module」で演習プロジェクトをインポートします。 187 | 188 | .. figure:: ./images/import-exercise01-05.png 189 | :width: 80% 190 | 191 | 「(ハンズオン資材のルートフォルダ)/exercise/01-urlshortener」を選択してください。 192 | 193 | .. figure:: ./images/import-exercise01-06.png 194 | :width: 80% 195 | 196 | 「Import module from external model」で「Maven」を指定してください。 197 | 198 | .. figure:: ./images/import-exercise01-07.png 199 | :width: 80% 200 | 201 | \ **「Search for projects recursively」と「Import Maven projects automatically」にチェックを入れて**\ 、次に進んでください。 202 | 203 | .. figure:: ./images/import-exercise01-08.png 204 | :width: 80% 205 | 206 | 「Next」を繰り返すと、以下のように演習1用のMavenプロジェクトがインポートされます。 207 | 208 | 209 | .. figure:: ./images/import-exercise01-09.png 210 | :width: 80% 211 | 212 | 213 | 課題1 TODOを埋めてプログラムを完成させてください 214 | -------------------------------------------------------------------------------- 215 | 216 | \ ``demo.UrlShortener``\ を編集してください。 217 | 218 | 以下\ ``TODO``\ 部分を埋めてください。 219 | 220 | .. code-block:: java 221 | 222 | @RequestMapping(value = "/", method = RequestMethod.POST) 223 | ResponseEntity save(@RequestParam String url) { 224 | if (urlValidator.isValid(url)) { 225 | String hash = ""/* TODO (1) URLをハッシュ化。ハッシュアルゴリズムには 32-bit murmur3 algorithm を使用する。 */; 226 | // ヒント: com.google.common.hash.Hashing.murmur3_32()を使う 227 | // TODO (2) urlMapにhashに紐づくURLを追加する。 228 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK); 229 | } else { 230 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST); 231 | } 232 | } 233 | 234 | \ ``UrlShortener``\ クラスを右クリックして、\ ``Run UrlShortener.main()``\ をクリックしてください。 235 | 236 | .. figure:: ./images/exercise01-01.png 237 | :width: 80% 238 | 239 | 240 | 以下のようにも実行できます。 241 | 242 | .. code-block:: bash 243 | 244 | $ cd (ハンズオン資材のルートフォルダ)/exercise/01-urlshortener 245 | $ mvn spring-boot:run -f urlshortener/pom.xml 246 | 247 | 以下の結果が返るか確認してください。 248 | 249 | .. code-block:: bash 250 | 251 | $ curl -X POST http://localhost:8080 -d "url=http://google.com" 252 | http://localhost:8080/58f3ae21 253 | $ curl -X GET http://localhost:8080/58f3ae21 254 | http://google.com 255 | 256 | 257 | \ ``UrlShortenerTest``\ クラスを右クリックして、\ ``Run UrlShortenerTest``\ をクリックしてください。 258 | 259 | .. figure:: ./images/exercise01-02.png 260 | :width: 80% 261 | 262 | テストが成功したら課題1は完了です。 263 | 264 | .. figure:: ./images/exercise01-03.png 265 | :width: 80% 266 | 267 | テストは以下のようにも実行できます。 268 | 269 | .. code-block:: bash 270 | 271 | $ mvn test -f urlshortener/pom.xml 272 | 273 | 課題2 Redisを使ってConcurrentHashMap使用部分を書き換えましょう 274 | -------------------------------------------------------------------------------- 275 | 次に\ ``ConcurrentHashMap``\ の部分をRedisを使用するように書き換えます。 276 | Spring BootによるAutoconfigurationでいかに簡単にRedis (Spring Data Redis)を使用できるか体験します。 277 | 278 | 279 | Redisの起動 280 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 281 | 282 | Macの場合 283 | 284 | .. code-block:: bash 285 | 286 | $ cd (ハンズオン資材のルートフォルダ)/software/redis-2.8.17 287 | $ ./src/redis-server 288 | [34286] 09 Nov 05:27:07.455 # Warning: no config file specified, using the default config. In order to specify a config file use ./src/redis-server /path/to/redis.conf 289 | [34286] 09 Nov 05:27:07.457 * Increased maximum number of open files to 10032 (it was originally set to 2560). 290 | _._ 291 | _.-``__ ''-._ 292 | _.-`` `. `_. ''-._ Redis 2.8.17 (00000000/0) 64 bit 293 | .-`` .-```. ```\/ _.,_ ''-._ 294 | ( ' , .-` | `, ) Running in stand alone mode 295 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 296 | | `-._ `._ / _.-' | PID: 34286 297 | `-._ `-._ `-./ _.-' _.-' 298 | |`-._`-._ `-.__.-' _.-'_.-'| 299 | | `-._`-._ _.-'_.-' | http://redis.io 300 | `-._ `-._`-.__.-'_.-' _.-' 301 | |`-._`-._ `-.__.-' _.-'_.-'| 302 | | `-._`-._ _.-'_.-' | 303 | `-._ `-._`-.__.-'_.-' _.-' 304 | `-._ `-.__.-' _.-' 305 | `-._ _.-' 306 | `-.__.-' 307 | 308 | [34286] 09 Nov 05:27:07.465 # Server started, Redis version 2.8.17 309 | [34286] 09 Nov 05:27:07.466 * DB loaded from disk: 0.001 seconds 310 | [34286] 09 Nov 05:27:07.466 * The server is now ready to accept connections on port 6379 311 | 312 | Windowsの場合、redis-server.exeを実行してください。 313 | 314 | 315 | ソースコードの修正 316 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 317 | 318 | 以下の3カ所を書き換えてください。 319 | 320 | .. code-block:: java 321 | 322 | final ConcurrentHashMap urlMap = new ConcurrentHashMap<>(); 323 | // ↓ 324 | @Autowired StringRedisTemplate redisTemplate; 325 | 326 | に書き換えてください。 327 | 328 | .. code-block:: java 329 | 330 | urlMap.putIfAbsent(hash, url); 331 | // ↓ 332 | redisTemplate.opsForValue().set(hash, url); 333 | 334 | に書き換えてください。 335 | 336 | 337 | .. code-block:: java 338 | 339 | String url = urlMap.get(hash); 340 | // ↓ 341 | String url = redisTemplate.opsForValue().get(hash); 342 | 343 | に書き換えてください。 344 | 345 | 346 | 書き換えた後に、課題1同様にテストが通れば課題2も完了です。 347 | 348 | 起動したアプリケーションは終了しておいてください。Redisは起動したままにしてください。 349 | 350 | 演習2 Spring Cloud Configで動的コンフィギュレーション 351 | ================================================================================ 352 | 演習2ではSpring Cloud Configを使った動的コンフィギュレーションを体験します。 353 | 354 | 355 | 演習2で扱うシステムのアーキテクチャ図を以下に示します。 356 | 357 | .. figure:: ./images/exercise02-01.png 358 | :width: 40% 359 | 360 | Config Clientとして演習1で作成した「URL短縮サービス」を使用し、Config Server(作成済み)から設定を取得します。 361 | 362 | Config ServerはデフォルトでGithubに接続しますが、今回はオフライン環境で実施するため、ローカルに立ち上げたGitbucketを使用します。 363 | 364 | 演習プロジェクトのインポート 365 | -------------------------------------------------------------------------------- 366 | 「File」->「Import Module」で演習プロジェクトをインポートします。 367 | 「(ハンズオン資材のルートフォルダ)/exercise/02-distributed-config」を選択してください。 368 | 369 | .. figure:: ./images/import-exercise02-01.png 370 | :width: 80% 371 | 372 | .. figure:: ./images/import-exercise02-02.png 373 | :width: 80% 374 | 375 | * configserverはConfig Serverを設定したプロジェクトです。 376 | * urlshortenerは演習1にConfig Clientの依存関係を追加したプロジェクトです。 377 | 378 | どちらも既に設定済みで、新規にコーディングする必要はありません。 379 | 380 | Gitbucketの起動 & Config Repositoryの作成 381 | -------------------------------------------------------------------------------- 382 | 383 | Gibucketを起動しましょう。8080番ポートを使用するので、このポートを使用しているアプリがあれば終了してください。 384 | 385 | .. code-block:: bash 386 | 387 | $ cd (ハンズオン資材のルートフォルダ)/software 388 | $ java -jar gitbucket.war 389 | 390 | http://localhost:8080\ にアクセスしユーザー名/パスワードともに「root」でログインしてください。 391 | 392 | .. figure:: ./images/exercise02-02.png 393 | :width: 80% 394 | 395 | 右上のメニューから「New repository」をクリックしてください。 396 | 397 | .. figure:: ./images/exercise02-03.png 398 | :width: 80% 399 | 400 | Repository nameに「config-repo」を入力し、「Initialize this repository with a README」にチェックを入れ、「Create repository」をクリックしてください。 401 | 402 | .. figure:: ./images/exercise02-04.png 403 | :width: 80% 404 | 405 | これでConfig Respositoryが作成できました。 406 | 407 | .. figure:: ./images/exercise02-05.png 408 | :width: 80% 409 | 410 | 動作確認用のコンフィギュレーションを作成しましょう。レポジトリ名の右に「+」マークをクリックしてください。 411 | 412 | .. figure:: ./images/exercise02-06.png 413 | :width: 80% 414 | 415 | 416 | ファイル名を「foo.properties」にし、以下の内容を記入し、「Commit changes」をクリックしてください。 417 | 418 | .. code-block:: properties 419 | 420 | foo: 123456 421 | bar: abcdef 422 | 423 | .. figure:: ./images/exercise02-07.png 424 | :width: 80% 425 | 426 | もう一つファイルを作成します。 427 | ファイル名を「foo-development.properties」にし、以下の内容を記入し、「Commit changes」をクリックしてください。 428 | 429 | .. code-block:: properties 430 | 431 | foo: Hello! 432 | 433 | .. figure:: ./images/exercise02-08.png 434 | :width: 80% 435 | 436 | Config Server起動 437 | -------------------------------------------------------------------------------- 438 | 439 | 「configserver」の\ ``bootstrap.yml``\ に以下の設定が行われていることを確認してください。 440 | 441 | .. code-block:: yaml 442 | 443 | spring.cloud.config.server.uri: http://localhost:8080/git/root/config-repo.git 444 | 445 | 以下のコマンドでConfig Serverを起動してください。 446 | 447 | .. code-block:: bash 448 | 449 | $ cd (ハンズオン資材のルートフォルダ)/exercise/02-distributed-config 450 | $ mvn spring-boot:run -f configserver/pom.xml 451 | 452 | 動作確認しましょう。 453 | 454 | .. code-block:: bash 455 | 456 | $ curl http://localhost:8888/admin/env 457 | 458 | 以下ではjqを使って整形した結果を示します。 459 | 460 | 461 | .. code-block:: bash 462 | 463 | $ curl http://localhost:8888/admin/env | jq . 464 | { 465 | "defaultProperties": { 466 | "spring.config.name": "configserver" 467 | }, 468 | "applicationConfig: [classpath:/bootstrap.yml]": { 469 | "spring.cloud.config.server.uri": "http://localhost:8080/git/root/config-repo.git" 470 | }, 471 | "applicationConfig: [classpath:/configserver.yml]": { 472 | "management.context_path": "/admin", 473 | "spring.application.name": "configserver", 474 | "server.port": 8888, 475 | "info.component": "Config Server", 476 | "spring.jmx.default_domain": "cloud.config.server" 477 | }, 478 | // ... 省略 479 | } 480 | 481 | \ ``spring.cloud.config.server.uri``\ が反映されていることを確認してください。 482 | 483 | 次にコンフィギュレーションを取得します。app名はfoo、profile名はdefaultにします。 484 | 485 | .. code-block:: bash 486 | 487 | $ curl http://localhost:8888/foo/default 488 | 489 | 以下ではjqを使って整形した結果を示します。 490 | 491 | .. code-block:: bash 492 | 493 | $ curl http://localhost:8888/foo/default | jq . 494 | { 495 | "propertySources": [ 496 | { 497 | "source": { 498 | "foo": "123456", 499 | "bar": "abcdef" 500 | }, 501 | "name": "http://localhost:8080/git/root/config-repo.git/foo.properties" 502 | } 503 | ], 504 | "label": "master", 505 | "name": "default" 506 | } 507 | 508 | 次にprofileを変更して取得しましょう。 509 | 510 | .. code-block:: bash 511 | 512 | $ curl http://localhost:8888/foo/development 513 | 514 | 515 | 以下ではjqを使って整形した結果を示します。 516 | 517 | .. code-block:: bash 518 | 519 | $ curl http://localhost:8888/foo/development | jq . 520 | { 521 | "propertySources": [ 522 | { 523 | "source": { 524 | "foo": "Hello!" 525 | }, 526 | "name": "http://localhost:8080/git/root/config-repo.git/foo-development.properties" 527 | }, 528 | { 529 | "source": { 530 | "foo": "123456", 531 | "bar": "abcdef" 532 | }, 533 | "name": "http://localhost:8080/git/root/config-repo.git/foo.properties" 534 | } 535 | ], 536 | "label": "master", 537 | "name": "development" 538 | } 539 | 540 | \ ``foo-development.properties``\ で上書きしていることが分かります。 541 | 542 | 543 | 「URL短縮サービス」向けのコンフィギュレーション作成 544 | -------------------------------------------------------------------------------- 545 | 546 | 同様に、URL短縮サービス向けのコンフィギュレーションを「urlshortener.yml」に作成します。設定内容は以下の通りです。 547 | 548 | .. code-block:: yaml 549 | 550 | urlshorten: 551 | url: http://localhost:${server.port} 552 | spring: 553 | redis: 554 | host: localhost # server host 555 | password: # server password 556 | port: 6379 # connection port 557 | pool: 558 | max-idle: 8 # pool settings ... 559 | min-idle: 0 560 | max-active: 8 561 | max-wait: -1 562 | endpoints.restart: 563 | enabled: true 564 | 565 | .. figure:: ./images/exercise02-09.png 566 | :width: 80% 567 | 568 | 569 | 動作確認しましょう。(Config Serverの再起動は不要です) 570 | 571 | .. code-block:: bash 572 | 573 | $ curl http://localhost:8888/urlshortener/default 574 | 575 | 576 | 以下ではjqを使って整形した結果を示します。 577 | 578 | .. code-block:: bash 579 | 580 | $ curl http://localhost:8888/urlshortener/default | jq . 581 | { 582 | "propertySources": [ 583 | { 584 | "source": { 585 | "spring.redis.pool.max-idle": 8, 586 | "spring.redis.password": "", 587 | "spring.redis.host": "localhost", 588 | "spring.redis.port": 6379, 589 | "urlshorten.url": "http://localhost:${server.port}", 590 | "endpoints.restart.enabled": true, 591 | "spring.redis.pool.max-active": 8, 592 | "spring.redis.pool.min-idle": 0, 593 | "spring.redis.pool.max-wait": -1 594 | }, 595 | "name": "http://localhost:8080/git/root/config-repo.git/urlshortener.yml" 596 | } 597 | ], 598 | "label": "master", 599 | "name": "default" 600 | } 601 | 602 | Git上の変更が即反映されています。 603 | 604 | 605 | 「URL短縮サービス」(Config Client)の起動 606 | -------------------------------------------------------------------------------- 607 | 608 | 次にConfig Clientとして、「URL短縮サービス」を起動します。 609 | 610 | インポートしたプロジェクト(exercise/02-distributed-config/urlshortener)のpom.xmlに以下の依存関係が追加されていることを確認してください。 611 | 612 | .. code-block:: xml 613 | 614 | 615 | org.springframework.cloud 616 | spring-cloud-starter 617 | 618 | 619 | org.springframework.boot 620 | spring-boot-starter-actuator 621 | 622 | 623 | また、urlshortenerのbootstrap.ymlに 624 | 625 | .. code-block:: yaml 626 | 627 | spring: 628 | application: 629 | name: urlshortener 630 | 631 | が設定されていることを確認してください。 632 | 633 | 「URL短縮サービス」を起動しましょう。8080番ポートは既に起動しているので、プログラム引数に\ ``--server.port=8081``\ をつけて8081番ポートで起動します。 634 | 635 | .. code-block:: bash 636 | 637 | $ cd (ハンズオン資材のルートフォルダ)/exercise/02-distributed-config 638 | $ mvn spring-boot:run -f urlshortener/pom.xml -Drun.arguments="--server.port=8081" 639 | 640 | 641 | 演習1同様に以下のリクエストを送ってください。(ポート名が変更されていることに気をつけてください) 642 | 643 | .. code-block:: bash 644 | 645 | $ curl -X POST http://localhost:8081 -d "url=http://google.com" 646 | http://localhost:8081/58f3ae21 647 | $ curl -X GET http://localhost:8081/58f3ae21 648 | http://google.com 649 | 650 | 次にConfig Server(urlshortener.yml)の値を変えましょう。 651 | 652 | http://localhost:8080/root/config-repo/blob/master/urlshortener.yml\ にアクセスし、「Edit」ボタンをクリックしてください。 653 | 654 | .. figure:: ./images/exercise02-10.png 655 | :width: 80% 656 | 657 | \ ``urlshorten.url``\ を\ ``http://localhost:9999``\ に変更して「Commit changes」をクリックしてください。(\ **この設定は演習3で使用します**\ )。 658 | 659 | .. figure:: ./images/exercise02-11.png 660 | :width: 80% 661 | 662 | 変更を反映する前に、Config Client上のプロパティを確認しましょう。 663 | 664 | .. code-block:: bash 665 | 666 | $ curl -X GET http://localhost:8081/env/urlshorten.url 667 | http://localhost:8081 668 | 669 | 670 | 次にConfig Clientをrefreshします。 671 | 672 | .. code-block:: bash 673 | 674 | $ curl -X POST http://localhost:8081/refresh 675 | ["urlshorten.url"] 676 | $ curl -X GET http://localhost:8081/env/urlshorten.url 677 | http://localhost:9999 678 | 679 | 変更が反映されました。しかし、以下の通りDI済みのプロパティに再DIはされていません。 680 | 681 | .. code-block:: bash 682 | 683 | $ curl -X POST http://localhost:8081 -d "url=http://google.com" 684 | http://localhost:8081/58f3ae21 685 | 686 | 今度はConfig Clientをrestartします。 687 | 688 | .. code-block:: bash 689 | 690 | $ curl -X POST http://localhost:8081/restart 691 | {"message":"Restarting"} 692 | 693 | restart後は、最新のプロパティで再DIされていることがわかります。 694 | 695 | .. code-block:: bash 696 | 697 | $ curl -X POST http://localhost:8081 -d "url=http://google.com" 698 | http://localhost:9999/58f3ae21 699 | 700 | 701 | 課題3 「URL短縮サービス」(Config Client)をRefreshスコープに変更 702 | -------------------------------------------------------------------------------- 703 | 「URL短縮サービス(\ ``UrlShortener``\ クラス)」へのプロパティインジェクション反映をrefreshで行えるように、 704 | \ ``UrlShortener``\ クラスをRefreshスコープに変更してください。 705 | 706 | .. code-block:: java 707 | 708 | @EnableAutoConfiguration 709 | @ComponentScan 710 | @RestController 711 | @RefreshScope // ここを追加 712 | public class UrlShortener { 713 | // 略 714 | } 715 | 716 | \ ``mvn spring-boot:run``\ で起動した「URL短縮サービス」をCtrl+Cで終了して、再度実行してください。 717 | 718 | 719 | .. code-block:: bash 720 | 721 | $ mvn spring-boot:run -f urlshortener/pom.xml -Drun.arguments="--server.port=8081" 722 | 723 | 今回は以下のようにEnvエンドポイントにPOSTすることでコンフィギュレーションを変更しましょう。 724 | 725 | 726 | .. code-block:: bash 727 | 728 | $ curl -X POST http://localhost:8081/env -d urlshorten.url=http://127.0.0.1:9999 729 | {"urlshorten.url":"http://127.0.0.1:9999"} 730 | 731 | 再度、refreshを行い、もう一度「URL短縮サービス」へリクエストを送りましょう。 732 | 733 | .. code-block:: bash 734 | 735 | $ curl -X POST http://localhost:8081/refresh 736 | [] 737 | $ curl -X POST http://localhost:8081 -d "url=http://google.com" 738 | http://127.0.0.1:9999/58f3ae21 739 | 740 | restartすることなく、アプリケーションにプロパティが反映されたことがわかります。 741 | 742 | Config Server、Config ClientともにCtrl+Cで終了してください。(Gitbucket, Redisは起動したままにしてください。) 743 | 744 | 演習3 Spring Cloud Netflixでマイクロサービスアーキテクチャ構築 745 | ================================================================================ 746 | 演習3ではSpring Cloud Netflixを使った様々なパターンを体験します。 747 | 748 | 749 | 演習3で扱うシステムのアーキテクチャ図を以下に示します。 750 | 751 | .. figure:: ./images/exercise03-01.png 752 | :width: 80% 753 | 754 | 演習プロジェクトのインポート 755 | -------------------------------------------------------------------------------- 756 | 「File」->「Import Module」で演習プロジェクトをインポートします。 757 | 「(ハンズオン資材のルートフォルダ)/exercise/03-netflix」を選択してください。 758 | 759 | .. figure:: ./images/import-exercise03-01.png 760 | :width: 80% 761 | 762 | .. figure:: ./images/import-exercise03-02.png 763 | :width: 80% 764 | 765 | * configserverはConfig Serverを設定したプロジェクトです。演習2と同じです。 766 | * eureka-serverはService DiscoveryであるEurekaを起動するプロジェクトです。ダッシュボードも提供します。 767 | * hystrix-dashboardはHystrixのダッシュボードを提供するプロジェクトです。 768 | * urlshortenerは演習2にConfig Clientの依存関係を追加したプロジェクトです。 769 | * urlshortener-uiは「URL短縮サービス」の画面です。\ ``RestClient``\ とClient LoadbalancerのRibboを使ってurlshortenerにアクセスします。 770 | 771 | どれも既に設定済みで、新規にコーディングする必要はありません。上から順番に起動します。 772 | 773 | 演習2で起動したGitbucketが必要ですので、終了してしまった場合は再び実行してください。 774 | 775 | 776 | 777 | Config Serverの起動 778 | -------------------------------------------------------------------------------- 779 | 780 | .. figure:: ./images/system-exercise03-01.png 781 | :width: 80% 782 | 783 | 演習2同様に、以下のコマンドでConfig Serverを起動してください。 784 | 785 | .. code-block:: bash 786 | 787 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix 788 | $ mvn spring-boot:run -f configserver/pom.xml 789 | 790 | 791 | Service Discovery (Eureka Server)の起動 792 | -------------------------------------------------------------------------------- 793 | 794 | .. figure:: ./images/system-exercise03-02.png 795 | :width: 80% 796 | 797 | 以下のコマンドでEureka Serverを起動してください。 798 | 799 | .. code-block:: bash 800 | 801 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix 802 | $ mvn spring-boot:run -f eureka-server/pom.xml 803 | 804 | http://localhost:8761/\ でEureka Serverのダッシュボードにアクセスできます。 805 | 806 | 807 | .. figure:: ./images/exercise03-02.png 808 | :width: 80% 809 | 810 | 現時点ではEureka Serverに登録されているインスタンスはありません。 811 | 812 | Circuit Breaker Monitor (Hystrix Dashboard)の起動 813 | -------------------------------------------------------------------------------- 814 | 815 | .. figure:: ./images/system-exercise03-03.png 816 | :width: 80% 817 | 818 | 以下のコマンドでHystrix Dashboardを起動してください。 819 | 820 | .. code-block:: bash 821 | 822 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix 823 | $ mvn spring-boot:run -f hystrix-dashboard/pom.xml 824 | 825 | 起動後、30秒経ったら\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。 826 | 827 | .. figure:: ./images/exercise03-03.png 828 | :width: 80% 829 | 830 | Hystrix DashboardがEurekaに登録されたことが分かります(アーキテクチャ図に記されていませんが、Circuit Breaker MonitorからService Discoveryへの線相当です)。 831 | 832 | ではHystrix Dashboardにアクセスしましょう。http://localhost:7979\ にアクセスしてください。 833 | 834 | .. figure:: ./images/exercise03-04.png 835 | :width: 80% 836 | 837 | 中央の入力フォームにはHystrixを利用したサービスの情報を取得するためのevent streamのURLを指定することで、 838 | そのサービスをモニタリングすることができます。 839 | 840 | まだHystrixを利用したサービスがないため、ここではデモ用のMock Streamを使用します。http://localhost:7979/mock.stream\ を入力して、「Monitor Stream」をクリックしてください。 841 | 842 | 843 | .. figure:: ./images/exercise03-05.png 844 | :width: 80% 845 | 846 | Hystrixのイベントをモニタリングできます。 847 | 848 | .. figure:: ./images/exercise03-06.png 849 | :width: 80% 850 | 851 | 後ほど「URL短縮サービス」のevent streamをモニタリングします。 852 | 853 | 854 | 「URL短縮サービス」の起動 855 | -------------------------------------------------------------------------------- 856 | 857 | 858 | .. figure:: ./images/system-exercise03-04.png 859 | :width: 80% 860 | 861 | 次に演習1から使い続けている「URL短縮サービス」を起動します。 862 | 863 | 後ほどこの「URL短縮サービス」を3台起動します。Eurekaに別hostnameとして認識させるため、あらかじめ/etc/hostsに以下の設定を追加しておきます。 864 | 865 | .. code-block:: bash 866 | 867 | 127.0.0.1 urlshortener1 urlshortener2 urlshortener3 868 | 869 | 尚、演習2のurlshortenに対して、以下の変更を加えています。 870 | 871 | \ ``UrlShortener``\ クラスがEurekaのクライアントになるために\ ``@EnableEurekaClient``\ を追加しています。 872 | 873 | .. code-block:: java 874 | 875 | @EnableAutoConfiguration 876 | @ComponentScan 877 | @RestController 878 | @RefreshScope 879 | @EnableEurekaClient // 追加 880 | public class UrlShortener { 881 | // 略 882 | } 883 | 884 | application.ymlにEurekaに関する情報を追加しています。 885 | 886 | .. code-block:: yaml 887 | 888 | eureka: 889 | client: 890 | serviceUrl: 891 | defaultZone: http://localhost:8761/eureka/ 892 | instance: 893 | hostname: ${APPLICATION_DOMAIN:127.0.0.1} 894 | nonSecurePort: ${server.port} 895 | 896 | 897 | それでは「URL短縮サービス」を起動しましょう。portとEurekaに登録するhostnameを指定します。 898 | 899 | .. code-block:: bash 900 | 901 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix 902 | $ mvn spring-boot:run -f urlshortener/pom.xml \ 903 | -Drun.arguments="--server.port=8081,--eureka.instance.hostname=urlshortener1" 904 | 905 | 起動後、30秒経ったら\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。 906 | 907 | .. figure:: ./images/exercise03-07.png 908 | :width: 80% 909 | 910 | urlshortenerがEurekaに登録されたことが分かります。 911 | 912 | 913 | 「URL短縮サービス」UIの起動 914 | -------------------------------------------------------------------------------- 915 | 最後のサービスとして「URL短縮サービス」UIを起動します。 916 | 917 | .. figure:: ./images/system-exercise03-05.png 918 | :width: 80% 919 | 920 | 起動する前にUI用のコンフィギュレーションを作成します。 921 | 922 | \ `Config Repository `_\ にアクセスして、urlshortener-ui.ymlを作成し、以下の内容を記述してください。 923 | 924 | 925 | .. code-block:: yaml 926 | 927 | urlshorten.api.url: http://urlshortener 928 | endpoints.restart: 929 | enabled: true 930 | 931 | 932 | .. figure:: ./images/exercise03-08.png 933 | :width: 80% 934 | 935 | 936 | UIを9999番ポートで起動します。 937 | 938 | .. code-block:: bash 939 | 940 | $ mvn spring-boot:run -f urlshortener-ui/pom.xml -Drun.arguments="--server.port=9999" 941 | 942 | 943 | 起動後、30秒経ったら\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。 944 | 945 | .. figure:: ./images/exercise03-09.png 946 | :width: 80% 947 | 948 | urlshortener-uiがEurekaに登録されたことが分かります。 949 | 950 | それでは\ http://localhost:9999\ にアクセスしましょう。 951 | 952 | .. figure:: ./images/exercise03-10.png 953 | :width: 80% 954 | 955 | url入力フォームに「http://google.com」を入力して、送信ボタンをクリックしましょう。 956 | 957 | .. figure:: ./images/exercise03-11.png 958 | :width: 80% 959 | 960 | バックエンドの「URL短縮サービス」が呼ばれて短縮URLが表示されます。 961 | 962 | .. figure:: ./images/exercise03-12.png 963 | :width: 80% 964 | 965 | 表示されたURLをクリックすると\ http://google.com\ へリダイレクトされます。 966 | 967 | urlshorten-uiではHystrix + Ribbonを使用して、urlshortenのサービスをcallしています。 968 | 969 | Hystrixのevent streamは\ http://localhost:9999/hystrix.stream\ でアクセスできます。 970 | 971 | 972 | .. figure:: ./images/exercise03-13.png 973 | :width: 80% 974 | 975 | \ `Hystrix Dashboard `_\ に\ http://localhost:9999/hystrix.stream\ を入力してモニタリングしてみましょう。 976 | 977 | 978 | .. figure:: ./images/exercise03-14.png 979 | :width: 80% 980 | 981 | UIからサービスを呼び出すとモニタリング画面に反映されます。 982 | 983 | .. figure:: ./images/exercise03-15.png 984 | :width: 80% 985 | 986 | 「URL短縮サービス」のスケールアウト 987 | -------------------------------------------------------------------------------- 988 | 989 | .. figure:: ./images/system-exercise03-06.png 990 | :width: 80% 991 | 992 | 最後に「URL短縮サービス」をあと2インスタンス起動し、Ribbonによるロードバランシングを体験しましょう。 993 | 994 | 早速、「URL短縮サービス」を起動しましょう。 995 | 996 | インスタンス2はport: 8082,hostname: urlshortener2で起動します。 997 | 998 | .. code-block:: bash 999 | 1000 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix 1001 | $ mvn spring-boot:run -f urlshortener/pom.xml \ 1002 | -Drun.arguments="--server.port=8082,--eureka.instance.hostname=urlshortener2" 1003 | 1004 | インスタンス3はport: 8083,hostname: urlshortener3で起動します。 1005 | 1006 | .. code-block:: bash 1007 | 1008 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix 1009 | $ mvn spring-boot:run -f urlshortener/pom.xml \ 1010 | -Drun.arguments="--server.port=8083,--eureka.instance.hostname=urlshortener3" 1011 | 1012 | 1013 | ノード2、ノード3起動後30秒経ったら、\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。 1014 | 1015 | .. figure:: ./images/exercise03-16.png 1016 | :width: 80% 1017 | 1018 | urlshortenerサービスに対して3つのインスタンスが登録されました。 1019 | 1020 | 1021 | UIではRibbonを利用することで、特定のインスタンスにアクセスしているわけではなく、\ ``http://urlshortener``\ というようにサービス名に対してアクセスしており、 1022 | ラウンドロビンのロードバランシングが行われます。 1023 | 1024 | いまの作りだと、どのインスタンスでURL短縮が行われているか分からないので、以下のような設定変更を行いましょう。 1025 | 1026 | .. code-block:: bash 1027 | 1028 | curl -X POST http://localhost:8081/env -d "urlshorten.url=http://localhost:\${server.port}" 1029 | curl -X POST http://localhost:8081/refresh 1030 | curl -X POST http://localhost:8082/env -d "urlshorten.url=http://localhost:\${server.port}" 1031 | curl -X POST http://localhost:8082/refresh 1032 | curl -X POST http://localhost:8083/env -d "urlshorten.url=http://localhost:\${server.port}" 1033 | curl -X POST http://localhost:8083/refresh 1034 | 1035 | これでUIから何度もリクエストを送ると、各インスタンスが順番に使用されていることが分かります。 1036 | 1037 | .. figure:: ./images/exercise03-17.png 1038 | :width: 80% 1039 | 1040 | どれかのインスタンスを落としたり、復旧させたりして何が起こるか試してみてください。 1041 | 1042 | .. note:: 1043 | 1044 | 単位時間辺りのエラー発生回数がしきい値を超えるとCiruitがOpen状態になり、一定時間ずっとエラーを返すようになります。 1045 | 1046 | 1047 | まとめ 1048 | ================================================================================ 1049 | 1050 | 本演習を通じて以下の内容を学びました。 1051 | 1052 | * 演習1ではSpring Bootを使って簡単にマイクロサービスを作成する方法を学びました。また数行でRedisに対応する方法も学びました。 1053 | * 演習2ではSpring Cloud Configを使って動的コンフィギュレーションの行い方を学びました。 1054 | * 演習3ではSpring Cloud Netflixを使ってマイクロサービスアーキテクチャにおけるいくつかのパターンを実現しました。 1055 | 1056 | さらなる学習には\ `Spring CloudのReference `_\ を参照してください。 1057 | --------------------------------------------------------------------------------