├── confsvr ├── src │ └── main │ │ ├── resources │ │ ├── bootstrap.yml │ │ ├── config │ │ │ ├── organizationservice │ │ │ │ └── organizationservice.yml │ │ │ └── licensingservice │ │ │ │ ├── licensingservice-dev.yml │ │ │ │ ├── licensingservice.yml │ │ │ │ └── licensingservice-prod.yml │ │ └── application.yml │ │ ├── docker │ │ ├── Dockerfile │ │ └── run.sh │ │ └── java │ │ └── com │ │ └── thoughtmechanix │ │ └── confsvr │ │ └── ConfigServerApplication.java └── pom.xml ├── organization-service ├── src │ └── main │ │ ├── resources │ │ ├── bootstrap.yml │ │ ├── application.yml │ │ └── schema.sql │ │ ├── docker │ │ ├── Dockerfile │ │ └── run.sh │ │ └── java │ │ └── com │ │ └── thoughtmechanix │ │ └── organization │ │ ├── repository │ │ └── OrganizationRepository.java │ │ ├── Application.java │ │ ├── utils │ │ ├── UserContextHolder.java │ │ ├── UserContextInterceptor.java │ │ ├── UserContext.java │ │ └── UserContextFilter.java │ │ ├── services │ │ └── OrganizationService.java │ │ ├── model │ │ └── Organization.java │ │ ├── hystrix │ │ ├── DelegatingUserContextCallable.java │ │ ├── ThreadLocalConfiguration.java │ │ └── ThreadLocalAwareStrategy.java │ │ └── controllers │ │ └── OrganizationServiceController.java └── pom.xml ├── eurekasvr ├── src │ └── main │ │ ├── docker │ │ ├── Dockerfile │ │ └── run.sh │ │ ├── resources │ │ └── application.yml │ │ └── java │ │ └── com │ │ └── thoughtmechanix │ │ └── eurekasvr │ │ └── EurekaServerApplication.java └── pom.xml ├── licensing-service ├── src │ └── main │ │ ├── docker │ │ ├── Dockerfile │ │ └── run.sh │ │ ├── resources │ │ ├── bootstrap.yml │ │ ├── application.yml │ │ └── schema.sql │ │ └── java │ │ └── com │ │ └── thoughtmechanix │ │ └── licenses │ │ ├── config │ │ └── ServiceConfig.java │ │ ├── repository │ │ └── LicenseRepository.java │ │ ├── controllers │ │ ├── ToolsController.java │ │ └── LicenseServiceController.java │ │ ├── Application.java │ │ ├── utils │ │ ├── UserContextHolder.java │ │ ├── UserContextInterceptor.java │ │ ├── UserContext.java │ │ └── UserContextFilter.java │ │ ├── clients │ │ └── OrganizationRestTemplateClient.java │ │ ├── model │ │ ├── Organization.java │ │ └── License.java │ │ ├── hystrix │ │ ├── DelegatingUserContextCallable.java │ │ ├── ThreadLocalConfiguration.java │ │ └── ThreadLocalAwareStrategy.java │ │ └── services │ │ ├── DiscoveryService.java │ │ └── LicenseService.java └── pom.xml ├── .gitignore ├── pom.xml ├── docker └── common │ └── docker-compose.yml ├── .travis.yml └── README.md /confsvr/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: configserver 4 | 5 | -------------------------------------------------------------------------------- /confsvr/src/main/resources/config/organizationservice/organizationservice.yml: -------------------------------------------------------------------------------- 1 | example.organization.property: "I AM THE DEFAULT ORGANIZATION SERVICE" 2 | -------------------------------------------------------------------------------- /organization-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: organizationservice 4 | profiles: 5 | active: 6 | default 7 | cloud: 8 | config: 9 | enabled: true 10 | -------------------------------------------------------------------------------- /eurekasvr/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | RUN apk update && apk upgrade && apk add netcat-openbsd 3 | RUN mkdir -p /usr/local/eurekaserver 4 | ADD @project.build.finalName@.jar /usr/local/eurekaserver/ 5 | ADD run.sh run.sh 6 | RUN chmod +x run.sh 7 | CMD ./run.sh 8 | -------------------------------------------------------------------------------- /licensing-service/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | RUN apk update && apk upgrade && apk add netcat-openbsd 3 | RUN mkdir -p /usr/local/licensingservice 4 | ADD @project.build.finalName@.jar /usr/local/licensingservice/ 5 | ADD run.sh run.sh 6 | RUN chmod +x run.sh 7 | CMD ./run.sh 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Java class files 2 | *.class 3 | 4 | # Eclipse project files 5 | .classpath 6 | .project 7 | .settings/ 8 | 9 | # Intellij project files 10 | *.iml 11 | *.ipr 12 | *.iws 13 | .idea/ 14 | 15 | .DS_Store/ 16 | /target 17 | 18 | /target 19 | /target/ 20 | **/target/ 21 | 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /licensing-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: licensingservice 4 | profiles: 5 | active: 6 | default 7 | cloud: 8 | config: 9 | enabled: true 10 | # discovery: 11 | # enabled: true 12 | # serviceId: configserver 13 | -------------------------------------------------------------------------------- /eurekasvr/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #Default port is 8761 2 | server: 3 | port: 8761 4 | 5 | eureka: 6 | client: 7 | registerWithEureka: false 8 | fetchRegistry: false 9 | server: 10 | waitTimeInMsWhenSyncEmpty: 0 11 | serviceUrl: 12 | defaultZone: http://localhost:8761 13 | -------------------------------------------------------------------------------- /organization-service/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | RUN apk update && apk upgrade && apk add netcat-openbsd 3 | RUN mkdir -p /usr/local/organizationservice 4 | ADD @project.build.finalName@.jar /usr/local/organizationservice/ 5 | ADD run.sh run.sh 6 | RUN chmod +x run.sh 7 | CMD ./run.sh 8 | -------------------------------------------------------------------------------- /eurekasvr/src/main/docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "********************************************************" 3 | echo "Starting the Eureka Server" 4 | echo "********************************************************" 5 | java -Djava.security.egd=file:/dev/./urandom -jar /usr/local/eurekaserver/@project.build.finalName@.jar 6 | -------------------------------------------------------------------------------- /licensing-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | instance: 3 | preferIpAddress: true 4 | client: 5 | registerWithEureka: true 6 | fetchRegistry: true 7 | serviceUrl: 8 | defaultZone: http://localhost:8761/eureka/ 9 | 10 | #Setting the logging levels for the service 11 | logging: 12 | level: 13 | com.netflix: WARN 14 | org.springframework.web: WARN 15 | com.thoughtmechanix: DEBUG 16 | -------------------------------------------------------------------------------- /organization-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | instance: 3 | preferIpAddress: true 4 | client: 5 | registerWithEureka: true 6 | fetchRegistry: true 7 | serviceUrl: 8 | defaultZone: http://localhost:8761/eureka/ 9 | 10 | #Setting the logging levels for the service 11 | logging: 12 | level: 13 | com.netflix: WARN 14 | org.springframework.web: WARN 15 | com.thoughtmechanix: DEBUG 16 | 17 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/config/ServiceConfig.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | public class ServiceConfig{ 8 | 9 | @Value("${example.property}") 10 | private String exampleProperty=""; 11 | 12 | public String getExampleProperty(){ 13 | return exampleProperty; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/repository/OrganizationRepository.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.repository; 2 | 3 | import com.thoughtmechanix.organization.model.Organization; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface OrganizationRepository extends CrudRepository { 11 | public Organization findById(String organizationId); 12 | } 13 | -------------------------------------------------------------------------------- /eurekasvr/src/main/java/com/thoughtmechanix/eurekasvr/EurekaServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.eurekasvr; 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 EurekaServerApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(EurekaServerApplication.class, args); 12 | } 13 | } -------------------------------------------------------------------------------- /confsvr/src/main/resources/config/licensingservice/licensingservice-dev.yml: -------------------------------------------------------------------------------- 1 | spring.jpa.database: "POSTGRESQL" 2 | spring.datasource.platform: "postgres" 3 | spring.jpa.show-sql: "true" 4 | spring.database.driverClassName: "org.postgresql.Driver" 5 | spring.datasource.url: "jdbc:postgresql://192.168.99.100:5432/eagle_eye_dev" 6 | spring.datasource.username: "postgres_dev" 7 | spring.datasource.password: "p0stgr@s_dev" 8 | spring.datasource.testWhileIdle: "true" 9 | spring.datasource.validationQuery: "SELECT 1" 10 | spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.PostgreSQLDialect" 11 | -------------------------------------------------------------------------------- /confsvr/src/main/resources/config/licensingservice/licensingservice.yml: -------------------------------------------------------------------------------- 1 | example.property: "I AM THE DEFAULT" 2 | spring.jpa.database: "POSTGRESQL" 3 | spring.datasource.platform: "postgres" 4 | spring.jpa.show-sql: "true" 5 | spring.database.driverClassName: "org.postgresql.Driver" 6 | spring.datasource.url: "jdbc:postgresql://192.168.99.100:5432/eagle_eye_local" 7 | spring.datasource.username: "postgres" 8 | spring.datasource.password: "p0stgr@s" 9 | spring.datasource.testWhileIdle: "true" 10 | spring.datasource.validationQuery: "SELECT 1" 11 | spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.PostgreSQLDialect" -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/repository/LicenseRepository.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.repository; 2 | 3 | import com.thoughtmechanix.licenses.model.License; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface LicenseRepository extends CrudRepository { 11 | public List findByOrganizationId(String organizationId); 12 | public License findByOrganizationIdAndLicenseId(String organizationId,String licenseId); 13 | } 14 | -------------------------------------------------------------------------------- /confsvr/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | RUN apk update && apk upgrade && apk add netcat-openbsd && apk add curl 3 | RUN mkdir -p /usr/local/configserver 4 | RUN cd /tmp/ && \ 5 | curl -k -LO "http://download.oracle.com/otn-pub/java/jce/8/jce_policy-8.zip" -H 'Cookie: oraclelicense=accept-securebackup-cookie' && \ 6 | unzip jce_policy-8.zip && \ 7 | rm jce_policy-8.zip && \ 8 | yes |cp -v /tmp/UnlimitedJCEPolicyJDK8/*.jar /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/ 9 | ADD @project.build.finalName@.jar /usr/local/configserver/ 10 | ADD run.sh run.sh 11 | RUN chmod +x run.sh 12 | CMD ./run.sh 13 | -------------------------------------------------------------------------------- /confsvr/src/main/java/com/thoughtmechanix/confsvr/ConfigServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.confsvr; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.config.server.EnableConfigServer; 6 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 | 8 | @SpringBootApplication 9 | @EnableEurekaClient 10 | @EnableConfigServer 11 | public class ConfigServerApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ConfigServerApplication.class, args); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /confsvr/src/main/resources/config/licensingservice/licensingservice-prod.yml: -------------------------------------------------------------------------------- 1 | example.property: "I AM A PROD PROPERTY OVERRIDE" 2 | spring.jpa.database: "POSTGRESQL" 3 | spring.datasource.platform: "postgres" 4 | spring.jpa.show-sql: "true" 5 | spring.database.driverClassName: "org.postgresql.Driver" 6 | spring.datasource.url: "jdbc:postgresql://192.168.99.100:5432/eagle_eye_prod" 7 | spring.datasource.username: "postgres_prod" 8 | spring.datasource.password: "p0stgr@s_prod" 9 | spring.datasource.testWhileIdle: "true" 10 | spring.datasource.validationQuery: "SELECT 1" 11 | spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.PostgreSQLDialect" -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/Application.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 6 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 | 8 | @SpringBootApplication 9 | @EnableEurekaClient 10 | @EnableCircuitBreaker 11 | public class Application { 12 | public static void main(String[] args) { 13 | SpringApplication.run(Application.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /confsvr/src/main/docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "********************************************************" 4 | echo "Waiting for the eureka server to start on port $EUREKASERVER_PORT" 5 | echo "********************************************************" 6 | while ! `nc -z eurekaserver $EUREKASERVER_PORT`; do sleep 3; done 7 | echo ">>>>>>>>>>>> Eureka Server has started" 8 | 9 | echo "********************************************************" 10 | echo "Starting Configuration Service with Eureka Endpoint: $EUREKASERVER_URI"; 11 | echo "********************************************************" 12 | java -Djava.security.egd=file:/dev/./urandom -Deureka.client.serviceUrl.defaultZone=$EUREKASERVER_URI -jar /usr/local/configserver/@project.build.finalName@.jar 13 | -------------------------------------------------------------------------------- /organization-service/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS organizations; 2 | 3 | CREATE TABLE organizations ( 4 | organization_id VARCHAR(100) PRIMARY KEY NOT NULL, 5 | name TEXT NOT NULL, 6 | contact_name TEXT NOT NULL, 7 | contact_email TEXT NOT NULL, 8 | contact_phone TEXT NOT NULL); 9 | 10 | 11 | INSERT INTO organizations (organization_id, name, contact_name, contact_email, contact_phone) 12 | VALUES ('e254f8c-c442-4ebe-a82a-e2fc1d1ff78a', 'customer-crm-co', 'Mark Balster', 'mark.balster@custcrmco.com', '823-555-1212'); 13 | 14 | INSERT INTO organizations (organization_id, name, contact_name, contact_email, contact_phone) 15 | VALUES ('442adb6e-fa58-47f3-9ca2-ed1fecdfe86c', 'HR-PowerSuite', 'Doug Drewry','doug.drewry@hr.com', '920-555-1212'); 16 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/controllers/ToolsController.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.controllers; 2 | 3 | import com.thoughtmechanix.licenses.services.DiscoveryService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import java.util.List; 10 | 11 | @RestController 12 | @RequestMapping(value="v1/tools") 13 | public class ToolsController { 14 | @Autowired 15 | private DiscoveryService discoveryService; 16 | 17 | @RequestMapping(value="/eureka/services",method = RequestMethod.GET) 18 | public List getEurekaServices() { 19 | 20 | return discoveryService.getEurekaServices(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/Application.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 6 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 7 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.web.client.RestTemplate; 10 | 11 | @SpringBootApplication 12 | @EnableEurekaClient 13 | @EnableCircuitBreaker 14 | public class Application { 15 | @LoadBalanced 16 | @Bean 17 | public RestTemplate restTemplate() { 18 | return new RestTemplate(); 19 | } 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(Application.class, args); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/utils/UserContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.utils; 2 | 3 | 4 | import org.springframework.util.Assert; 5 | 6 | public class UserContextHolder { 7 | private static final ThreadLocal userContext = new ThreadLocal(); 8 | 9 | public static final UserContext getContext(){ 10 | UserContext context = userContext.get(); 11 | 12 | if (context == null) { 13 | context = createEmptyContext(); 14 | userContext.set(context); 15 | 16 | } 17 | return userContext.get(); 18 | } 19 | 20 | public static final void setContext(UserContext context) { 21 | Assert.notNull(context, "Only non-null UserContext instances are permitted"); 22 | userContext.set(context); 23 | } 24 | 25 | public static final UserContext createEmptyContext(){ 26 | return new UserContext(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/utils/UserContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.utils; 2 | 3 | 4 | import org.springframework.util.Assert; 5 | 6 | public class UserContextHolder { 7 | private static final ThreadLocal userContext = new ThreadLocal(); 8 | 9 | public static final UserContext getContext(){ 10 | UserContext context = userContext.get(); 11 | 12 | if (context == null) { 13 | context = createEmptyContext(); 14 | userContext.set(context); 15 | 16 | } 17 | return userContext.get(); 18 | } 19 | 20 | public static final void setContext(UserContext context) { 21 | Assert.notNull(context, "Only non-null UserContext instances are permitted"); 22 | userContext.set(context); 23 | } 24 | 25 | public static final UserContext createEmptyContext(){ 26 | return new UserContext(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/clients/OrganizationRestTemplateClient.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.clients; 2 | 3 | import com.thoughtmechanix.licenses.model.Organization; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.http.HttpMethod; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.client.RestTemplate; 9 | 10 | @Component 11 | public class OrganizationRestTemplateClient { 12 | @Autowired 13 | RestTemplate restTemplate; 14 | 15 | public Organization getOrganization(String organizationId){ 16 | ResponseEntity restExchange = 17 | restTemplate.exchange( 18 | "http://organizationservice/v1/organizations/{organizationId}", 19 | HttpMethod.GET, 20 | null, Organization.class, organizationId); 21 | 22 | return restExchange.getBody(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/services/OrganizationService.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.services; 2 | 3 | import com.thoughtmechanix.organization.model.Organization; 4 | import com.thoughtmechanix.organization.repository.OrganizationRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.UUID; 9 | 10 | @Service 11 | public class OrganizationService { 12 | @Autowired 13 | private OrganizationRepository orgRepository; 14 | 15 | public Organization getOrg(String organizationId) { 16 | return orgRepository.findById(organizationId); 17 | } 18 | 19 | public void saveOrg(Organization org){ 20 | org.setId( UUID.randomUUID().toString()); 21 | 22 | orgRepository.save(org); 23 | 24 | } 25 | 26 | public void updateOrg(Organization org){ 27 | orgRepository.save(org); 28 | } 29 | 30 | public void deleteOrg(Organization org){ 31 | orgRepository.delete( org.getId()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/utils/UserContextInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.http.HttpHeaders; 6 | import org.springframework.http.HttpRequest; 7 | import org.springframework.http.client.ClientHttpRequestExecution; 8 | import org.springframework.http.client.ClientHttpRequestInterceptor; 9 | import org.springframework.http.client.ClientHttpResponse; 10 | 11 | import java.io.IOException; 12 | 13 | public class UserContextInterceptor implements ClientHttpRequestInterceptor { 14 | private static final Logger logger = LoggerFactory.getLogger(UserContextInterceptor.class); 15 | @Override 16 | public ClientHttpResponse intercept( 17 | HttpRequest request, byte[] body, ClientHttpRequestExecution execution) 18 | throws IOException { 19 | 20 | HttpHeaders headers = request.getHeaders(); 21 | headers.add(UserContext.CORRELATION_ID, UserContextHolder.getContext().getCorrelationId()); 22 | headers.add(UserContext.AUTH_TOKEN, UserContextHolder.getContext().getAuthToken()); 23 | 24 | return execution.execute(request, body); 25 | } 26 | } -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/utils/UserContextInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.http.HttpHeaders; 6 | import org.springframework.http.HttpRequest; 7 | import org.springframework.http.client.ClientHttpRequestExecution; 8 | import org.springframework.http.client.ClientHttpRequestInterceptor; 9 | import org.springframework.http.client.ClientHttpResponse; 10 | 11 | import java.io.IOException; 12 | 13 | public class UserContextInterceptor implements ClientHttpRequestInterceptor { 14 | private static final Logger logger = LoggerFactory.getLogger(UserContextInterceptor.class); 15 | 16 | @Override 17 | public ClientHttpResponse intercept( 18 | HttpRequest request, byte[] body, ClientHttpRequestExecution execution) 19 | throws IOException { 20 | 21 | HttpHeaders headers = request.getHeaders(); 22 | headers.add(UserContext.CORRELATION_ID, UserContextHolder.getContext().getCorrelationId()); 23 | headers.add(UserContext.AUTH_TOKEN, UserContextHolder.getContext().getAuthToken()); 24 | 25 | return execution.execute(request, body); 26 | } 27 | } -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/model/Organization.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.model; 2 | 3 | public class Organization { 4 | String id; 5 | String name; 6 | String contactName; 7 | String contactEmail; 8 | String contactPhone; 9 | 10 | 11 | public String getId() { 12 | return id; 13 | } 14 | 15 | 16 | public void setId(String id) { 17 | this.id = id; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | public String getContactName() { 29 | return contactName; 30 | } 31 | 32 | public void setContactName(String contactName) { 33 | this.contactName = contactName; 34 | } 35 | 36 | public String getContactEmail() { 37 | return contactEmail; 38 | } 39 | 40 | public void setContactEmail(String contactEmail) { 41 | this.contactEmail = contactEmail; 42 | } 43 | 44 | public String getContactPhone() { 45 | return contactPhone; 46 | } 47 | 48 | public void setContactPhone(String contactPhone) { 49 | this.contactPhone = contactPhone; 50 | } 51 | 52 | 53 | } -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/hystrix/DelegatingUserContextCallable.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.hystrix; 2 | 3 | import com.thoughtmechanix.licenses.utils.UserContext; 4 | import com.thoughtmechanix.licenses.utils.UserContextHolder; 5 | import java.util.concurrent.Callable; 6 | 7 | 8 | public final class DelegatingUserContextCallable implements Callable { 9 | private final Callable delegate; 10 | private UserContext originalUserContext; 11 | 12 | public DelegatingUserContextCallable(Callable delegate, 13 | UserContext userContext) { 14 | this.delegate = delegate; 15 | this.originalUserContext = userContext; 16 | } 17 | 18 | public V call() throws Exception { 19 | UserContextHolder.setContext( originalUserContext ); 20 | 21 | try { 22 | return delegate.call(); 23 | } 24 | finally { 25 | this.originalUserContext = null; 26 | } 27 | } 28 | 29 | public static Callable create(Callable delegate, 30 | UserContext userContext) { 31 | return new DelegatingUserContextCallable(delegate, userContext); 32 | } 33 | } -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/services/DiscoveryService.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.services; 2 | 3 | 4 | import com.thoughtmechanix.licenses.model.Organization; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.cloud.client.ServiceInstance; 7 | import org.springframework.cloud.client.discovery.DiscoveryClient; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Random; 16 | 17 | @Service 18 | public class DiscoveryService { 19 | @Autowired 20 | RestTemplate restTemplate; 21 | 22 | @Autowired 23 | private DiscoveryClient discoveryClient; 24 | 25 | public List getEurekaServices(){ 26 | List services = new ArrayList(); 27 | 28 | discoveryClient.getServices().forEach(serviceName -> { 29 | discoveryClient.getInstances(serviceName).forEach(instance->{ 30 | services.add( String.format("%s:%s",serviceName,instance.getUri())); 31 | }); 32 | }); 33 | 34 | return services; 35 | } 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /licensing-service/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS licenses; 2 | 3 | CREATE TABLE licenses ( 4 | license_id VARCHAR(100) PRIMARY KEY NOT NULL, 5 | organization_id TEXT NOT NULL, 6 | license_type TEXT NOT NULL, 7 | product_name TEXT NOT NULL, 8 | license_max INT NOT NULL, 9 | license_allocated INT, 10 | comment VARCHAR(100)); 11 | 12 | 13 | INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated) 14 | VALUES ('f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a', 'e254f8c-c442-4ebe-a82a-e2fc1d1ff78a', 'user','CustomerPro', 100,5); 15 | INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated) 16 | VALUES ('t9876f8c-c338-4abc-zf6a-ttt1', 'e254f8c-c442-4ebe-a82a-e2fc1d1ff78a', 'user','suitability-plus', 200,189); 17 | INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated) 18 | VALUES ('38777179-7094-4200-9d61-edb101c6ea84', '442adb6e-fa58-47f3-9ca2-ed1fecdfe86c', 'user','HR-PowerSuite', 100,4); 19 | INSERT INTO licenses (license_id, organization_id, license_type, product_name, license_max, license_allocated) 20 | VALUES ('08dbe05-606e-4dad-9d33-90ef10e334f9', '442adb6e-fa58-47f3-9ca2-ed1fecdfe86c', 'core-prod','WildCat Application Gateway', 16,16); -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.thoughtmechanix 6 | 0.0.1-SNAPSHOT 7 | tmx-parent-pom 8 | pom 9 | 10 | 11 | thoughtmechanix-parent-pom 12 | Parent Pom for the thoughtmechanix project 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | confsvr 21 | licensing-service 22 | organization-service 23 | eurekasvr 24 | 25 | 26 | 27 | 28 | com.spotify 29 | docker-maven-plugin 30 | 0.4.10 31 | 32 | java 33 | example 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/utils/UserContext.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.utils; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class UserContext { 7 | public static final String CORRELATION_ID = "tmx-correlation-id"; 8 | public static final String AUTH_TOKEN = "tmx-auth-token"; 9 | public static final String USER_ID = "tmx-user-id"; 10 | public static final String ORG_ID = "tmx-org-id"; 11 | 12 | private String correlationId= new String(); 13 | private String authToken= new String(); 14 | private String userId = new String(); 15 | private String orgId = new String(); 16 | 17 | public String getCorrelationId() { return correlationId;} 18 | public void setCorrelationId(String correlationId) { 19 | this.correlationId = correlationId; 20 | } 21 | 22 | public String getAuthToken() { 23 | return authToken; 24 | } 25 | 26 | public void setAuthToken(String authToken) { 27 | this.authToken = authToken; 28 | } 29 | 30 | public String getUserId() { 31 | return userId; 32 | } 33 | 34 | public void setUserId(String userId) { 35 | this.userId = userId; 36 | } 37 | 38 | public String getOrgId() { 39 | return orgId; 40 | } 41 | 42 | public void setOrgId(String orgId) { 43 | this.orgId = orgId; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/utils/UserContext.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.utils; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class UserContext { 7 | public static final String CORRELATION_ID = "tmx-correlation-id"; 8 | public static final String AUTH_TOKEN = "tmx-auth-token"; 9 | public static final String USER_ID = "tmx-user-id"; 10 | public static final String ORG_ID = "tmx-org-id"; 11 | 12 | private String correlationId= new String(); 13 | private String authToken= new String(); 14 | private String userId = new String(); 15 | private String orgId = new String(); 16 | 17 | public String getCorrelationId() { return correlationId;} 18 | public void setCorrelationId(String correlationId) { 19 | this.correlationId = correlationId; 20 | } 21 | 22 | public String getAuthToken() { 23 | return authToken; 24 | } 25 | 26 | public void setAuthToken(String authToken) { 27 | this.authToken = authToken; 28 | } 29 | 30 | public String getUserId() { 31 | return userId; 32 | } 33 | 34 | public void setUserId(String userId) { 35 | this.userId = userId; 36 | } 37 | 38 | public String getOrgId() { 39 | return orgId; 40 | } 41 | 42 | public void setOrgId(String orgId) { 43 | this.orgId = orgId; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /confsvr/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This is the core configuration for service. I have two examples here: 3 | # 4 | # - A configuration service which completelty loads the configuration for the services 5 | # from the local file server of the config service. This should only be used for 6 | # demonstration purposes or for a small application with a limited number of services. 7 | # 8 | # - A configuraton service that uses a git-based repository to read the files from 9 | # 10 | 11 | 12 | #################################### 13 | server: 14 | port: 8888 15 | spring: 16 | cloud: 17 | config: 18 | discovery: 19 | enabled: true 20 | server: 21 | encrypt.enabled: false 22 | git: 23 | uri: https://github.com/carnellj/config-repo/ 24 | searchPaths: licensingservice,organizationservice 25 | username: native-cloud-apps 26 | password: 0ffended 27 | 28 | 29 | #### 30 | #Classpath and file-based solution 31 | #### 32 | 33 | #server: 34 | # port: 8888 35 | #spring: 36 | # profiles: 37 | # active: native 38 | # cloud: 39 | # config: 40 | # server: 41 | # native: 42 | # searchLocations: file:///confsvr/src/main/resources/config/licensingservice, 43 | # file://confsvr/src/main/resources/config/organizationservice 44 | ## #searchLocations: classpath:config/,classpath:config/licensingservice 45 | -------------------------------------------------------------------------------- /licensing-service/src/main/docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "********************************************************" 4 | echo "Waiting for the eureka server to start on port $EUREKASERVER_PORT" 5 | echo "********************************************************" 6 | while ! `nc -z eurekaserver $EUREKASERVER_PORT`; do sleep 3; done 7 | echo "******* Eureka Server has started" 8 | 9 | 10 | echo "********************************************************" 11 | echo "Waiting for the database server to start on port $DATABASESERVER_PORT" 12 | echo "********************************************************" 13 | while ! `nc -z database $DATABASESERVER_PORT`; do sleep 3; done 14 | echo "******** Database Server has started " 15 | 16 | echo "********************************************************" 17 | echo "Waiting for the configuration server to start on port $CONFIGSERVER_PORT" 18 | echo "********************************************************" 19 | while ! `nc -z configserver $CONFIGSERVER_PORT`; do sleep 3; done 20 | echo "******* Configuration Server has started" 21 | 22 | 23 | echo "********************************************************" 24 | echo "Starting License Server with Configuration Service via Eureka : $EUREKASERVER_URI" ON PORT: $SERVER_PORT; 25 | echo "********************************************************" 26 | java -Djava.security.egd=file:/dev/./urandom -Dserver.port=$SERVER_PORT \ 27 | -Deureka.client.serviceUrl.defaultZone=$EUREKASERVER_URI \ 28 | -Dspring.cloud.config.uri=$CONFIGSERVER_URI \ 29 | -Dspring.profiles.active=$PROFILE -jar /usr/local/licensingservice/@project.build.finalName@.jar 30 | -------------------------------------------------------------------------------- /organization-service/src/main/docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | getPort() { 4 | echo $1 | cut -d : -f 3 | xargs basename 5 | } 6 | 7 | echo "********************************************************" 8 | echo "Waiting for the eureka server to start on port $EUREKASERVER_PORT" 9 | echo "********************************************************" 10 | while ! `nc -z eurekaserver $EUREKASERVER_PORT`; do sleep 3; done 11 | echo "******* Eureka Server has started" 12 | 13 | 14 | echo "********************************************************" 15 | echo "Waiting for the database server to start on port $DATABASESERVER_PORT" 16 | echo "********************************************************" 17 | while ! `nc -z database $DATABASESERVER_PORT`; do sleep 3; done 18 | echo "******** Database Server has started " 19 | 20 | echo "********************************************************" 21 | echo "Waiting for the configuration server to start on port $CONFIGSERVER_PORT" 22 | echo "********************************************************" 23 | while ! `nc -z configserver $CONFIGSERVER_PORT`; do sleep 3; done 24 | echo "******* Configuration Server has started" 25 | 26 | 27 | echo "********************************************************" 28 | echo "Starting Organization Service " 29 | echo "********************************************************" 30 | java -Djava.security.egd=file:/dev/./urandom -Dserver.port=$SERVER_PORT \ 31 | -Deureka.client.serviceUrl.defaultZone=$EUREKASERVER_URI \ 32 | -Dspring.cloud.config.uri=$CONFIGSERVER_URI \ 33 | -Dspring.profiles.active=$PROFILE \ 34 | -jar /usr/local/organizationservice/@project.build.finalName@.jar 35 | -------------------------------------------------------------------------------- /docker/common/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | eurekaserver: 4 | image: johncarnell/tmx-eurekasvr:chapter5 5 | ports: 6 | - "8761:8761" 7 | configserver: 8 | image: johncarnell/tmx-confsvr:chapter5 9 | ports: 10 | - "8888:8888" 11 | environment: 12 | EUREKASERVER_URI: "http://eurekaserver:8761/eureka/" 13 | EUREKASERVER_PORT: "8761" 14 | ENCRYPT_KEY: "IMSYMMETRIC" 15 | database: 16 | image: postgres:9.5 17 | ports: 18 | - "5432:5432" 19 | environment: 20 | - POSTGRES_USER=postgres 21 | - POSTGRES_PASSWORD=p0stgr@s 22 | - POSTGRES_DB=eagle_eye_local 23 | licensingservice: 24 | image: johncarnell/tmx-licensing-service:chapter5 25 | ports: 26 | - "8080:8080" 27 | - "8090:8090" 28 | environment: 29 | PROFILE: "default" 30 | SERVER_PORT: "8080" 31 | CONFIGSERVER_URI: "http://configserver:8888" 32 | EUREKASERVER_URI: "http://eurekaserver:8761/eureka/" 33 | CONFIGSERVER_PORT: "8888" 34 | DATABASESERVER_PORT: "5432" 35 | EUREKASERVER_PORT: "8761" 36 | ENCRYPT_KEY: "IMSYMMETRIC" 37 | organizationservice: 38 | image: johncarnell/tmx-organization-service:chapter5 39 | ports: 40 | - "8085:8085" 41 | environment: 42 | PROFILE: "default" 43 | SERVER_PORT: "8085" 44 | CONFIGSERVER_URI: "http://configserver:8888" 45 | EUREKASERVER_URI: "http://eurekaserver:8761/eureka/" 46 | CONFIGSERVER_PORT: "8888" 47 | DATABASESERVER_PORT: "5432" 48 | EUREKASERVER_PORT: "8761" 49 | ENCRYPT_KEY: "IMSYMMETRIC" 50 | -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/model/Organization.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.model; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | @Entity 9 | @Table(name = "organizations") 10 | public class Organization { 11 | @Id 12 | @Column(name = "organization_id", nullable = false) 13 | String id; 14 | 15 | @Column(name = "name", nullable = false) 16 | String name; 17 | 18 | @Column(name = "contact_name", nullable = false) 19 | String contactName; 20 | 21 | @Column(name = "contact_email", nullable = false) 22 | String contactEmail; 23 | 24 | @Column(name = "contact_phone", nullable = false) 25 | String contactPhone; 26 | 27 | 28 | public String getId() { 29 | return id; 30 | } 31 | 32 | 33 | public void setId(String id) { 34 | this.id = id; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public String getContactName() { 46 | return contactName; 47 | } 48 | 49 | public void setContactName(String contactName) { 50 | this.contactName = contactName; 51 | } 52 | 53 | public String getContactEmail() { 54 | return contactEmail; 55 | } 56 | 57 | public void setContactEmail(String contactEmail) { 58 | this.contactEmail = contactEmail; 59 | } 60 | 61 | public String getContactPhone() { 62 | return contactPhone; 63 | } 64 | 65 | public void setContactPhone(String contactPhone) { 66 | this.contactPhone = contactPhone; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/utils/UserContextFilter.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.Filter; 8 | import javax.servlet.FilterChain; 9 | import javax.servlet.FilterConfig; 10 | import javax.servlet.ServletException; 11 | import javax.servlet.ServletRequest; 12 | import javax.servlet.ServletResponse; 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.io.IOException; 15 | 16 | @Component 17 | public class UserContextFilter implements Filter { 18 | private static final Logger logger = LoggerFactory.getLogger(UserContextFilter.class); 19 | 20 | @Override 21 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 22 | throws IOException, ServletException { 23 | 24 | 25 | HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; 26 | 27 | UserContextHolder.getContext().setCorrelationId( httpServletRequest.getHeader(UserContext.CORRELATION_ID) ); 28 | UserContextHolder.getContext().setUserId(httpServletRequest.getHeader(UserContext.USER_ID)); 29 | UserContextHolder.getContext().setAuthToken(httpServletRequest.getHeader(UserContext.AUTH_TOKEN)); 30 | UserContextHolder.getContext().setOrgId(httpServletRequest.getHeader(UserContext.ORG_ID)); 31 | 32 | logger.debug("UserContextFilter Correlation id: {}", UserContextHolder.getContext().getCorrelationId()); 33 | 34 | filterChain.doFilter(httpServletRequest, servletResponse); 35 | } 36 | 37 | @Override 38 | public void init(FilterConfig filterConfig) throws ServletException {} 39 | 40 | @Override 41 | public void destroy() {} 42 | } -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/utils/UserContextFilter.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.Filter; 8 | import javax.servlet.FilterChain; 9 | import javax.servlet.FilterConfig; 10 | import javax.servlet.ServletException; 11 | import javax.servlet.ServletRequest; 12 | import javax.servlet.ServletResponse; 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.io.IOException; 15 | 16 | @Component 17 | public class UserContextFilter implements Filter { 18 | private static final Logger logger = LoggerFactory.getLogger(UserContextFilter.class); 19 | 20 | @Override 21 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 22 | throws IOException, ServletException { 23 | 24 | 25 | HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; 26 | 27 | UserContextHolder.getContext().setCorrelationId( httpServletRequest.getHeader(UserContext.CORRELATION_ID) ); 28 | UserContextHolder.getContext().setUserId( httpServletRequest.getHeader(UserContext.USER_ID) ); 29 | UserContextHolder.getContext().setAuthToken( httpServletRequest.getHeader(UserContext.AUTH_TOKEN) ); 30 | UserContextHolder.getContext().setOrgId( httpServletRequest.getHeader(UserContext.ORG_ID) ); 31 | 32 | logger.debug("Organization Service Incoming Correlation id: {}" ,UserContextHolder.getContext().getCorrelationId()); 33 | filterChain.doFilter(httpServletRequest, servletResponse); 34 | } 35 | 36 | @Override 37 | public void init(FilterConfig filterConfig) throws ServletException {} 38 | 39 | @Override 40 | public void destroy() {} 41 | } -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/hystrix/DelegatingUserContextCallable.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.hystrix; 2 | 3 | import com.thoughtmechanix.organization.utils.UserContext; 4 | import com.thoughtmechanix.organization.utils.UserContextHolder; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.util.Assert; 8 | 9 | import java.util.concurrent.Callable; 10 | 11 | 12 | public final class DelegatingUserContextCallable implements Callable { 13 | private static final Logger logger = LoggerFactory.getLogger(DelegatingUserContextCallable.class); 14 | private final Callable delegate; 15 | 16 | private UserContext originalUserContext; 17 | 18 | public DelegatingUserContextCallable(Callable delegate, 19 | UserContext userContext) { 20 | Assert.notNull(delegate, "delegate cannot be null"); 21 | Assert.notNull(userContext, "userContext cannot be null"); 22 | this.delegate = delegate; 23 | this.originalUserContext = userContext; 24 | } 25 | 26 | public DelegatingUserContextCallable(Callable delegate) { 27 | this(delegate, UserContextHolder.getContext()); 28 | } 29 | 30 | public V call() throws Exception { 31 | UserContextHolder.setContext( originalUserContext ); 32 | 33 | try { 34 | return delegate.call(); 35 | } 36 | finally { 37 | this.originalUserContext = null; 38 | } 39 | } 40 | 41 | public String toString() { 42 | return delegate.toString(); 43 | } 44 | 45 | 46 | public static Callable create(Callable delegate, 47 | UserContext userContext) { 48 | return new DelegatingUserContextCallable(delegate, userContext); 49 | } 50 | } -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/controllers/OrganizationServiceController.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.controllers; 2 | 3 | 4 | import com.thoughtmechanix.organization.model.Organization; 5 | import com.thoughtmechanix.organization.services.OrganizationService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.ResponseStatus; 14 | 15 | import java.util.List; 16 | 17 | @RestController 18 | @RequestMapping(value="v1/organizations") 19 | public class OrganizationServiceController { 20 | @Autowired 21 | private OrganizationService orgService; 22 | 23 | @RequestMapping(value="/{organizationId}",method = RequestMethod.GET) 24 | public Organization getOrganization( @PathVariable("organizationId") String organizationId) { 25 | return orgService.getOrg(organizationId); 26 | } 27 | 28 | @RequestMapping(value="/{organizationId}",method = RequestMethod.PUT) 29 | public void updateOrganization( @PathVariable("organizationId") String orgId, @RequestBody Organization org) { 30 | orgService.updateOrg( org ); 31 | } 32 | 33 | @RequestMapping(value="/{organizationId}",method = RequestMethod.POST) 34 | public void saveOrganization(@RequestBody Organization org) { 35 | orgService.saveOrg( org ); 36 | } 37 | 38 | @RequestMapping(value="/{organizationId}",method = RequestMethod.DELETE) 39 | @ResponseStatus(HttpStatus.NO_CONTENT) 40 | public void deleteOrganization( @PathVariable("orgId") String orgId, @RequestBody Organization org) { 41 | orgService.deleteOrg( org ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | cache: 3 | directories: 4 | - $HOME/.m2 5 | jdk: 6 | - oraclejdk8 7 | sudo: required 8 | services: 9 | - docker 10 | notifications: 11 | slack: teamcarnell:zhTADgjsb7yJ5RPEd7LPNFwI 12 | email: 13 | - carnell28@gmail.com 14 | on_success: always 15 | on_failure: always 16 | env: 17 | global: 18 | - secure: M7DILVRt6Gp8mbyvio/seyPF05ZfTSskQ/VAaP49TVGiVgZB2sHjzPc8a4MDRBnkKqpyMrg2HB/TaLZ0/0ar2S5jF6NnRWh1DtphwQjtz6cKviyWBT3qyiku2cxzPGIG3/AZirRpEUdXMZYG68CgAMuiy6KQYZK1LzRkpPsyO3NUsBImqAvDsbNw0CrVFLLBjmomZCn1AQWLlh21q8cwM6hHIBSmGbS7Yag0YxgRlxCzTjbNhy1wtBwjK7yf0qcA8cf+dQrvzGNYqletfKxmv0Z3A+NK6p+d6dun/gJTnNT7WgK2teTziObzXeOwvgeEMzjNmVVgEPfrnZ4N/GEb6QPfMdEwoBhCL0fdRego5xlAPkjhMRegvJX0jRcB47jrx0qSRr0RhIrx3PfBxzfyHU6vr3HMGYFfLUEer5P90pMEbRyVVkvHINhWqzIt0Z+f2Tq964/D9XFNaMXjyvvBRaJ1KYHCl0Q6TrbW+Ru2YOUVDdWlGDshdDEBLuk3k6H4nh1e88IahdbrVaV2v4UXumvIrCBK0ES+QV+T6VPE6NAS2Dj/M00Gl99LyEajzmrnrKj+QOU4CvzUbFNUGb7+AT99ICL9LkhTGsdXjATHxVE5HzM3w3b+9Nx/dtNQon8O1VV31NSxL22tKvpcbOHDYLSLMf1W+wBtHbMniB0wV+Y= 19 | - secure: YZDu76GfYNuhgWVwOxWWYjUQR+7H/ljse1fd2F6Hpbr8Za8WtC0My27n2ka5oP2GjVgEHl6u1XCXIoOuH8qYMipT25Ot5OI+5Xj9ANFHVWaedoi+39LePgE/45elk/jhq6oOqMjN2gG5bngwuJ7HhUvfKkFE6GXUzFEB5HoJtiL4OePcVQbq7Xq07Wpe5Sue2duGdbxdsCK8gK5T5HOsRul9u87tybBW4d1sfyFb/9DEY7sW8COwkZZeyIfYVPwO9Woa7tFeJY6NBaOmf0YWyzTOU/cdztH7kXKgAMN+kAHI7E+kQvXorTy1qLfnwqALngczeQYUdj3/1AKsJbMuF7RzW0EhJEVieQzK18aazoEej5AHInf5aaZUWOpPsOXRvCDXaReBg9/6OJYbZRB8371R8IDUosL62ZEhzmAfXFctuBNW7d1aDrgW3VNfBJyQdED5BznmVgKdz7OQVkGeNFKdt3UFpKJVMn59nMMeN7dpzazuW5m4f9tw8fhBYzGHQBpXIT/EeX3yTEOC6GgOSnGlqAkQuCI2pO52f11wqx11nT5G59bEJS9bUnbjZY7DQb69YMUjjJNDgSACyyujTHvaSFqsjH+pq6/DRzQuVe5n8ChGW9ErPaEYRgSU3U0TdMSPXmdHUHLTSwdzfDz5XWg+RJ2tlGyveqdrVe2Vn48= 20 | script: 21 | - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD 22 | - mvn clean package docker:build -U 23 | - docker push johncarnell/tmx-licensing-service:chapter5 24 | - docker push johncarnell/tmx-organization-service:chapter5 25 | - docker push johncarnell/tmx-confsvr:chapter5 26 | - docker push johncarnell/tmx-eurekasvr:chapter5 27 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/hystrix/ThreadLocalConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.hystrix; 2 | 3 | import com.netflix.hystrix.strategy.HystrixPlugins; 4 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; 5 | import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; 6 | import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; 7 | import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; 8 | import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import javax.annotation.PostConstruct; 13 | 14 | @Configuration 15 | public class ThreadLocalConfiguration { 16 | @Autowired(required = false) 17 | private HystrixConcurrencyStrategy existingConcurrencyStrategy; 18 | 19 | @PostConstruct 20 | public void init() { 21 | // Keeps references of existing Hystrix plugins. 22 | HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance() 23 | .getEventNotifier(); 24 | HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance() 25 | .getMetricsPublisher(); 26 | HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance() 27 | .getPropertiesStrategy(); 28 | HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance() 29 | .getCommandExecutionHook(); 30 | 31 | HystrixPlugins.reset(); 32 | 33 | HystrixPlugins.getInstance().registerConcurrencyStrategy(new ThreadLocalAwareStrategy(existingConcurrencyStrategy)); 34 | HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); 35 | HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher); 36 | HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy); 37 | HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/hystrix/ThreadLocalConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.hystrix; 2 | 3 | import com.netflix.hystrix.strategy.HystrixPlugins; 4 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; 5 | import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; 6 | import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; 7 | import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; 8 | import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import javax.annotation.PostConstruct; 13 | 14 | @Configuration 15 | public class ThreadLocalConfiguration { 16 | @Autowired(required = false) 17 | private HystrixConcurrencyStrategy existingConcurrencyStrategy; 18 | 19 | @PostConstruct 20 | public void init() { 21 | // Keeps references of existing Hystrix plugins. 22 | HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance() 23 | .getEventNotifier(); 24 | HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance() 25 | .getMetricsPublisher(); 26 | HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance() 27 | .getPropertiesStrategy(); 28 | HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance() 29 | .getCommandExecutionHook(); 30 | 31 | HystrixPlugins.reset(); 32 | 33 | // Registers existing plugins excepts the Concurrent Strategy plugin. 34 | HystrixPlugins.getInstance().registerConcurrencyStrategy(new ThreadLocalAwareStrategy(existingConcurrencyStrategy)); 35 | HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); 36 | HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher); 37 | HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy); 38 | HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/controllers/LicenseServiceController.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.controllers; 2 | 3 | import com.thoughtmechanix.licenses.model.License; 4 | import com.thoughtmechanix.licenses.services.LicenseService; 5 | import com.thoughtmechanix.licenses.config.ServiceConfig; 6 | import com.thoughtmechanix.licenses.utils.UserContextHolder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import org.springframework.web.bind.annotation.RequestMethod; 15 | import org.springframework.web.bind.annotation.PathVariable; 16 | import org.springframework.web.bind.annotation.ResponseStatus; 17 | 18 | import java.util.List; 19 | 20 | @RestController 21 | @RequestMapping(value="v1/organizations/{organizationId}/licenses") 22 | public class LicenseServiceController { 23 | private static final Logger logger = LoggerFactory.getLogger(LicenseServiceController.class); 24 | @Autowired 25 | private LicenseService licenseService; 26 | 27 | @Autowired 28 | private ServiceConfig serviceConfig; 29 | 30 | @RequestMapping(value="/",method = RequestMethod.GET) 31 | public List getLicenses( @PathVariable("organizationId") String organizationId) { 32 | logger.debug("LicenseServiceController Correlation id: {}", UserContextHolder.getContext().getCorrelationId()); 33 | return licenseService.getLicensesByOrg(organizationId); 34 | } 35 | 36 | @RequestMapping(value="/{licenseId}",method = RequestMethod.GET) 37 | public License getLicenses( @PathVariable("organizationId") String organizationId, 38 | @PathVariable("licenseId") String licenseId) { 39 | 40 | return licenseService.getLicense(organizationId, licenseId); 41 | } 42 | 43 | @RequestMapping(value="{licenseId}",method = RequestMethod.PUT) 44 | public void updateLicenses( @PathVariable("licenseId") String licenseId, @RequestBody License license) { 45 | licenseService.updateLicense(license); 46 | } 47 | 48 | @RequestMapping(value="/",method = RequestMethod.POST) 49 | public void saveLicenses(@RequestBody License license) { 50 | licenseService.saveLicense(license); 51 | } 52 | 53 | @RequestMapping(value="{licenseId}",method = RequestMethod.DELETE) 54 | @ResponseStatus(HttpStatus.NO_CONTENT) 55 | public void deleteLicenses( @PathVariable("licenseId") String licenseId, @RequestBody License license) { 56 | licenseService.deleteLicense(license); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/hystrix/ThreadLocalAwareStrategy.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.hystrix; 2 | 3 | import com.netflix.hystrix.HystrixThreadPoolKey; 4 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; 5 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable; 6 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle; 7 | import com.netflix.hystrix.strategy.properties.HystrixProperty; 8 | import com.thoughtmechanix.licenses.utils.UserContextHolder; 9 | 10 | import java.util.concurrent.BlockingQueue; 11 | import java.util.concurrent.Callable; 12 | import java.util.concurrent.ThreadPoolExecutor; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | 16 | public class ThreadLocalAwareStrategy extends HystrixConcurrencyStrategy{ 17 | private HystrixConcurrencyStrategy existingConcurrencyStrategy; 18 | 19 | public ThreadLocalAwareStrategy( 20 | HystrixConcurrencyStrategy existingConcurrencyStrategy) { 21 | this.existingConcurrencyStrategy = existingConcurrencyStrategy; 22 | } 23 | 24 | @Override 25 | public BlockingQueue getBlockingQueue(int maxQueueSize) { 26 | return existingConcurrencyStrategy != null 27 | ? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize) 28 | : super.getBlockingQueue(maxQueueSize); 29 | } 30 | 31 | @Override 32 | public HystrixRequestVariable getRequestVariable( 33 | HystrixRequestVariableLifecycle rv) { 34 | return existingConcurrencyStrategy != null 35 | ? existingConcurrencyStrategy.getRequestVariable(rv) 36 | : super.getRequestVariable(rv); 37 | } 38 | 39 | @Override 40 | public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, 41 | HystrixProperty corePoolSize, 42 | HystrixProperty maximumPoolSize, 43 | HystrixProperty keepAliveTime, TimeUnit unit, 44 | BlockingQueue workQueue) { 45 | return existingConcurrencyStrategy != null 46 | ? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, 47 | maximumPoolSize, keepAliveTime, unit, workQueue) 48 | : super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, 49 | keepAliveTime, unit, workQueue); 50 | } 51 | 52 | @Override 53 | public Callable wrapCallable(Callable callable) { 54 | return existingConcurrencyStrategy != null 55 | ? existingConcurrencyStrategy 56 | .wrapCallable(new DelegatingUserContextCallable(callable, UserContextHolder.getContext())) 57 | : super.wrapCallable(new DelegatingUserContextCallable(callable, UserContextHolder.getContext())); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /organization-service/src/main/java/com/thoughtmechanix/organization/hystrix/ThreadLocalAwareStrategy.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.organization.hystrix; 2 | 3 | import com.netflix.hystrix.HystrixThreadPoolKey; 4 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; 5 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable; 6 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle; 7 | import com.netflix.hystrix.strategy.properties.HystrixProperty; 8 | import com.thoughtmechanix.organization.utils.UserContextHolder; 9 | 10 | import java.util.concurrent.BlockingQueue; 11 | import java.util.concurrent.Callable; 12 | import java.util.concurrent.ThreadPoolExecutor; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | 16 | public class ThreadLocalAwareStrategy extends HystrixConcurrencyStrategy{ 17 | private HystrixConcurrencyStrategy existingConcurrencyStrategy; 18 | 19 | public ThreadLocalAwareStrategy( 20 | HystrixConcurrencyStrategy existingConcurrencyStrategy) { 21 | this.existingConcurrencyStrategy = existingConcurrencyStrategy; 22 | } 23 | 24 | @Override 25 | public BlockingQueue getBlockingQueue(int maxQueueSize) { 26 | return existingConcurrencyStrategy != null 27 | ? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize) 28 | : super.getBlockingQueue(maxQueueSize); 29 | } 30 | 31 | @Override 32 | public HystrixRequestVariable getRequestVariable( 33 | HystrixRequestVariableLifecycle rv) { 34 | return existingConcurrencyStrategy != null 35 | ? existingConcurrencyStrategy.getRequestVariable(rv) 36 | : super.getRequestVariable(rv); 37 | } 38 | 39 | @Override 40 | public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, 41 | HystrixProperty corePoolSize, 42 | HystrixProperty maximumPoolSize, 43 | HystrixProperty keepAliveTime, TimeUnit unit, 44 | BlockingQueue workQueue) { 45 | return existingConcurrencyStrategy != null 46 | ? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, 47 | maximumPoolSize, keepAliveTime, unit, workQueue) 48 | : super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, 49 | keepAliveTime, unit, workQueue); 50 | } 51 | 52 | @Override 53 | public Callable wrapCallable(Callable callable) { 54 | 55 | return existingConcurrencyStrategy != null 56 | ? existingConcurrencyStrategy 57 | .wrapCallable(new DelegatingUserContextCallable(callable, UserContextHolder.getContext())) 58 | : super.wrapCallable(new DelegatingUserContextCallable(callable, UserContextHolder.getContext())); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | Welcome to Spring Microservices in Action, Chapter 5. Chapter 5 does not introduce any new services. Instead it focuses on how to use Spring Cloud and Netflix's Hystrix project to help protect service clients from failing or poorly behaving services. This chapter will introduce you to the concepts of fail-fast service calls, bulkheads and fallbacks for when a client call fails. 3 | 4 | By the time you are done reading this chapter you will have built and/or deployed: 5 | 6 | 1. A Spring Cloud Config server that is deployed as Docker container and can manage a services configuration information using a file system or GitHub-based repository. 7 | 2. A Eureka server running as a Spring-Cloud based service. This service will allow multiple service instances to register with it. Clients that need to call a service will use Eureka to lookup the physical location of the target service. 8 | 3. A organization service that will manage organization data used within EagleEye. 9 | 4. A licensing service that will manage licensing data used within EagleEye. 10 | 5. A Postgres SQL database used to hold the data for these two services. 11 | 12 | # Software needed 13 | 1. Apache Maven (http://maven.apache.org). I used version 3.3.9 of the Maven. I chose Maven because, while other build tools like Gradle are extremely popular, Maven is still the pre-dominate build tool in use in the Java ecosystem. All of the code examples in this book have been compiled with Java version 1.8. 14 | 2. Docker (http://docker.com). I built the code examples in this book using Docker V1.12 and above. I am taking advantage of the embedded DNS server in Docker that came out in release V1.11. New Docker releases are constantly coming out so it's release version you are using may change on a regular basis. 15 | 3. Git Client (http://git-scm.com). All of the source code for this book is stored in a GitHub repository. For the book, I used version 2.8.4 of the git client. 16 | 17 | # Building the Docker Images for Chapter 5 18 | To build the code examples for Chapter 5 as a docker image, open a command-line window change to the directory where you have downloaded the chapter 5 source code. 19 | 20 | Run the following maven command. This command will execute the [Spotify docker plugin](https://github.com/spotify/docker-maven-plugin) defined in the pom.xml file. 21 | 22 | **mvn clean package docker:build** 23 | 24 | Running the above command at the root of the project directory will build all of the projects. If everything builds successfully you should see a message indicating that the build was successful. 25 | 26 | # Running the services in Chapter 5 27 | 28 | Now we are going to use docker-compose to start the actual image. To start the docker image, 29 | change to the directory containing your chapter 5 source code. Issue the following docker-compose command: 30 | 31 | **docker-compose -f docker/common/docker-compose.yml up** 32 | 33 | If everything starts correctly you should see a bunch of Spring Boot information fly by on standard out. At this point all of the services needed for the chapter code examples will be running. 34 | -------------------------------------------------------------------------------- /eurekasvr/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.thoughtmechanix 6 | eurekasvr 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | Eureka Server 11 | Eureka Server demo project 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 1.3.8.RELEASE 17 | 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-dependencies 23 | Brixton.SR6 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-eureka-server 34 | 35 | 36 | 37 | 38 | 39 | UTF-8 40 | com.thoughtmechanix.eurekasvr.EurekaServerApplication 41 | 1.8 42 | johncarnell/tmx-eurekasvr 43 | chapter5 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | maven-resources-plugin 52 | 53 | 54 | copy-resources 55 | 56 | validate 57 | 58 | copy-resources 59 | 60 | 61 | ${basedir}/target/dockerfile 62 | 63 | 64 | src/main/docker 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | com.spotify 74 | docker-maven-plugin 75 | 0.4.10 76 | 77 | ${docker.image.name}:${docker.image.tag} 78 | ${basedir}/target/dockerfile 79 | 80 | 81 | / 82 | ${project.build.directory} 83 | ${project.build.finalName}.jar 84 | 85 | 86 | 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-maven-plugin 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /confsvr/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.thoughtmechanix 6 | configurationserver 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | Config Server 11 | Config Server demo project 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 1.4.4.RELEASE 17 | 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-dependencies 23 | Camden.SR5 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-config-server 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-config 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-eureka 44 | 45 | 46 | 47 | 48 | 49 | UTF-8 50 | com.thoughtmechanix.confsvr.ConfigServerApplication 51 | 1.8 52 | johncarnell/tmx-confsvr 53 | chapter5 54 | 55 | 56 | 57 | 58 | 59 | 60 | maven-resources-plugin 61 | 62 | 63 | copy-resources 64 | 65 | validate 66 | 67 | copy-resources 68 | 69 | 70 | ${basedir}/target/dockerfile 71 | 72 | 73 | src/main/docker 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | com.spotify 83 | docker-maven-plugin 84 | 0.4.10 85 | 86 | ${docker.image.name}:${docker.image.tag} 87 | ${basedir}/target/dockerfile 88 | 89 | 90 | / 91 | ${project.build.directory} 92 | ${project.build.finalName}.jar 93 | 94 | 95 | 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-maven-plugin 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/services/LicenseService.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.services; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; 5 | import com.thoughtmechanix.licenses.clients.OrganizationRestTemplateClient; 6 | import com.thoughtmechanix.licenses.config.ServiceConfig; 7 | import com.thoughtmechanix.licenses.model.License; 8 | import com.thoughtmechanix.licenses.model.Organization; 9 | import com.thoughtmechanix.licenses.repository.LicenseRepository; 10 | import com.thoughtmechanix.licenses.utils.UserContextHolder; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Service; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Random; 19 | import java.util.UUID; 20 | 21 | @Service 22 | public class LicenseService { 23 | private static final Logger logger = LoggerFactory.getLogger(LicenseService.class); 24 | @Autowired 25 | private LicenseRepository licenseRepository; 26 | 27 | @Autowired 28 | ServiceConfig config; 29 | 30 | @Autowired 31 | OrganizationRestTemplateClient organizationRestClient; 32 | 33 | 34 | public License getLicense(String organizationId,String licenseId) { 35 | License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId); 36 | 37 | Organization org = getOrganization(organizationId); 38 | 39 | return license 40 | .withOrganizationName( org.getName()) 41 | .withContactName( org.getContactName()) 42 | .withContactEmail( org.getContactEmail() ) 43 | .withContactPhone( org.getContactPhone() ) 44 | .withComment(config.getExampleProperty()); 45 | } 46 | 47 | @HystrixCommand 48 | private Organization getOrganization(String organizationId) { 49 | return organizationRestClient.getOrganization(organizationId); 50 | } 51 | 52 | private void randomlyRunLong(){ 53 | Random rand = new Random(); 54 | 55 | int randomNum = rand.nextInt((3 - 1) + 1) + 1; 56 | 57 | if (randomNum==3) sleep(); 58 | } 59 | 60 | private void sleep(){ 61 | try { 62 | Thread.sleep(11000); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | 68 | @HystrixCommand(//fallbackMethod = "buildFallbackLicenseList", 69 | threadPoolKey = "licenseByOrgThreadPool", 70 | threadPoolProperties = 71 | {@HystrixProperty(name = "coreSize",value="30"), 72 | @HystrixProperty(name="maxQueueSize", value="10")}, 73 | commandProperties={ 74 | @HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="10"), 75 | @HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value="75"), 76 | @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="7000"), 77 | @HystrixProperty(name="metrics.rollingStats.timeInMilliseconds", value="15000"), 78 | @HystrixProperty(name="metrics.rollingStats.numBuckets", value="5")} 79 | ) 80 | public List getLicensesByOrg(String organizationId){ 81 | logger.debug("LicenseService.getLicensesByOrg Correlation id: {}", UserContextHolder.getContext().getCorrelationId()); 82 | randomlyRunLong(); 83 | 84 | return licenseRepository.findByOrganizationId(organizationId); 85 | } 86 | 87 | private List buildFallbackLicenseList(String organizationId){ 88 | List fallbackList = new ArrayList<>(); 89 | License license = new License() 90 | .withId("0000000-00-00000") 91 | .withOrganizationId( organizationId ) 92 | .withProductName("Sorry no licensing information currently available"); 93 | 94 | fallbackList.add(license); 95 | return fallbackList; 96 | } 97 | 98 | public void saveLicense(License license){ 99 | license.withId( UUID.randomUUID().toString()); 100 | 101 | licenseRepository.save(license); 102 | } 103 | 104 | public void updateLicense(License license){ 105 | licenseRepository.save(license); 106 | } 107 | 108 | public void deleteLicense(License license){ 109 | licenseRepository.delete( license.getLicenseId()); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /licensing-service/src/main/java/com/thoughtmechanix/licenses/model/License.java: -------------------------------------------------------------------------------- 1 | package com.thoughtmechanix.licenses.model; 2 | 3 | 4 | import javax.persistence.Column; 5 | import javax.persistence.Entity; 6 | import javax.persistence.Id; 7 | import javax.persistence.Table; 8 | import javax.persistence.Transient; 9 | 10 | @Entity 11 | @Table(name = "licenses") 12 | public class License{ 13 | @Id 14 | @Column(name = "license_id", nullable = false) 15 | private String licenseId; 16 | 17 | @Column(name = "organization_id", nullable = false) 18 | private String organizationId; 19 | 20 | @Transient 21 | private String organizationName =""; 22 | 23 | @Transient 24 | private String contactName =""; 25 | 26 | @Transient 27 | private String contactPhone =""; 28 | 29 | @Transient 30 | private String contactEmail =""; 31 | 32 | @Column(name = "product_name", nullable = false) 33 | private String productName; 34 | 35 | @Column(name = "license_type", nullable = false) 36 | private String licenseType; 37 | 38 | @Column(name = "license_max", nullable = false) 39 | private Integer licenseMax; 40 | 41 | @Column(name = "license_allocated", nullable = false) 42 | private Integer licenseAllocated; 43 | 44 | @Column(name="comment") 45 | private String comment; 46 | 47 | 48 | public Integer getLicenseMax() { 49 | return licenseMax; 50 | } 51 | 52 | public void setLicenseMax(Integer licenseMax) { 53 | this.licenseMax = licenseMax; 54 | } 55 | 56 | public Integer getLicenseAllocated() { 57 | return licenseAllocated; 58 | } 59 | 60 | public void setLicenseAllocated(Integer licenseAllocated) { 61 | this.licenseAllocated = licenseAllocated; 62 | } 63 | 64 | 65 | public String getLicenseId() { 66 | return licenseId; 67 | } 68 | 69 | public void setLicenseId(String licenseId) { 70 | this.licenseId = licenseId; 71 | } 72 | 73 | public String getOrganizationId() { 74 | return organizationId; 75 | } 76 | 77 | public void setOrganizationId(String organizationId) { 78 | this.organizationId = organizationId; 79 | } 80 | 81 | public String getProductName() { 82 | return productName; 83 | } 84 | 85 | public void setProductName(String productName) { 86 | this.productName = productName; 87 | } 88 | 89 | public String getLicenseType() { 90 | return licenseType; 91 | } 92 | 93 | public void setLicenseType(String licenseType) { 94 | this.licenseType = licenseType; 95 | } 96 | 97 | public String getComment() { 98 | return comment; 99 | } 100 | 101 | public void setComment(String comment) { 102 | this.comment = comment; 103 | } 104 | 105 | public String getOrganizationName() { 106 | return organizationName; 107 | } 108 | 109 | public void setOrganizationName(String organizationName) { 110 | this.organizationName = organizationName; 111 | } 112 | 113 | public String getContactName() { 114 | return contactName; 115 | } 116 | 117 | public void setContactName(String contactName) { 118 | this.contactName = contactName; 119 | } 120 | 121 | public String getContactPhone() { 122 | return contactPhone; 123 | } 124 | 125 | public void setContactPhone(String contactPhone) { 126 | this.contactPhone = contactPhone; 127 | } 128 | 129 | public String getContactEmail() { 130 | return contactEmail; 131 | } 132 | 133 | public void setContactEmail(String contactEmail) { 134 | this.contactEmail = contactEmail; 135 | } 136 | 137 | public License withId(String id){ 138 | this.setLicenseId(id); 139 | return this; 140 | } 141 | 142 | public License withOrganizationId(String organizationId){ 143 | this.setOrganizationId(organizationId); 144 | return this; 145 | } 146 | 147 | public License withProductName(String productName){ 148 | this.setProductName(productName); 149 | return this; 150 | } 151 | 152 | public License withLicenseType(String licenseType){ 153 | this.setLicenseType(licenseType); 154 | return this; 155 | } 156 | 157 | public License withLicenseMax(Integer licenseMax){ 158 | this.setLicenseMax(licenseMax); 159 | return this; 160 | } 161 | 162 | public License withLicenseAllocated(Integer licenseAllocated){ 163 | this.setLicenseAllocated(licenseAllocated); 164 | return this; 165 | } 166 | 167 | public License withComment(String comment){ 168 | this.setComment(comment); 169 | return this; 170 | } 171 | 172 | public License withOrganizationName(String organizationName){ 173 | this.setOrganizationName(organizationName); 174 | return this; 175 | } 176 | 177 | public License withContactName(String contactName){ 178 | this.setContactName(contactName); 179 | return this; 180 | } 181 | 182 | public License withContactPhone(String contactPhone){ 183 | this.setContactPhone(contactPhone); 184 | return this; 185 | } 186 | 187 | public License withContactEmail(String contactEmail){ 188 | this.setContactEmail(contactEmail); 189 | return this; 190 | } 191 | 192 | 193 | 194 | 195 | } 196 | -------------------------------------------------------------------------------- /organization-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.thoughtmechanix 7 | organization-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Eagle Eye Organization Service 12 | Organization Service 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-dependencies 24 | Camden.SR5 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jpa 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-actuator 43 | 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-config 48 | 49 | 50 | 51 | org.springframework.cloud 52 | spring-cloud-starter-eureka 53 | 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-config-client 58 | 59 | 60 | 61 | org.springframework.cloud 62 | spring-cloud-starter-hystrix 63 | 64 | 65 | 66 | com.h2database 67 | h2 68 | 69 | 70 | 71 | postgresql 72 | postgresql 73 | 9.1-901.jdbc4 74 | 75 | 76 | org.springframework.security 77 | spring-security-rsa 78 | 79 | 80 | com.netflix.hystrix 81 | hystrix-javanica 82 | 1.5.9 83 | 84 | 85 | 86 | 87 | UTF-8 88 | 1.8 89 | UTF-8 90 | com.thoughtmechanix.organization.Application 91 | johncarnell/tmx-organization-service 92 | chapter5 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | maven-resources-plugin 101 | 102 | 103 | copy-resources 104 | 105 | validate 106 | 107 | copy-resources 108 | 109 | 110 | ${basedir}/target/dockerfile 111 | 112 | 113 | src/main/docker 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | com.spotify 123 | docker-maven-plugin 124 | 0.4.10 125 | 126 | ${docker.image.name}:${docker.image.tag} 127 | ${basedir}/target/dockerfile 128 | 129 | 130 | / 131 | ${project.build.directory} 132 | ${project.build.finalName}.jar 133 | 134 | 135 | 136 | 137 | 138 | org.springframework.boot 139 | spring-boot-maven-plugin 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /licensing-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.thoughtmechanix 7 | licensing-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Eagle Eye Licensing Service 12 | Licensing Service 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 1.4.4.RELEASE 17 | 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-dependencies 23 | Camden.SR5 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-jpa 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-web 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-actuator 42 | 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-starter-eureka 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-feign 52 | 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-starter-config 57 | 58 | 59 | 60 | org.springframework.cloud 61 | spring-cloud-config-client 62 | 63 | 64 | 65 | org.springframework.cloud 66 | spring-cloud-starter-hystrix 67 | 68 | 69 | 70 | com.h2database 71 | h2 72 | 73 | 74 | 75 | postgresql 76 | postgresql 77 | 9.1-901.jdbc4 78 | 79 | 80 | org.springframework.security 81 | spring-security-rsa 82 | 83 | 84 | 85 | com.netflix.hystrix 86 | hystrix-javanica 87 | 1.5.9 88 | 89 | 90 | 91 | UTF-8 92 | 1.8 93 | UTF-8 94 | com.thoughtmechanix.licenses.Application 95 | johncarnell/tmx-licensing-service 96 | chapter5 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | maven-resources-plugin 105 | 106 | 107 | copy-resources 108 | 109 | validate 110 | 111 | copy-resources 112 | 113 | 114 | ${basedir}/target/dockerfile 115 | 116 | 117 | src/main/docker 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | com.spotify 127 | docker-maven-plugin 128 | 0.4.10 129 | 130 | ${docker.image.name}:${docker.image.tag} 131 | ${basedir}/target/dockerfile 132 | 133 | 134 | / 135 | ${project.build.directory} 136 | ${project.build.finalName}.jar 137 | 138 | 139 | 140 | 141 | 142 | org.springframework.boot 143 | spring-boot-maven-plugin 144 | 145 | 146 | 147 | 148 | --------------------------------------------------------------------------------