├── .gitignore ├── LICENSE ├── README.md ├── api-gateway-microservice ├── pom.xml └── src │ └── main │ ├── docker │ └── Dockerfile │ ├── java │ └── services │ │ └── Application.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── config-microservice ├── manifest.yml ├── pom.xml └── src │ └── main │ ├── docker │ └── Dockerfile │ ├── java │ └── services │ │ └── Application.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── consul-microservice ├── pom.xml └── src │ └── main │ ├── docker │ └── Dockerfile │ ├── java │ └── consul │ │ ├── Application.java │ │ └── ConsulProperties.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── discovery-microservice ├── pom.xml └── src │ └── main │ ├── docker │ └── Dockerfile │ ├── java │ └── services │ │ └── Application.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── docker └── docker-compose.yml ├── hystrix-dashboard ├── README.md ├── pom.xml └── src │ ├── main │ ├── docker │ │ └── Dockerfile │ ├── java │ │ └── hystrixdashboard │ │ │ ├── HystrixDashboardApplication.java │ │ │ └── stream │ │ │ └── MockStreamServlet.java │ └── resources │ │ ├── application.yml │ │ ├── bootstrap.yml │ │ ├── hystrixdashboard │ │ └── stream │ │ │ └── hystrix.stream │ │ └── log4j.properties │ └── test │ └── java │ └── hystrixdashboard │ └── test │ └── SampleHystrixApp.java ├── movie-microservice ├── pom.xml └── src │ └── main │ ├── docker │ └── Dockerfile │ ├── java │ └── data │ │ ├── Application.java │ │ ├── domain │ │ ├── nodes │ │ │ ├── Genre.java │ │ │ └── Movie.java │ │ └── rels │ │ │ └── HasGenre.java │ │ └── repositories │ │ ├── GenreRepository.java │ │ └── MovieRepository.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── movies-ui ├── pom.xml └── src │ ├── main │ ├── docker │ │ └── Dockerfile │ ├── java │ │ └── application │ │ │ ├── Application.java │ │ │ ├── clients │ │ │ └── UserClient.java │ │ │ ├── models │ │ │ └── User.java │ │ │ └── ui │ │ │ ├── AddressbookUI.java │ │ │ └── ContactForm.java │ └── resources │ │ ├── application.yml │ │ └── bootstrap.yml │ └── test │ └── java │ └── application │ └── DemoApplicationTests.java ├── pom.xml ├── recommendation-microservice ├── pom.xml └── src │ └── main │ ├── docker │ └── Dockerfile │ ├── java │ └── data │ │ ├── Application.java │ │ ├── domain │ │ ├── nodes │ │ │ ├── Event.java │ │ │ └── Job.java │ │ └── rels │ │ │ └── On.java │ │ └── repositories │ │ ├── EventRepository.java │ │ ├── JobRepository.java │ │ └── OnRepository.java │ └── resources │ ├── application.yml │ └── bootstrap.yml └── users-microservice ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── manifest.yml ├── pom.xml └── src └── main ├── docker └── Dockerfile ├── java └── data │ ├── Application.java │ ├── config │ ├── DateTimeToLongConverter.java │ └── LongToDateTimeConverter.java │ ├── domain │ ├── nodes │ │ ├── Content.java │ │ ├── Event.java │ │ └── User.java │ └── rels │ │ ├── Action.java │ │ ├── Caused.java │ │ ├── Follows.java │ │ └── On.java │ └── repositories │ ├── ActionRepository.java │ ├── ContentRepository.java │ ├── EventRepository.java │ ├── FollowsRepository.java │ ├── OnRepository.java │ └── UserRepository.java └── resources ├── application.yml └── bootstrap.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | .gradle/ 15 | .idea/ 16 | 17 | *.iml 18 | 19 | build/ 20 | target/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Cloud Example Project 2 | 3 | An example project that demonstrates an end-to-end cloud-native platform using Spring Cloud for building a practical microservices architecture. 4 | 5 | Demonstrated concepts: 6 | 7 | * Integration testing using Docker 8 | * Polyglot persistence 9 | * Microservice architecture 10 | * Service discovery 11 | * API gateway 12 | 13 | ## Docker 14 | 15 | Each service is built and deployed using Docker. End-to-end integration testing can be done on a developer's machine using Docker compose. 16 | 17 | ## Polyglot Persistence 18 | 19 | One of the core concepts of this example project is how polyglot persistence can be approached in practice. Microservices in the project use their own database, while integrating with the data from other services through REST or a message bus. 20 | 21 | * Neo4j (graph) 22 | * MongoDB (document) 23 | * MySQL (relational) 24 | 25 | ## Microservice architecture 26 | 27 | This example project demonstrates how to build a new application using microservices, as opposed to a monolith-first strategy. Since each microservice in the project is a module of a single parent project, developers have the advantage of being able to run and develop with each microservice running on their local machine. Adding a new microservice is easy, as the discovery microservice will automatically discover new services running on the cluster. 28 | 29 | ## Service discovery 30 | 31 | This project contains two discovery services, one on Netflix Eureka, and the other uses Consul from Hashicorp. Having multiple discovery services provides the opportunity to use one (Consul) as a DNS provider for the cluster, and the other (Eureka) as a proxy-based API gateway. 32 | 33 | ## API gateway 34 | 35 | Each microservice will coordinate with Eureka to retrieve API routes for the entire cluster. Using this strategy each microservice in a cluster can be load balanced and exposed through one API gateway. Each service will automatically discover and route API requests to the service that owns the route. This proxying technique is equally helpful when developing user interfaces, as the full API of the platform is available through its own host as a proxy. 36 | 37 | # License 38 | 39 | This project is licensed under Apache License 2.0. 40 | -------------------------------------------------------------------------------- /api-gateway-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | api-gateway-microservice 8 | 0.1.0 9 | jar 10 | 11 | 12 | org.kbastani 13 | spring-cloud-microservice-example-parent 14 | 0.1.0-SNAPSHOT 15 | 16 | 17 | 18 | kbastani 19 | 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-zuul 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-netflix-sidecar 29 | 30 | 31 | org.springframework.cloud 32 | spring-cloud-starter 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-maven-plugin 41 | 42 | 43 | 44 | repackage 45 | 46 | 47 | 48 | 49 | 50 | com.spotify 51 | docker-maven-plugin 52 | 0.2.3 53 | 54 | 55 | package 56 | 57 | build 58 | 59 | 60 | 61 | 62 | ${docker.image.prefix}/${project.artifactId} 63 | ${project.basedir}/src/main/docker 64 | 65 | 66 | / 67 | ${project.build.directory} 68 | ${project.build.finalName}.jar 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /api-gateway-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD api-gateway-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 6 | -------------------------------------------------------------------------------- /api-gateway-microservice/src/main/java/services/Application.java: -------------------------------------------------------------------------------- 1 | package services; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.sidecar.EnableSidecar; 6 | 7 | @SpringBootApplication 8 | @EnableSidecar 9 | public class Application { 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /api-gateway-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10000 3 | 4 | endpoints: 5 | restart: 6 | enabled: true 7 | shutdown: 8 | enabled: true 9 | health: 10 | sensitive: false 11 | 12 | eureka: 13 | instance: 14 | hostname: gateway 15 | client: 16 | registerWithEureka: true 17 | fetchRegistry: true 18 | serviceUrl: 19 | defaultZone: http://discovery:8761/eureka/ -------------------------------------------------------------------------------- /api-gateway-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: gateway 4 | cloud: 5 | config: 6 | uri: http://configserver:8888 7 | encrypt: 8 | failOnError: false -------------------------------------------------------------------------------- /config-microservice/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: config-microservice 4 | host: config-microservice-${random-word} 5 | memory: 512M 6 | instances: 1 7 | path: target/config-microservice-0.1.0.jar 8 | -------------------------------------------------------------------------------- /config-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | config-microservice 8 | 0.1.0 9 | jar 10 | 11 | config-microservice 12 | 13 | 14 | org.kbastani 15 | spring-cloud-microservice-example-parent 16 | 0.1.0-SNAPSHOT 17 | 18 | 19 | 20 | kbastani 21 | services.Application 22 | 23 | 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-config-server 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-eureka 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-maven-plugin 44 | 45 | 46 | 47 | repackage 48 | 49 | 50 | 51 | 52 | 53 | com.spotify 54 | docker-maven-plugin 55 | 0.2.3 56 | 57 | 58 | package 59 | 60 | build 61 | 62 | 63 | 64 | 65 | ${docker.image.prefix}/${project.artifactId} 66 | ${project.basedir}/src/main/docker 67 | 68 | 69 | / 70 | ${project.build.directory} 71 | ${project.build.finalName}.jar 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /config-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD config-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | EXPOSE 8888 6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 7 | -------------------------------------------------------------------------------- /config-microservice/src/main/java/services/Application.java: -------------------------------------------------------------------------------- 1 | package services; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.config.server.EnableConfigServer; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @EnableAutoConfiguration 11 | @EnableDiscoveryClient 12 | @EnableConfigServer 13 | public class Application { 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /config-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8888 3 | 4 | eureka: 5 | instance: 6 | hostname: configserver 7 | client: 8 | registerWithEureka: true 9 | fetchRegistry: true 10 | serviceUrl: 11 | defaultZone: http://discovery:8761/eureka/ 12 | 13 | spring: 14 | cloud: 15 | config: 16 | server: 17 | git: 18 | uri: https://github.com/kbastani/spring-boot-microservice-config -------------------------------------------------------------------------------- /config-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: configserver -------------------------------------------------------------------------------- /consul-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | consul-microservice 7 | 0.1.0 8 | jar 9 | org.kbastani 10 | 11 | kbastani 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-web 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-actuator 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-consul-config 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-consul-discovery 29 | 30 | 31 | org.springframework.cloud 32 | spring-cloud-consul-bus 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-consul-ui 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.cloud 48 | spring-cloud-consul 49 | 1.0.0.M1 50 | pom 51 | import 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-maven-plugin 60 | 1.2.3.RELEASE 61 | 62 | consul.Application 63 | 64 | 65 | 66 | 67 | repackage 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-compiler-plugin 75 | 3.1 76 | 77 | 1.8 78 | 1.8 79 | 80 | 81 | 82 | com.spotify 83 | docker-maven-plugin 84 | 0.2.3 85 | 86 | 87 | package 88 | 89 | build 90 | 91 | 92 | 93 | 94 | ${docker.image.prefix}/${project.artifactId} 95 | ${project.basedir}/src/main/docker 96 | 97 | 98 | / 99 | ${project.build.directory} 100 | ${project.build.finalName}.jar 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | spring-snapshots 110 | Spring Snapshots 111 | http://repo.spring.io/libs-snapshot-local 112 | 113 | true 114 | 115 | 116 | 117 | spring-milestones 118 | Spring Milestones 119 | http://repo.spring.io/libs-milestone-local 120 | 121 | false 122 | 123 | 124 | 125 | spring-releases 126 | Spring Releases 127 | http://repo.spring.io/libs-release-local 128 | 129 | false 130 | 131 | 132 | 133 | 134 | 135 | spring-snapshots 136 | Spring Snapshots 137 | http://repo.spring.io/libs-snapshot-local 138 | 139 | true 140 | 141 | 142 | 143 | spring-milestones 144 | Spring Milestones 145 | http://repo.spring.io/libs-milestone-local 146 | 147 | false 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /consul-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD consul-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | EXPOSE 8000 6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 7 | -------------------------------------------------------------------------------- /consul-microservice/src/main/java/consul/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package consul; 18 | 19 | import lombok.extern.slf4j.Slf4j; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.beans.factory.annotation.Value; 24 | import org.springframework.boot.SpringApplication; 25 | import org.springframework.boot.autoconfigure.SpringBootApplication; 26 | import org.springframework.boot.bind.RelaxedPropertyResolver; 27 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 28 | import org.springframework.cloud.bus.jackson.SubtypeModule; 29 | import org.springframework.cloud.client.ServiceInstance; 30 | import org.springframework.cloud.client.discovery.DiscoveryClient; 31 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 32 | import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; 33 | import org.springframework.cloud.consul.bus.SimpleRemoteEvent; 34 | import org.springframework.cloud.ui.EnableConsulUi; 35 | import org.springframework.context.ApplicationListener; 36 | import org.springframework.context.annotation.Bean; 37 | import org.springframework.core.env.Environment; 38 | import org.springframework.web.bind.annotation.RequestMapping; 39 | import org.springframework.web.bind.annotation.RequestParam; 40 | import org.springframework.web.bind.annotation.RestController; 41 | 42 | import javax.annotation.PostConstruct; 43 | 44 | /** 45 | * @author Spencer Gibb, Kenny Bastani 46 | */ 47 | @SpringBootApplication 48 | @EnableDiscoveryClient 49 | @EnableConsulUi 50 | @RestController 51 | @EnableConfigurationProperties 52 | @Slf4j 53 | public class Application implements ApplicationListener { 54 | 55 | final Logger logger = LoggerFactory.getLogger(Application.class); 56 | 57 | @Autowired 58 | private LoadBalancerClient loadBalancer; 59 | 60 | @Autowired 61 | private DiscoveryClient discoveryClient; 62 | 63 | @Autowired 64 | private Environment env; 65 | 66 | @Autowired(required = false) 67 | private RelaxedPropertyResolver resolver; 68 | 69 | @Value("${spring.application.name:consul}") 70 | private String appName; 71 | 72 | @PostConstruct 73 | public void init() { 74 | if (resolver == null) { 75 | resolver = new RelaxedPropertyResolver(env); 76 | } 77 | } 78 | 79 | @RequestMapping("/me") 80 | public ServiceInstance me() { 81 | return discoveryClient.getLocalServiceInstance(); 82 | } 83 | 84 | @RequestMapping("/") 85 | public ServiceInstance lb() { 86 | return loadBalancer.choose(appName); 87 | } 88 | 89 | @RequestMapping("/myenv") 90 | public String env(@RequestParam("prop") String prop) { 91 | return resolver.getProperty(prop, "Not Found"); 92 | } 93 | 94 | @RequestMapping("/prop") 95 | public String prop() { 96 | return sampleProperties().getProp(); 97 | } 98 | 99 | @Bean 100 | public SubtypeModule sampleSubtypeModule() { 101 | return new SubtypeModule(SimpleRemoteEvent.class); 102 | } 103 | 104 | @Bean 105 | public ConsulProperties sampleProperties() { 106 | return new ConsulProperties(); 107 | } 108 | 109 | public static void main(String[] args) { 110 | SpringApplication.run(Application.class, args); 111 | } 112 | 113 | @Override 114 | public void onApplicationEvent(SimpleRemoteEvent event) { 115 | logger.info("Received event: {}", event); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /consul-microservice/src/main/java/consul/ConsulProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package consul; 18 | 19 | import lombok.Data; 20 | import org.springframework.boot.context.properties.ConfigurationProperties; 21 | 22 | @ConfigurationProperties("consul") 23 | @Data 24 | public class ConsulProperties { 25 | 26 | private String prop = "default value"; 27 | 28 | public String getProp() { 29 | return prop; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /consul-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8000 3 | 4 | ribbon: 5 | ServerListRefreshInterval: 1000 6 | 7 | endpoints: 8 | health: 9 | sensitive: false 10 | restart: 11 | enabled: true 12 | shutdown: 13 | enabled: true 14 | 15 | logging: 16 | level: 17 | org.springframework.cloud.consul: DEBUG 18 | 19 | eureka: 20 | client: 21 | registerWithEureka: true 22 | fetchRegistry: false 23 | serviceUrl: 24 | defaultZone: http://discovery:${server.port}/eureka/ 25 | 26 | spring.cloud.config.discovery.enabled: true -------------------------------------------------------------------------------- /consul-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: consul 4 | cloud: 5 | consul: 6 | host: consulagent 7 | -------------------------------------------------------------------------------- /discovery-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | discovery-microservice 7 | 0.1.0 8 | jar 9 | 10 | 11 | org.kbastani 12 | spring-cloud-microservice-example-parent 13 | 0.1.0-SNAPSHOT 14 | 15 | 16 | 17 | kbastani 18 | 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-eureka-server 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-maven-plugin 32 | 33 | 34 | 35 | repackage 36 | 37 | 38 | 39 | 40 | 41 | com.spotify 42 | docker-maven-plugin 43 | 0.2.3 44 | 45 | 46 | package 47 | 48 | build 49 | 50 | 51 | 52 | 53 | ${docker.image.prefix}/${project.artifactId} 54 | ${project.basedir}/src/main/docker 55 | 56 | 57 | / 58 | ${project.build.directory} 59 | ${project.build.finalName}.jar 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /discovery-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD discovery-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | EXPOSE 8761 6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 7 | -------------------------------------------------------------------------------- /discovery-microservice/src/main/java/services/Application.java: -------------------------------------------------------------------------------- 1 | package services; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class Application { 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /discovery-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8761 3 | 4 | eureka: 5 | instance: 6 | hostname: discovery 7 | client: 8 | registerWithEureka: false 9 | fetchRegistry: false 10 | serviceUrl: 11 | defaultZone: http://discovery:${server.port}/eureka/ 12 | 13 | spring.cloud.config.discovery.enabled: true -------------------------------------------------------------------------------- /discovery-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: discovery -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | hystrix: 2 | image: kbastani/hystrix-dashboard 3 | ports: 4 | - "7979:7979" 5 | links: 6 | - gateway 7 | - discovery 8 | discovery: 9 | image: kbastani/discovery-microservice 10 | ports: 11 | - "8761:8761" 12 | consulagent: 13 | image: progrium/consul 14 | ports: 15 | - "8500:8500" 16 | - "8400:8400" 17 | - "8600:53/udp" 18 | command: -server -bootstrap-expect 1 -ui-dir /ui 19 | net: host 20 | consul: 21 | image: kbastani/consul-microservice 22 | ports: 23 | - "8000:8000" 24 | net: host 25 | config: 26 | image: kbastani/config-microservice 27 | ports: 28 | - "8888:8888" 29 | links: 30 | - discovery 31 | gateway: 32 | image: kbastani/api-gateway-microservice 33 | ports: 34 | - "10000:10000" 35 | links: 36 | - discovery 37 | - config 38 | - user 39 | - movie 40 | - recommendation 41 | user: 42 | image: kbastani/users-microservice 43 | links: 44 | - discovery 45 | - config 46 | movie: 47 | image: kbastani/movie-microservice 48 | links: 49 | - discovery 50 | - config 51 | recommendation: 52 | image: kbastani/recommendation-microservice 53 | links: 54 | - discovery 55 | - config 56 | moviesui: 57 | image: kbastani/movies-ui 58 | ports: 59 | - "9006:9006" 60 | links: 61 | - discovery 62 | - config 63 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /hystrix-dashboard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.kbastani 7 | hystrix-dashboard 8 | jar 9 | http://projects.spring.io/spring-cloud/ 10 | 0.1.0 11 | 12 | 13 | spring-cloud-microservice-example-parent 14 | org.kbastani 15 | 0.1.0-SNAPSHOT 16 | 17 | 18 | 19 | kbastani 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-maven-plugin 27 | 28 | 29 | com.spotify 30 | docker-maven-plugin 31 | 0.2.3 32 | 33 | 34 | package 35 | 36 | build 37 | 38 | 39 | 40 | 41 | ${docker.image.prefix}/${project.artifactId} 42 | ${project.basedir}/src/main/docker 43 | 44 | 45 | / 46 | ${project.build.directory} 47 | ${project.build.finalName}.jar 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.cloud 58 | spring-cloud-starter-hystrix-dashboard 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-actuator 63 | 64 | 65 | org.springframework.cloud 66 | spring-cloud-starter-hystrix 67 | test 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /hystrix-dashboard/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD hystrix-dashboard-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 6 | -------------------------------------------------------------------------------- /hystrix-dashboard/src/main/java/hystrixdashboard/HystrixDashboardApplication.java: -------------------------------------------------------------------------------- 1 | package hystrixdashboard; 2 | 3 | import hystrixdashboard.stream.MockStreamServlet; 4 | 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.boot.builder.SpringApplicationBuilder; 7 | import org.springframework.boot.context.embedded.ServletRegistrationBean; 8 | import org.springframework.boot.context.web.SpringBootServletInitializer; 9 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.ComponentScan; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.stereotype.Controller; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | 16 | /** 17 | * Created by sgibb on 7/11/14. 18 | */ 19 | @Configuration 20 | @ComponentScan 21 | @EnableAutoConfiguration 22 | @Controller 23 | @EnableHystrixDashboard 24 | public class HystrixDashboardApplication extends SpringBootServletInitializer { 25 | 26 | @RequestMapping("/") 27 | public String home() { 28 | return "forward:/hystrix"; 29 | } 30 | 31 | @Override 32 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 33 | return application.sources(HystrixDashboardApplication.class).web(true); 34 | } 35 | 36 | public static void main(String[] args) { 37 | new SpringApplicationBuilder(HystrixDashboardApplication.class).web(true).run(args); 38 | } 39 | 40 | @Bean 41 | public ServletRegistrationBean mockStreamServlet() { 42 | return new ServletRegistrationBean(new MockStreamServlet(), "/mock.stream"); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | logging: 14 | level: 15 | ROOT: INFO 16 | org.springframework.web: DEBUG 17 | 18 | eureka: 19 | client: 20 | #Region where eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string 21 | #indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1 22 | region: default 23 | 24 | 25 | #For eureka clients running in eureka server, it needs to connect to servers in other zones 26 | preferSameZone: false 27 | 28 | #Change this if you want to use a DNS based lookup for determining other eureka servers. For example 29 | #of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties 30 | #shouldUseDns: false 31 | 32 | us-east-1: 33 | availabilityZones: default 34 | 35 | instance: 36 | #Virtual host name by which the clients identifies this service 37 | virtualHostName: ${spring.application.name} 38 | -------------------------------------------------------------------------------- /hystrix-dashboard/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: hystrixdashboard 4 | cloud: 5 | config: 6 | uri: ${vcap.services.${PREFIX:}configserver.credentials.uri:http://user:password@localhost:8888} 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /hystrix-dashboard/src/test/java/hystrixdashboard/test/SampleHystrixApp.java: -------------------------------------------------------------------------------- 1 | package hystrixdashboard.test; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration; 8 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | /** 15 | * @author Spencer Gibb 16 | */ 17 | @Configuration 18 | @EnableAutoConfiguration(exclude = FreeMarkerAutoConfiguration.class) 19 | @EnableHystrix 20 | @RestController 21 | public class SampleHystrixApp { 22 | 23 | @Autowired 24 | Service service; 25 | 26 | @RequestMapping("/") 27 | public String slash() { 28 | return service.hello(); 29 | } 30 | 31 | @Bean 32 | Service service() { 33 | return new Service(); 34 | } 35 | 36 | public static void main(String[] args) { 37 | SpringApplication.run(SampleHystrixApp.class, args); 38 | } 39 | } 40 | class Service { 41 | 42 | @HystrixCommand 43 | public String hello() { 44 | return "Hello"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /movie-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | movie-microservice 7 | 0.1.0 8 | jar 9 | 10 | 11 | org.kbastani 12 | spring-cloud-microservice-example-parent 13 | 0.1.0-SNAPSHOT 14 | 15 | 16 | 17 | kbastani 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-rest 24 | 25 | 26 | org.springframework.data 27 | spring-data-neo4j 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-zuul 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-eureka 36 | 37 | 38 | org.hibernate 39 | hibernate-validator 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-maven-plugin 48 | 49 | 50 | 51 | repackage 52 | 53 | 54 | 55 | 56 | 57 | com.spotify 58 | docker-maven-plugin 59 | 0.2.3 60 | 61 | 62 | package 63 | 64 | build 65 | 66 | 67 | 68 | 69 | ${docker.image.prefix}/${project.artifactId} 70 | ${project.basedir}/src/main/docker 71 | 72 | 73 | / 74 | ${project.build.directory} 75 | ${project.build.finalName}.jar 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /movie-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD movie-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | EXPOSE 9000 6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 7 | -------------------------------------------------------------------------------- /movie-microservice/src/main/java/data/Application.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 9 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.data.neo4j.config.EnableNeo4jRepositories; 12 | import org.springframework.data.neo4j.config.Neo4jConfiguration; 13 | 14 | @SpringBootApplication 15 | @EnableNeo4jRepositories 16 | @EnableDiscoveryClient 17 | @EnableZuulProxy 18 | @EnableHystrix 19 | public class Application extends Neo4jConfiguration { 20 | 21 | public Application() { 22 | setBasePackage("data"); 23 | } 24 | 25 | @Bean(destroyMethod = "shutdown") 26 | public GraphDatabaseService graphDatabaseService() { 27 | return new GraphDatabaseFactory().newEmbeddedDatabase("target/movie.db"); 28 | } 29 | 30 | public static void main(String[] args) { 31 | SpringApplication.run(Application.class, args); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /movie-microservice/src/main/java/data/domain/nodes/Genre.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import org.springframework.data.neo4j.annotation.GraphId; 4 | import org.springframework.data.neo4j.annotation.NodeEntity; 5 | 6 | @NodeEntity 7 | public class Genre { 8 | @GraphId 9 | private Long id; 10 | 11 | private String name; 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public void setName(String name) { 18 | this.name = name; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /movie-microservice/src/main/java/data/domain/nodes/Movie.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import org.springframework.data.neo4j.annotation.GraphId; 4 | import org.springframework.data.neo4j.annotation.NodeEntity; 5 | 6 | @NodeEntity 7 | public class Movie { 8 | @GraphId 9 | private Long id; 10 | 11 | private String title; 12 | 13 | public String getTitle() { 14 | return title; 15 | } 16 | 17 | public void setTitle(String title) { 18 | this.title = title; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /movie-microservice/src/main/java/data/domain/rels/HasGenre.java: -------------------------------------------------------------------------------- 1 | package data.domain.rels; 2 | 3 | import data.domain.nodes.Genre; 4 | import data.domain.nodes.Movie; 5 | import org.springframework.data.neo4j.annotation.EndNode; 6 | import org.springframework.data.neo4j.annotation.GraphId; 7 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 8 | import org.springframework.data.neo4j.annotation.StartNode; 9 | 10 | @RelationshipEntity(type="HAS_GENRE") 11 | public class HasGenre { 12 | @GraphId 13 | Long id; 14 | @StartNode 15 | Movie movie; 16 | @EndNode 17 | Genre genre; 18 | 19 | public HasGenre() { 20 | } 21 | 22 | public Movie getMovie() { 23 | return movie; 24 | } 25 | 26 | public void setMovie(Movie movie) { 27 | this.movie = movie; 28 | } 29 | 30 | public Genre getGenre() { 31 | return genre; 32 | } 33 | 34 | public void setGenre(Genre genre) { 35 | this.genre = genre; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /movie-microservice/src/main/java/data/repositories/GenreRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.Genre; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "genres", path = "genres") 11 | public interface GenreRepository extends PagingAndSortingRepository { 12 | List findByName(@Param("0") String name); 13 | } 14 | -------------------------------------------------------------------------------- /movie-microservice/src/main/java/data/repositories/MovieRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.Movie; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "movies", path = "movies") 11 | public interface MovieRepository extends PagingAndSortingRepository { 12 | List findByTitle(@Param("0") String title); 13 | } 14 | -------------------------------------------------------------------------------- /movie-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9005 3 | 4 | eureka: 5 | client: 6 | serviceUrl: 7 | defaultZone: http://discovery:8761/eureka/ 8 | instance: 9 | preferIpAddress: true 10 | 11 | ribbon: 12 | eureka: 13 | enabled: true -------------------------------------------------------------------------------- /movie-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: movie 4 | cloud: 5 | config: 6 | uri: http://configserver:8888 7 | encrypt: 8 | failOnError: false -------------------------------------------------------------------------------- /movies-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | movies-ui 6 | 0.1.0 7 | jar 8 | 9 | movies-ui 10 | Movies ui project for Spring Boot 11 | 12 | 13 | org.kbastani 14 | spring-cloud-microservice-example-parent 15 | 0.1.0-SNAPSHOT 16 | 17 | 18 | 19 | UTF-8 20 | 1.8 21 | kbastani 22 | 23 | 24 | 25 | 26 | com.vaadin 27 | vaadin-spring-boot-starter 28 | 1.0.0.beta3 29 | 30 | 31 | org.springframework.cloud 32 | spring-cloud-starter 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-starter-feign 37 | 38 | 39 | org.springframework.cloud 40 | spring-cloud-starter-zuul 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-eureka 45 | 46 | 47 | joda-time 48 | joda-time 49 | 2.7 50 | 51 | 52 | org.springframework.hateoas 53 | spring-hateoas 54 | 0.16.0.RELEASE 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-maven-plugin 63 | 64 | 65 | 66 | repackage 67 | 68 | 69 | 70 | 71 | 72 | com.spotify 73 | docker-maven-plugin 74 | 0.2.3 75 | 76 | 77 | package 78 | 79 | build 80 | 81 | 82 | 83 | 84 | ${docker.image.prefix}/${project.artifactId} 85 | ${project.basedir}/src/main/docker 86 | 87 | 88 | / 89 | ${project.build.directory} 90 | ${project.build.finalName}.jar 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | spring-snapshots 101 | Spring Snapshots 102 | https://repo.spring.io/snapshot 103 | 104 | true 105 | 106 | 107 | 108 | spring-milestones 109 | Spring Milestones 110 | https://repo.spring.io/milestone 111 | 112 | false 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | com.vaadin 121 | vaadin-bom 122 | 7.4.5 123 | pom 124 | import 125 | 126 | 127 | org.springframework.cloud 128 | spring-cloud-starter-parent 129 | Brixton.BUILD-SNAPSHOT 130 | pom 131 | import 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /movies-ui/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD movies-ui-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | EXPOSE 9006 6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 7 | -------------------------------------------------------------------------------- /movies-ui/src/main/java/application/Application.java: -------------------------------------------------------------------------------- 1 | package application; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.context.web.SpringBootServletInitializer; 9 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 10 | import org.springframework.cloud.netflix.feign.EnableFeignClients; 11 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 12 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.hateoas.hal.Jackson2HalModule; 15 | import org.springframework.http.MediaType; 16 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 17 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 18 | 19 | @SpringBootApplication 20 | @EnableFeignClients 21 | @EnableWebMvc 22 | @EnableAutoConfiguration 23 | @EnableDiscoveryClient 24 | @EnableZuulProxy 25 | @EnableHystrix 26 | public class Application extends SpringBootServletInitializer { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(Application.class, args); 30 | } 31 | 32 | 33 | @Bean 34 | public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { 35 | MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); 36 | jsonConverter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json")); 37 | ObjectMapper objectMapper = new ObjectMapper(); 38 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 39 | objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); 40 | objectMapper.registerModule(new Jackson2HalModule()); 41 | jsonConverter.setObjectMapper(objectMapper); 42 | return jsonConverter; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /movies-ui/src/main/java/application/clients/UserClient.java: -------------------------------------------------------------------------------- 1 | package application.clients; 2 | 3 | import application.models.User; 4 | import org.springframework.cloud.netflix.feign.FeignClient; 5 | import org.springframework.hateoas.PagedResources; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | 12 | import java.util.List; 13 | 14 | @FeignClient("user") 15 | public interface UserClient { 16 | 17 | @RequestMapping(method = RequestMethod.GET, value = "/users") 18 | PagedResources findAll(); 19 | 20 | @RequestMapping(method = RequestMethod.GET, value = "/users/{id}") 21 | List findById(@RequestParam("id") String id); 22 | 23 | @RequestMapping(method = RequestMethod.POST, value = "/users", 24 | consumes = MediaType.APPLICATION_JSON_VALUE, 25 | produces = MediaType.APPLICATION_JSON_VALUE) 26 | void createUser(@RequestBody User user); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /movies-ui/src/main/java/application/models/User.java: -------------------------------------------------------------------------------- 1 | package application.models; 2 | 3 | import org.joda.time.DateTime; 4 | 5 | public class User { 6 | private Long id; 7 | private String firstName; 8 | private String lastName; 9 | private String phone; 10 | private String email; 11 | private DateTime birthDate; 12 | 13 | public String getFirstName() { 14 | return firstName; 15 | } 16 | 17 | public void setFirstName(String firstName) { 18 | this.firstName = firstName; 19 | } 20 | 21 | public String getLastName() { 22 | return lastName; 23 | } 24 | 25 | public void setLastName(String lastName) { 26 | this.lastName = lastName; 27 | } 28 | 29 | public String getPhone() { 30 | return phone; 31 | } 32 | 33 | public void setPhone(String phone) { 34 | this.phone = phone; 35 | } 36 | 37 | public String getEmail() { 38 | return email; 39 | } 40 | 41 | public void setEmail(String email) { 42 | this.email = email; 43 | } 44 | 45 | public DateTime getBirthDate() { 46 | return birthDate; 47 | } 48 | 49 | public void setBirthDate(DateTime birthDate) { 50 | this.birthDate = birthDate; 51 | } 52 | 53 | public Long getId() { 54 | return id; 55 | } 56 | 57 | public void setId(Long id) { 58 | this.id = id; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /movies-ui/src/main/java/application/ui/AddressbookUI.java: -------------------------------------------------------------------------------- 1 | package application.ui; 2 | 3 | 4 | import application.clients.UserClient; 5 | import application.models.User; 6 | import com.vaadin.annotations.Theme; 7 | import com.vaadin.annotations.Title; 8 | import com.vaadin.annotations.VaadinServletConfiguration; 9 | import com.vaadin.data.util.BeanItemContainer; 10 | import com.vaadin.server.VaadinRequest; 11 | import com.vaadin.server.VaadinServlet; 12 | import com.vaadin.spring.annotation.SpringUI; 13 | import com.vaadin.ui.*; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | 16 | import javax.servlet.annotation.WebServlet; 17 | 18 | @SpringUI(path = "") 19 | @Title("Addressbook") 20 | @Theme("valo") 21 | public class AddressbookUI extends UI { 22 | 23 | /* Hundreds of widgets. 24 | * Vaadin's user interface components are just Java objects that encapsulate 25 | * and handle cross-browser support and client-server communication. The 26 | * default Vaadin components are in the com.vaadin.ui package and there 27 | * are over 500 more in vaadin.com/directory. 28 | */ 29 | TextField filter = new TextField(); 30 | Grid contactList = new Grid(); 31 | Button newContact = new Button("New contact"); 32 | 33 | // ContactForm is an example of a custom component class 34 | ContactForm contactForm = new ContactForm(); 35 | 36 | // ContactService is a in-memory mock DAO that mimics 37 | // a real-world datasource. Typically implemented for 38 | // example as EJB or Spring Data based service. 39 | @Autowired 40 | UserClient userClient; 41 | 42 | 43 | /* The "Main method". 44 | * 45 | * This is the entry point method executed to initialize and configure 46 | * the visible user interface. Executed on every browser reload because 47 | * a new instance is created for each web page loaded. 48 | */ 49 | @Override 50 | protected void init(VaadinRequest request) { 51 | configureComponents(); 52 | buildLayout(); 53 | } 54 | 55 | 56 | private void configureComponents() { 57 | /* Synchronous event handling. 58 | * 59 | * Receive user interaction events on the server-side. This allows you 60 | * to synchronously handle those events. Vaadin automatically sends 61 | * only the needed changes to the web page without loading a new page. 62 | */ 63 | newContact.addClickListener(e -> contactForm.edit(new User())); 64 | 65 | filter.setInputPrompt("Filter contacts..."); 66 | filter.addTextChangeListener(e -> refreshContacts(e.getText())); 67 | 68 | contactList.setContainerDataSource(new BeanItemContainer<>(User.class)); 69 | contactList.setColumnOrder("id", "firstName", "lastName", "email"); 70 | contactList.removeColumn("birthDate"); 71 | contactList.setSelectionMode(Grid.SelectionMode.SINGLE); 72 | contactList.addSelectionListener(e 73 | -> contactForm.edit((User) contactList.getSelectedRow())); 74 | refreshContacts(); 75 | } 76 | 77 | /* Robust layouts. 78 | * 79 | * Layouts are components that contain other components. 80 | * HorizontalLayout contains TextField and Button. It is wrapped 81 | * with a Grid into VerticalLayout for the left side of the screen. 82 | * Allow user to resize the components with a SplitPanel. 83 | * 84 | * In addition to programmatically building layout in Java, 85 | * you may also choose to setup layout declaratively 86 | * with Vaadin Designer, CSS and HTML. 87 | */ 88 | private void buildLayout() { 89 | HorizontalLayout actions = new HorizontalLayout(filter, newContact); 90 | actions.setWidth("100%"); 91 | filter.setWidth("100%"); 92 | actions.setExpandRatio(filter, 1); 93 | 94 | VerticalLayout left = new VerticalLayout(actions, contactList); 95 | left.setSizeFull(); 96 | contactList.setSizeFull(); 97 | left.setExpandRatio(contactList, 1); 98 | 99 | HorizontalLayout mainLayout = new HorizontalLayout(left, contactForm); 100 | mainLayout.setSizeFull(); 101 | mainLayout.setExpandRatio(left, 1); 102 | 103 | // Split and allow resizing 104 | setContent(mainLayout); 105 | } 106 | 107 | /* Choose the design patterns you like. 108 | * 109 | * It is good practice to have separate data access methods that 110 | * handle the back-end access and/or the user interface updates. 111 | * You can further split your code into classes to easier maintenance. 112 | * With Vaadin you can follow MVC, MVP or any other design pattern 113 | * you choose. 114 | */ 115 | void refreshContacts() { 116 | refreshContacts(filter.getValue()); 117 | } 118 | 119 | private void refreshContacts(String stringFilter) { 120 | contactList.setContainerDataSource(new BeanItemContainer<>( 121 | User.class, userClient.findAll().getContent())); 122 | contactForm.setVisible(false); 123 | } 124 | 125 | 126 | /* Deployed as a Servlet or Portlet. 127 | * 128 | * You can specify additional servlet parameters like the URI and UI 129 | * class name and turn on production mode when you have finished developing the application. 130 | */ 131 | @WebServlet(urlPatterns = "/*") 132 | @VaadinServletConfiguration(ui = AddressbookUI.class, productionMode = false) 133 | public static class MyUIServlet extends VaadinServlet { 134 | } 135 | 136 | 137 | } 138 | -------------------------------------------------------------------------------- /movies-ui/src/main/java/application/ui/ContactForm.java: -------------------------------------------------------------------------------- 1 | package application.ui; 2 | 3 | import application.models.User; 4 | import com.vaadin.data.fieldgroup.BeanFieldGroup; 5 | import com.vaadin.data.fieldgroup.FieldGroup; 6 | import com.vaadin.event.ShortcutAction; 7 | import com.vaadin.ui.*; 8 | import com.vaadin.ui.Notification.Type; 9 | import com.vaadin.ui.themes.ValoTheme; 10 | 11 | /* Create custom UI Components. 12 | * 13 | * Create your own Vaadin components by inheritance and composition. 14 | * This is a form component inherited from VerticalLayout. Use 15 | * Use BeanFieldGroup to bind data fields from DTO to UI fields. 16 | * Similarly named field by naming convention or customized 17 | * with @PropertyId annotation. 18 | */ 19 | public class ContactForm extends FormLayout { 20 | 21 | Button save = new Button("Save", this::save); 22 | Button cancel = new Button("Cancel", this::cancel); 23 | TextField firstName = new TextField("First name"); 24 | TextField lastName = new TextField("Last name"); 25 | TextField phone = new TextField("Phone"); 26 | TextField email = new TextField("Email"); 27 | DateField birthDate = new DateField("Birth date"); 28 | 29 | User contact; 30 | 31 | // Easily bind forms to beans and manage validation and buffering 32 | BeanFieldGroup formFieldBindings; 33 | 34 | public ContactForm() { 35 | configureComponents(); 36 | buildLayout(); 37 | } 38 | 39 | private void configureComponents() { 40 | /* Highlight primary actions. 41 | * 42 | * With Vaadin built-in styles you can highlight the primary save button 43 | * and give it a keyboard shortcut for a better UX. 44 | */ 45 | save.setStyleName(ValoTheme.BUTTON_PRIMARY); 46 | save.setClickShortcut(ShortcutAction.KeyCode.ENTER); 47 | setVisible(false); 48 | } 49 | 50 | private void buildLayout() { 51 | setSizeUndefined(); 52 | setMargin(true); 53 | 54 | HorizontalLayout actions = new HorizontalLayout(save, cancel); 55 | actions.setSpacing(true); 56 | 57 | addComponents(actions, firstName, lastName, phone, email, birthDate); 58 | } 59 | 60 | /* Use any JVM language. 61 | * 62 | * Vaadin supports all languages supported by Java Virtual Machine 1.6+. 63 | * This allows you to program user interface in Java 8, Scala, Groovy or any other 64 | * language you choose. 65 | * The new languages give you very powerful tools for organizing your code 66 | * as you choose. For example, you can implement the listener methods in your 67 | * compositions or in separate controller classes and receive 68 | * to various Vaadin component events, like button clicks. Or keep it simple 69 | * and compact with Lambda expressions. 70 | */ 71 | public void save(Button.ClickEvent event) { 72 | try { 73 | // Commit the fields from UI to DAO 74 | formFieldBindings.commit(); 75 | 76 | // Save DAO to backend with direct synchronous service API 77 | getUI().userClient.createUser(contact); 78 | 79 | String msg = String.format("Saved '%s %s'.", 80 | contact.getFirstName(), 81 | contact.getLastName()); 82 | Notification.show(msg, Type.TRAY_NOTIFICATION); 83 | getUI().refreshContacts(); 84 | } catch (FieldGroup.CommitException e) { 85 | // Validation exceptions could be shown here 86 | } 87 | } 88 | 89 | public void cancel(Button.ClickEvent event) { 90 | // Place to call business logic. 91 | Notification.show("Cancelled", Type.TRAY_NOTIFICATION); 92 | getUI().contactList.select(null); 93 | } 94 | 95 | void edit(User contact) { 96 | this.contact = contact; 97 | if (contact != null) { 98 | // Bind the properties of the contact POJO to fiels in this form 99 | formFieldBindings = BeanFieldGroup.bindFieldsBuffered(contact, this); 100 | firstName.focus(); 101 | } 102 | setVisible(contact != null); 103 | } 104 | 105 | @Override 106 | public AddressbookUI getUI() { 107 | return (AddressbookUI) super.getUI(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /movies-ui/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9006 3 | 4 | eureka: 5 | client: 6 | serviceUrl: 7 | defaultZone: http://discovery:8761/eureka/ 8 | instance: 9 | preferIpAddress: true 10 | 11 | ribbon: 12 | eureka: 13 | enabled: true -------------------------------------------------------------------------------- /movies-ui/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: moviesui 4 | cloud: 5 | config: 6 | uri: http://configserver:8888 7 | encrypt: 8 | failOnError: false -------------------------------------------------------------------------------- /movies-ui/src/test/java/application/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package application; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.test.context.web.WebAppConfiguration; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = Application.class) 11 | @WebAppConfiguration 12 | public class DemoApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.kbastani 7 | spring-cloud-microservice-example-parent 8 | 0.1.0-SNAPSHOT 9 | pom 10 | 11 | 12 | users-microservice 13 | discovery-microservice 14 | api-gateway-microservice 15 | recommendation-microservice 16 | config-microservice 17 | hystrix-dashboard 18 | consul-microservice 19 | movie-microservice 20 | movies-ui 21 | 22 | 23 | 24 | org.springframework.cloud 25 | spring-cloud-starter-parent 26 | Brixton.BUILD-SNAPSHOT 27 | 28 | 29 | 30 | 31 | 32 | UTF-8 33 | 1.8 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-web 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-actuator 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-starter-parent 57 | Brixton.BUILD-SNAPSHOT 58 | pom 59 | import 60 | 61 | 62 | 63 | 64 | 65 | 66 | spring-snapshots 67 | Spring Snapshots 68 | http://repo.spring.io/libs-snapshot-local 69 | 70 | true 71 | 72 | 73 | 74 | spring-milestones 75 | Spring Milestones 76 | http://repo.spring.io/libs-milestone-local 77 | 78 | false 79 | 80 | 81 | 82 | spring-releases 83 | Spring Releases 84 | http://repo.spring.io/libs-release-local 85 | 86 | false 87 | 88 | 89 | 90 | 91 | 92 | spring-snapshots 93 | Spring Snapshots 94 | http://repo.spring.io/libs-snapshot-local 95 | 96 | true 97 | 98 | 99 | 100 | spring-milestones 101 | Spring Milestones 102 | http://repo.spring.io/libs-milestone-local 103 | 104 | false 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /recommendation-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | spring-cloud-microservice-example-parent 8 | org.kbastani 9 | 0.1.0-SNAPSHOT 10 | 11 | 12 | 4.0.0 13 | 14 | recommendation-microservice 15 | 0.1.0 16 | jar 17 | 18 | 19 | kbastani 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-data-rest 26 | 27 | 28 | org.springframework.data 29 | spring-data-neo4j 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-zuul 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-starter-eureka 38 | 39 | 40 | org.hibernate 41 | hibernate-validator 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | 53 | repackage 54 | 55 | 56 | 57 | 58 | 59 | com.spotify 60 | docker-maven-plugin 61 | 0.2.3 62 | 63 | 64 | package 65 | 66 | build 67 | 68 | 69 | 70 | 71 | ${docker.image.prefix}/${project.artifactId} 72 | ${project.basedir}/src/main/docker 73 | 74 | 75 | / 76 | ${project.build.directory} 77 | ${project.build.finalName}.jar 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD recommendation-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 6 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/Application.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 9 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.data.neo4j.config.EnableNeo4jRepositories; 12 | import org.springframework.data.neo4j.config.Neo4jConfiguration; 13 | 14 | @SpringBootApplication 15 | @EnableNeo4jRepositories 16 | @EnableDiscoveryClient 17 | @EnableZuulProxy 18 | @EnableHystrix 19 | public class Application extends Neo4jConfiguration { 20 | 21 | public Application() { 22 | setBasePackage("data"); 23 | } 24 | 25 | @Bean(destroyMethod = "shutdown") 26 | public GraphDatabaseService graphDatabaseService() { 27 | return new GraphDatabaseFactory().newEmbeddedDatabase("target/recommendation.db"); 28 | } 29 | 30 | public static void main(String[] args) { 31 | SpringApplication.run(Application.class, args); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/domain/nodes/Event.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import org.neo4j.graphdb.Direction; 4 | import org.springframework.data.neo4j.annotation.Fetch; 5 | import org.springframework.data.neo4j.annotation.GraphId; 6 | import org.springframework.data.neo4j.annotation.NodeEntity; 7 | import org.springframework.data.neo4j.annotation.RelatedTo; 8 | 9 | @NodeEntity 10 | public class Event { 11 | @GraphId 12 | private Long id; 13 | private String eventType; 14 | 15 | 16 | @Fetch 17 | @RelatedTo(type = "ON", direction = Direction.OUTGOING) 18 | Job job; 19 | 20 | public Event() { 21 | } 22 | 23 | public Event(String eventType) { 24 | this.eventType = eventType; 25 | } 26 | 27 | public String getEventType() { 28 | return eventType; 29 | } 30 | 31 | public void setEventType(String eventType) { 32 | this.eventType = eventType; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/domain/nodes/Job.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import org.neo4j.graphdb.Direction; 4 | import org.springframework.data.neo4j.annotation.Fetch; 5 | import org.springframework.data.neo4j.annotation.GraphId; 6 | import org.springframework.data.neo4j.annotation.NodeEntity; 7 | import org.springframework.data.neo4j.annotation.RelatedTo; 8 | 9 | import java.util.Set; 10 | 11 | @NodeEntity 12 | public class Job { 13 | @GraphId 14 | private Long id; 15 | 16 | private String title; 17 | private String url; 18 | 19 | @Fetch 20 | @RelatedTo(type = "ON", direction = Direction.INCOMING) 21 | Set events; 22 | 23 | public String getTitle() { 24 | return title; 25 | } 26 | 27 | public void setTitle(String title) { 28 | this.title = title; 29 | } 30 | 31 | public String getUrl() { 32 | return url; 33 | } 34 | 35 | public void setUrl(String url) { 36 | this.url = url; 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/domain/rels/On.java: -------------------------------------------------------------------------------- 1 | package data.domain.rels; 2 | 3 | import data.domain.nodes.Job; 4 | import data.domain.nodes.Event; 5 | import org.springframework.data.neo4j.annotation.EndNode; 6 | import org.springframework.data.neo4j.annotation.GraphId; 7 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 8 | import org.springframework.data.neo4j.annotation.StartNode; 9 | 10 | @RelationshipEntity(type="ON") 11 | public class On { 12 | @GraphId 13 | Long id; 14 | @StartNode 15 | Event event; 16 | @EndNode 17 | Job job; 18 | 19 | public On() { 20 | } 21 | 22 | public Event getEvent() { 23 | return event; 24 | } 25 | 26 | public void setEvent(Event event) { 27 | this.event = event; 28 | } 29 | 30 | public Job getJob() { 31 | return job; 32 | } 33 | 34 | public void setJob(Job job) { 35 | this.job = job; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/repositories/EventRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.Event; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "events", path = "events") 11 | public interface EventRepository extends PagingAndSortingRepository { 12 | List findByEventType(@Param("0") String eventType); 13 | } 14 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/repositories/JobRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.Job; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "job", path = "job") 11 | public interface JobRepository extends PagingAndSortingRepository { 12 | List findByTitle(@Param("0") String title); 13 | } 14 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/java/data/repositories/OnRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.rels.On; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 6 | 7 | @RepositoryRestResource(collectionResourceRel = "on", path = "on") 8 | public interface OnRepository extends PagingAndSortingRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /recommendation-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9003 3 | 4 | eureka: 5 | client: 6 | serviceUrl: 7 | defaultZone: http://discovery:8761/eureka/ 8 | instance: 9 | preferIpAddress: true 10 | 11 | ribbon: 12 | eureka: 13 | enabled: true -------------------------------------------------------------------------------- /recommendation-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: recommendation 4 | cloud: 5 | config: 6 | uri: http://configserver:8888 7 | encrypt: 8 | failOnError: false -------------------------------------------------------------------------------- /users-microservice/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 15 13:48:01 EDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip 7 | -------------------------------------------------------------------------------- /users-microservice/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: users-microservice 4 | host: users-microservice-${random-word} 5 | memory: 512M 6 | instances: 1 7 | timeout: 90 8 | path: target/users-microservice-0.1.0.jar 9 | env: 10 | SPRING_PROFILES_ACTIVE: cloud -------------------------------------------------------------------------------- /users-microservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | users-microservice 6 | 0.1.0 7 | jar 8 | 9 | 10 | org.kbastani 11 | spring-cloud-microservice-example-parent 12 | 0.1.0-SNAPSHOT 13 | 14 | 15 | 16 | kbastani 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-rest 23 | 24 | 25 | org.springframework.data 26 | spring-data-neo4j 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-starter-zuul 31 | 32 | 33 | org.springframework.cloud 34 | spring-cloud-starter-eureka 35 | 36 | 37 | org.hibernate 38 | hibernate-validator 39 | 40 | 41 | joda-time 42 | joda-time 43 | 2.7 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | 55 | repackage 56 | 57 | 58 | 59 | 60 | 61 | com.spotify 62 | docker-maven-plugin 63 | 0.2.3 64 | 65 | 66 | package 67 | 68 | build 69 | 70 | 71 | 72 | 73 | ${docker.image.prefix}/${project.artifactId} 74 | ${project.basedir}/src/main/docker 75 | 76 | 77 | / 78 | ${project.build.directory} 79 | ${project.build.finalName}.jar 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /users-microservice/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | VOLUME /tmp 3 | ADD users-microservice-0.1.0.jar app.jar 4 | RUN bash -c 'touch /app.jar' 5 | EXPOSE 9000 6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 7 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/Application.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import data.domain.nodes.User; 4 | import org.neo4j.graphdb.GraphDatabaseService; 5 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 9 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 10 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 11 | import org.springframework.context.ConfigurableApplicationContext; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.data.neo4j.config.EnableNeo4jRepositories; 14 | import org.springframework.data.neo4j.config.Neo4jConfiguration; 15 | import org.springframework.data.rest.core.config.RepositoryRestConfiguration; 16 | import org.springframework.hateoas.Link; 17 | import org.springframework.hateoas.Resource; 18 | import org.springframework.hateoas.ResourceProcessor; 19 | 20 | @SpringBootApplication 21 | @EnableNeo4jRepositories 22 | @EnableDiscoveryClient 23 | @EnableZuulProxy 24 | @EnableHystrix 25 | public class Application extends Neo4jConfiguration { 26 | 27 | public Application() { 28 | setBasePackage("data"); 29 | } 30 | 31 | @Bean(destroyMethod = "shutdown") 32 | public GraphDatabaseService graphDatabaseService() { 33 | return new GraphDatabaseFactory().newEmbeddedDatabase("target/test2.db"); 34 | } 35 | 36 | public static void main(String[] args) { 37 | ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args); 38 | 39 | RepositoryRestConfiguration restConfiguration = ctx.getBean("config", RepositoryRestConfiguration.class); 40 | restConfiguration.exposeIdsFor(User.class); 41 | } 42 | 43 | @Bean 44 | public ResourceProcessor> movieProcessor() { 45 | return new ResourceProcessor>() { 46 | @Override 47 | public Resource process(Resource resource) { 48 | 49 | resource.add(new Link("/movie/movies", "movies")); 50 | return resource; 51 | } 52 | }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/config/DateTimeToLongConverter.java: -------------------------------------------------------------------------------- 1 | package data.config; 2 | 3 | import org.joda.time.DateTime; 4 | import org.springframework.core.convert.converter.Converter; 5 | 6 | public class DateTimeToLongConverter implements Converter { 7 | 8 | @Override 9 | public Long convert(DateTime source) { 10 | return source.toDateTimeISO().getMillis(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/config/LongToDateTimeConverter.java: -------------------------------------------------------------------------------- 1 | package data.config; 2 | 3 | import org.joda.time.DateTime; 4 | import org.springframework.core.convert.converter.Converter; 5 | 6 | public class LongToDateTimeConverter implements Converter { 7 | 8 | @Override 9 | public DateTime convert(Long source) { 10 | return new DateTime(source); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/nodes/Content.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import org.neo4j.graphdb.Direction; 4 | import org.springframework.data.neo4j.annotation.Fetch; 5 | import org.springframework.data.neo4j.annotation.GraphId; 6 | import org.springframework.data.neo4j.annotation.NodeEntity; 7 | import org.springframework.data.neo4j.annotation.RelatedTo; 8 | 9 | import java.util.Set; 10 | 11 | @NodeEntity 12 | public class Content { 13 | @GraphId 14 | private Long id; 15 | 16 | private String title; 17 | private String url; 18 | 19 | @Fetch 20 | @RelatedTo(type = "ON", direction = Direction.INCOMING) 21 | Set events; 22 | 23 | public String getTitle() { 24 | return title; 25 | } 26 | 27 | public void setTitle(String title) { 28 | this.title = title; 29 | } 30 | 31 | public String getUrl() { 32 | return url; 33 | } 34 | 35 | public void setUrl(String url) { 36 | this.url = url; 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/nodes/Event.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import org.neo4j.graphdb.Direction; 4 | import org.springframework.data.neo4j.annotation.Fetch; 5 | import org.springframework.data.neo4j.annotation.GraphId; 6 | import org.springframework.data.neo4j.annotation.NodeEntity; 7 | import org.springframework.data.neo4j.annotation.RelatedTo; 8 | 9 | @NodeEntity 10 | public class Event { 11 | @GraphId 12 | private Long id; 13 | private String eventType; 14 | 15 | @Fetch 16 | @RelatedTo(type = "ACTION", direction = Direction.INCOMING) 17 | User user; 18 | 19 | @Fetch 20 | @RelatedTo(type = "ON", direction = Direction.OUTGOING) 21 | Content content; 22 | 23 | public Event() { 24 | } 25 | 26 | public Event(String eventType) { 27 | this.eventType = eventType; 28 | } 29 | 30 | public String getEventType() { 31 | return eventType; 32 | } 33 | 34 | public void setEventType(String eventType) { 35 | this.eventType = eventType; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/nodes/User.java: -------------------------------------------------------------------------------- 1 | package data.domain.nodes; 2 | 3 | import data.domain.rels.Action; 4 | import org.neo4j.graphdb.Direction; 5 | import org.springframework.data.neo4j.annotation.*; 6 | 7 | import java.util.Set; 8 | 9 | @NodeEntity 10 | public class User { 11 | 12 | @GraphId 13 | Long id; 14 | 15 | private String firstName; 16 | private String lastName; 17 | private String phone; 18 | private String email; 19 | @GraphProperty(propertyType = Long.class) 20 | private Long birthDate; 21 | 22 | @Fetch 23 | @RelatedTo(type = "FOLLOWS", direction = Direction.OUTGOING, elementClass = User.class) 24 | Set follows; 25 | 26 | @Fetch 27 | @RelatedTo(type = "FOLLOWS", direction = Direction.INCOMING, elementClass = User.class) 28 | Set followers; 29 | 30 | @RelatedToVia(type = "ACTION", direction = Direction.OUTGOING) 31 | Set actions; 32 | 33 | @RelatedTo(type = "ACTION", direction = Direction.OUTGOING) 34 | Set events; 35 | 36 | public String getFirstName() { 37 | return firstName; 38 | } 39 | 40 | public void setFirstName(String firstName) { 41 | this.firstName = firstName; 42 | } 43 | 44 | public String getLastName() { 45 | return lastName; 46 | } 47 | 48 | public void setLastName(String lastName) { 49 | this.lastName = lastName; 50 | } 51 | 52 | public String getPhone() { 53 | return phone; 54 | } 55 | 56 | public void setPhone(String phone) { 57 | this.phone = phone; 58 | } 59 | 60 | public String getEmail() { 61 | return email; 62 | } 63 | 64 | public void setEmail(String email) { 65 | this.email = email; 66 | } 67 | 68 | public Long getBirthDate() { 69 | return birthDate; 70 | } 71 | 72 | public void setBirthDate(Long birthDate) { 73 | this.birthDate = birthDate; 74 | } 75 | 76 | public Long getId() { 77 | return id; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/rels/Action.java: -------------------------------------------------------------------------------- 1 | package data.domain.rels; 2 | 3 | import data.domain.nodes.Event; 4 | import data.domain.nodes.User; 5 | import org.springframework.data.neo4j.annotation.EndNode; 6 | import org.springframework.data.neo4j.annotation.GraphId; 7 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 8 | import org.springframework.data.neo4j.annotation.StartNode; 9 | 10 | @RelationshipEntity(type="ACTION") 11 | public class Action { 12 | @GraphId 13 | Long id; 14 | 15 | public User getActor() { 16 | return actor; 17 | } 18 | 19 | public void setActor(User actor) { 20 | this.actor = actor; 21 | } 22 | 23 | @StartNode 24 | User actor; 25 | 26 | public Event getEvent() { 27 | return event; 28 | } 29 | 30 | public void setEvent(Event event) { 31 | this.event = event; 32 | } 33 | 34 | @EndNode 35 | Event event; 36 | 37 | public Action(User actor, Event event) { 38 | this.actor = actor; 39 | this.event = event; 40 | } 41 | 42 | public Action() { 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/rels/Caused.java: -------------------------------------------------------------------------------- 1 | package data.domain.rels; 2 | 3 | import data.domain.nodes.Event; 4 | import data.domain.nodes.User; 5 | import org.springframework.data.neo4j.annotation.EndNode; 6 | import org.springframework.data.neo4j.annotation.GraphId; 7 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 8 | import org.springframework.data.neo4j.annotation.StartNode; 9 | 10 | @RelationshipEntity(type = "CAUSED") 11 | public class Caused { 12 | @GraphId 13 | Long id; 14 | @StartNode 15 | User user; 16 | @EndNode 17 | Event event; 18 | } 19 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/rels/Follows.java: -------------------------------------------------------------------------------- 1 | package data.domain.rels; 2 | 3 | import data.domain.nodes.User; 4 | import org.springframework.data.neo4j.annotation.EndNode; 5 | import org.springframework.data.neo4j.annotation.GraphId; 6 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 7 | import org.springframework.data.neo4j.annotation.StartNode; 8 | 9 | @RelationshipEntity(type="FOLLOWS") 10 | public class Follows { 11 | @GraphId 12 | Long id; 13 | @StartNode 14 | User follower; 15 | @EndNode 16 | User follows; 17 | 18 | public Follows() { 19 | } 20 | 21 | public User getFollower() { 22 | return follower; 23 | } 24 | 25 | public void setFollower(User follower) { 26 | this.follower = follower; 27 | } 28 | 29 | public User getFollows() { 30 | return follows; 31 | } 32 | 33 | public void setFollows(User follows) { 34 | this.follows = follows; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/domain/rels/On.java: -------------------------------------------------------------------------------- 1 | package data.domain.rels; 2 | 3 | import data.domain.nodes.Content; 4 | import data.domain.nodes.Event; 5 | import org.springframework.data.neo4j.annotation.EndNode; 6 | import org.springframework.data.neo4j.annotation.GraphId; 7 | import org.springframework.data.neo4j.annotation.RelationshipEntity; 8 | import org.springframework.data.neo4j.annotation.StartNode; 9 | 10 | @RelationshipEntity(type="ON") 11 | public class On { 12 | @GraphId 13 | Long id; 14 | @StartNode 15 | Event event; 16 | @EndNode 17 | Content content; 18 | 19 | public On() { 20 | } 21 | 22 | public Event getEvent() { 23 | return event; 24 | } 25 | 26 | public void setEvent(Event event) { 27 | this.event = event; 28 | } 29 | 30 | public Content getContent() { 31 | return content; 32 | } 33 | 34 | public void setContent(Content content) { 35 | this.content = content; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/repositories/ActionRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | 4 | import data.domain.rels.Action; 5 | import org.springframework.data.repository.PagingAndSortingRepository; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | @RepositoryRestResource(collectionResourceRel = "actions", path = "actions") 9 | public interface ActionRepository extends PagingAndSortingRepository { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/repositories/ContentRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.Content; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "content", path = "content") 11 | public interface ContentRepository extends PagingAndSortingRepository { 12 | List findByTitle(@Param("0") String title); 13 | } 14 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/repositories/EventRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.Event; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "events", path = "events") 11 | public interface EventRepository extends PagingAndSortingRepository { 12 | List findByEventType(@Param("0") String eventType); 13 | } 14 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/repositories/FollowsRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.rels.Follows; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 6 | 7 | @RepositoryRestResource(collectionResourceRel = "follows", path = "follows") 8 | public interface FollowsRepository extends PagingAndSortingRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/repositories/OnRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.rels.On; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 6 | 7 | @RepositoryRestResource(collectionResourceRel = "on", path = "on") 8 | public interface OnRepository extends PagingAndSortingRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /users-microservice/src/main/java/data/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package data.repositories; 2 | 3 | import data.domain.nodes.User; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 7 | 8 | import java.util.List; 9 | 10 | @RepositoryRestResource(collectionResourceRel = "users", path = "users") 11 | public interface UserRepository extends PagingAndSortingRepository { 12 | 13 | // TODO: Replace with @Param("name") when Spring Data Neo4j supports names vs. positional arguments 14 | List findByLastName(@Param("0") String name); 15 | 16 | List findAll(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /users-microservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | 4 | eureka: 5 | client: 6 | serviceUrl: 7 | defaultZone: http://discovery:8761/eureka/ 8 | instance: 9 | preferIpAddress: true 10 | 11 | ribbon: 12 | eureka: 13 | enabled: true -------------------------------------------------------------------------------- /users-microservice/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: user 4 | cloud: 5 | config: 6 | uri: http://configserver:8888 7 | encrypt: 8 | failOnError: false --------------------------------------------------------------------------------