├── microservice-spring-demo ├── postgres │ ├── Dockerfile │ ├── docker-compose.yaml │ └── init-user-db.sh ├── prometheus │ ├── Dockerfile │ └── prometheus.yml ├── load.sh ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── microservice-spring-shipping │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── ewolff │ │ │ │ │ └── microservice │ │ │ │ │ └── shipping │ │ │ │ │ ├── ShipmentService.java │ │ │ │ │ ├── ShipmentRepository.java │ │ │ │ │ ├── web │ │ │ │ │ ├── PollController.java │ │ │ │ │ └── ShippingController.java │ │ │ │ │ ├── ShippingApp.java │ │ │ │ │ ├── Address.java │ │ │ │ │ ├── ShipmentServiceImpl.java │ │ │ │ │ ├── Item.java │ │ │ │ │ ├── poller │ │ │ │ │ ├── OrderFeed.java │ │ │ │ │ ├── OrderFeedEntry.java │ │ │ │ │ └── ShippingPoller.java │ │ │ │ │ ├── ShipmentLine.java │ │ │ │ │ ├── Customer.java │ │ │ │ │ └── Shipment.java │ │ │ └── resources │ │ │ │ ├── application.yml │ │ │ │ ├── templates │ │ │ │ ├── success.html │ │ │ │ ├── layout.html │ │ │ │ ├── shipmentlist.html │ │ │ │ └── shipment.html │ │ │ │ └── application.properties │ │ └── test │ │ │ ├── resources │ │ │ ├── application-test.properties │ │ │ └── logback-spring.xml │ │ │ └── java │ │ │ └── com │ │ │ └── ewolff │ │ │ └── microservice │ │ │ └── shipping │ │ │ ├── ShippingTestApp.java │ │ │ ├── ShippingWebIntegrationTest.java │ │ │ ├── ShippingServiceTest.java │ │ │ └── PollingTest.java │ ├── Dockerfile │ └── pom.xml ├── microservice-spring-invoicing │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── ewolff │ │ │ │ │ └── microservice │ │ │ │ │ └── invoicing │ │ │ │ │ ├── InvoiceService.java │ │ │ │ │ ├── InvoiceRepository.java │ │ │ │ │ ├── web │ │ │ │ │ ├── PollController.java │ │ │ │ │ └── InvoiceController.java │ │ │ │ │ ├── InvoiceApp.java │ │ │ │ │ ├── Address.java │ │ │ │ │ ├── InvoiceServiceImpl.java │ │ │ │ │ ├── poller │ │ │ │ │ ├── OrderFeed.java │ │ │ │ │ ├── OrderFeedEntry.java │ │ │ │ │ └── InvoicePoller.java │ │ │ │ │ ├── InvoiceLine.java │ │ │ │ │ ├── Item.java │ │ │ │ │ ├── Customer.java │ │ │ │ │ └── Invoice.java │ │ │ └── resources │ │ │ │ ├── application.yml │ │ │ │ ├── templates │ │ │ │ ├── success.html │ │ │ │ ├── layout.html │ │ │ │ ├── invoicelist.html │ │ │ │ └── invoice.html │ │ │ │ └── application.properties │ │ └── test │ │ │ ├── resources │ │ │ ├── application-test.properties │ │ │ └── logback-spring.xml │ │ │ └── java │ │ │ └── com │ │ │ └── ewolff │ │ │ └── microservice │ │ │ └── invoicing │ │ │ ├── InvoiceTestApp.java │ │ │ ├── InvoiceWebIntegrationTest.java │ │ │ ├── InvoicingServiceTest.java │ │ │ └── PollingTest.java │ ├── Dockerfile │ └── pom.xml ├── microservice-spring-order │ ├── Dockerfile │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application-test.properties │ │ │ │ ├── logback-spring.xml │ │ │ │ ├── access-filter.json │ │ │ │ └── pacts │ │ │ │ │ ├── Invoice-OrderProvider.json │ │ │ │ │ ├── Shipping-OrderProvider.json │ │ │ │ │ └── #Shipping-OrderProvider.json# │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── ewolff │ │ │ │ └── microservice │ │ │ │ └── order │ │ │ │ ├── OrderTestApp.java │ │ │ │ ├── ItemTestDataGeneratorTest.java │ │ │ │ ├── logic │ │ │ │ ├── OrderServiceTest.java │ │ │ │ └── OrderWebIntegrationTest.java │ │ │ │ ├── CustomerTestDataGeneratorTest.java │ │ │ │ ├── PactTest.java │ │ │ │ ├── OrderTestDataGenerator.java │ │ │ │ └── FeedClientTest.java │ │ └── main │ │ │ ├── resources │ │ │ ├── templates │ │ │ │ ├── success.html │ │ │ │ ├── layout.html │ │ │ │ ├── orderlist.html │ │ │ │ ├── order.html │ │ │ │ ├── order-full.html │ │ │ │ └── orderForm.html │ │ │ └── application.properties │ │ │ └── java │ │ │ └── com │ │ │ └── ewolff │ │ │ └── microservice │ │ │ └── order │ │ │ ├── logic │ │ │ ├── OrderRepository.java │ │ │ ├── OrderService.java │ │ │ ├── OrderLine.java │ │ │ ├── Address.java │ │ │ ├── OrderRestController.java │ │ │ ├── OrderController.java │ │ │ └── Order.java │ │ │ ├── customer │ │ │ ├── CustomerRepository.java │ │ │ ├── CustomerFormatter.java │ │ │ ├── CustomerTestDataGenerator.java │ │ │ └── Customer.java │ │ │ ├── item │ │ │ ├── ItemRepository.java │ │ │ ├── ItemFormatter.java │ │ │ ├── ItemTestDataGenerator.java │ │ │ └── Item.java │ │ │ ├── OrderApp.java │ │ │ ├── RandomlyFailingFilter.java │ │ │ ├── OrderFeed.java │ │ │ └── OrderFeedEntry.java │ └── pom.xml ├── docker-build.sh ├── docker-push.sh ├── service-proxy.sh ├── index.html ├── infrastructure.yaml ├── docker-compose.yaml ├── fail.yaml ├── pom.xml ├── microservices.yaml ├── mvnw.cmd └── mvnw ├── .gitignore ├── .github └── workflows │ └── build-images.yml └── README.md /microservice-spring-demo/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/postgres:14.17 2 | COPY init-user-db.sh /docker-entrypoint-initdb.d/init-user-db.sh 3 | -------------------------------------------------------------------------------- /microservice-spring-demo/prometheus/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/prom/prometheus:v2.47.2 2 | ADD prometheus.yml /etc/prometheus/ 3 | EXPOSE 9090 4 | -------------------------------------------------------------------------------- /microservice-spring-demo/load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in `seq 1 1000`; 3 | do 4 | curl -s -o /dev/null -I -w "%{http_code}" $1 5 | echo 6 | done 7 | -------------------------------------------------------------------------------- /microservice-spring-demo/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewolff/microservice-spring/HEAD/microservice-spring-demo/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /microservice-spring-demo/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/ShipmentService.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | public interface ShipmentService { 4 | 5 | void ship(Shipment shipment); 6 | 7 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/InvoiceService.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | public interface InvoiceService { 4 | 5 | void generateInvoice(Invoice invoice); 6 | 7 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/eclipse-temurin:24-jre-alpine 2 | COPY target/microservice-spring-order-0.0.1-SNAPSHOT.jar . 3 | CMD java -Xmx300m -Xms300m -XX:TieredStopAtLevel=1 -noverify -jar microservice-spring-order-0.0.1-SNAPSHOT.jar 4 | EXPOSE 8081 5 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/eclipse-temurin:24-jre-alpine 2 | COPY target/microservice-spring-shipping-0.0.1-SNAPSHOT.jar . 3 | CMD java -Xmx300m -Xms300m -XX:TieredStopAtLevel=1 -noverify -jar microservice-spring-shipping-0.0.1-SNAPSHOT.jar 4 | EXPOSE 8083 5 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/eclipse-temurin:24-jre-alpine 2 | COPY target/microservice-spring-invoicing-0.0.1-SNAPSHOT.jar . 3 | CMD java -Xmx300m -Xms300m -XX:TieredStopAtLevel=1 -noverify -jar microservice-spring-invoicing-0.0.1-SNAPSHOT.jar 4 | EXPOSE 8082 5 | -------------------------------------------------------------------------------- /microservice-spring-demo/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker build --tag=microservice-spring-postgres:1 postgres 3 | docker build --tag=microservice-spring-shipping:1 microservice-spring-shipping 4 | docker build --tag=microservice-spring-invoicing:1 microservice-spring-invoicing 5 | docker build --tag=microservice-spring-order:1 microservice-spring-order 6 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/resources/application-test.properties: -------------------------------------------------------------------------------- 1 | spring.thymeleaf.cache=false 2 | spring.datasource.url=jdbc:hsqldb:mem:mymemdb 3 | spring.datasource.username=dbuser 4 | spring.datasource.password=dbpass 5 | spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver 6 | baseUrl=http://localhost:8081/order/ -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/test/resources/application-test.properties: -------------------------------------------------------------------------------- 1 | order.url=http://localhost:8081/feed 2 | spring.thymeleaf.cache=false 3 | poller.actived=false 4 | spring.datasource.url=jdbc:hsqldb:mem:mymemdb 5 | spring.datasource.username=dbuser 6 | spring.datasource.password=dbpass 7 | spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/test/resources/application-test.properties: -------------------------------------------------------------------------------- 1 | order.url=http://localhost:8081/feed 2 | spring.thymeleaf.cache=false 3 | poller.actived=false 4 | spring.datasource.url=jdbc:hsqldb:mem:mymemdb 5 | spring.datasource.username=dbuser 6 | spring.datasource.password=dbpass 7 | spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -------------------------------------------------------------------------------- /microservice-spring-demo/postgres/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | db: 5 | image: spring-microservices/postgres 6 | container_name: postgres 7 | ports: 8 | - "5432:5432" 9 | build: 10 | context: . 11 | dockerfile: Dockerfile 12 | environment: 13 | POSTGRES_USER: dbuser 14 | POSTGRES_PASSWORD: dbpass -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vagrant 3 | .dapr 4 | .springBeans 5 | .classpath 6 | .project 7 | .settings 8 | .idea 9 | .metadata 10 | *.iml 11 | target/ 12 | istio-*/ 13 | pom.xml.tag 14 | pom.xml.releaseBackup 15 | pom.xml.versionsBackup 16 | pom.xml.next 17 | release.properties 18 | dependency-reduced-pom.xml 19 | buildNumber.properties 20 | .mvn/timing.properties 21 | .code 22 | .vscode 23 | *~ 24 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/test/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/test/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /microservice-spring-demo/postgres/init-user-db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL 5 | CREATE DATABASE dbshipping; 6 | GRANT ALL PRIVILEGES ON DATABASE dbshipping TO dbuser; 7 | CREATE DATABASE dbinvoicing; 8 | GRANT ALL PRIVILEGES ON DATABASE dbinvoicing TO dbuser; 9 | CREATE DATABASE dborder; 10 | GRANT ALL PRIVILEGES ON DATABASE dborder TO dbuser; 11 | EOSQL 12 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/resources/templates/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Success 6 | 7 | 8 | 9 |

Success

10 |
11 | Action was successful! 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/resources/access-filter.json: -------------------------------------------------------------------------------- 1 | { "rules": [ 2 | {"excludeClasses": "org.apache.maven.surefire.**"}, 3 | {"excludeClasses": "net.bytebuddy.**"}, 4 | {"excludeClasses": "org.apiguardian.**"}, 5 | {"excludeClasses": "org.junit.**"}, 6 | {"excludeClasses": "org.mockito.**"}, 7 | {"excludeClasses": "org.springframework.test.**"}, 8 | {"excludeClasses": "org.springframework.boot.test.**"} 9 | ] 10 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | resilience4j.retry: 2 | configs: 3 | default: 4 | maxAttempts: 3 5 | waitDuration: 100 6 | retryExceptions: 7 | - org.springframework.web.client.HttpServerErrorException 8 | - java.util.concurrent.TimeoutException 9 | - java.io.IOException 10 | instances: 11 | poller: 12 | baseConfig: default -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | resilience4j.retry: 2 | configs: 3 | default: 4 | maxAttempts: 3 5 | waitDuration: 100 6 | retryExceptions: 7 | - org.springframework.web.client.HttpServerErrorException 8 | - java.util.concurrent.TimeoutException 9 | - java.io.IOException 10 | instances: 11 | poller: 12 | baseConfig: default -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/resources/templates/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sucess 6 | 7 | 8 | 9 |

Invoicing

10 |
11 | Polling was successful!
12 | 13 | Home 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/resources/templates/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sucess 6 | 7 | 8 | 9 |

Shipment

10 |
11 | Polling was successful!
12 | 13 | Home 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /microservice-spring-demo/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 5s 3 | 4 | scrape_configs: 5 | - job_name: 'order' 6 | metrics_path: /actuator/prometheus 7 | static_configs: 8 | - targets: ['spring-order:8080'] 9 | - job_name: 'invoicing' 10 | metrics_path: /actuator/prometheus 11 | static_configs: 12 | - targets: ['spring-invoicing:8080'] 13 | - job_name: 'shipping' 14 | metrics_path: /actuator/prometheus 15 | static_configs: 16 | - targets: ['spring-shipping:8080'] 17 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/OrderTestApp.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class OrderTestApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication app = new SpringApplication(OrderTestApp.class); 11 | app.setAdditionalProfiles("test"); 12 | app.run(args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/test/java/com/ewolff/microservice/invoicing/InvoiceTestApp.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvoiceTestApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication app = new SpringApplication(InvoiceTestApp.class); 11 | app.setAdditionalProfiles("test"); 12 | app.run(args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/test/java/com/ewolff/microservice/shipping/ShippingTestApp.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ShippingTestApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication app = new SpringApplication(ShippingTestApp.class); 11 | app.setAdditionalProfiles("test"); 12 | app.run(args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | import org.springframework.data.repository.PagingAndSortingRepository; 8 | 9 | public interface OrderRepository extends PagingAndSortingRepository, CrudRepository { 10 | 11 | @Query("SELECT max(o.updated) FROM Order o") 12 | Date lastUpdate(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/customer/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.customer; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.PagingAndSortingRepository; 7 | import org.springframework.data.repository.query.Param; 8 | 9 | public interface CustomerRepository extends PagingAndSortingRepository, CrudRepository { 10 | 11 | List findByName(@Param("name") String name); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/InvoiceRepository.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | import org.springframework.data.repository.PagingAndSortingRepository; 8 | 9 | public interface InvoiceRepository extends PagingAndSortingRepository, CrudRepository { 10 | 11 | @Query("SELECT max(i.updated) FROM Invoice i") 12 | Date lastUpdate(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/ShipmentRepository.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | import org.springframework.data.repository.PagingAndSortingRepository; 8 | 9 | public interface ShipmentRepository extends PagingAndSortingRepository, CrudRepository { 10 | 11 | @Query("SELECT max(s.updated) FROM Shipment s") 12 | Date lastUpdate(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/build-images.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Images for the Microservices 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build-docker-images: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up JDK 17 17 | uses: actions/setup-java@v1 18 | with: 19 | java-version: 17 20 | - name: Compile microservices with Maven 21 | run: cd microservice-spring-demo && mvn -B package 22 | - name: Create Docker images 23 | run: cd microservice-spring-demo && docker-compose build -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/web/PollController.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing.web; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | 6 | import com.ewolff.microservice.invoicing.poller.InvoicePoller; 7 | 8 | @Controller 9 | public class PollController { 10 | 11 | private InvoicePoller poller; 12 | 13 | public PollController(InvoicePoller poller) { 14 | this.poller = poller; 15 | } 16 | 17 | @PostMapping("/poll") 18 | public String poll() { 19 | poller.poll(); 20 | return "success"; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/web/PollController.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping.web; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | 6 | import com.ewolff.microservice.shipping.poller.ShippingPoller; 7 | 8 | @Controller 9 | public class PollController { 10 | 11 | private ShippingPoller poller; 12 | 13 | public PollController(ShippingPoller poller) { 14 | this.poller = poller; 15 | } 16 | 17 | @PostMapping("/poll") 18 | public String poll() { 19 | poller.poll(); 20 | return "success"; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /microservice-spring-demo/docker-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker tag microservice-spring-postgres:1 ewolff/microservice-spring-postgres:latest 3 | docker tag microservice-spring-apache:1 ewolff/microservice-spring-apache:latest 4 | docker tag microservice-spring-shipping:1 ewolff/microservice-spring-shipping:latest 5 | docker tag microservice-spring-invoicing:1 ewolff/microservice-spring-invoicing:latest 6 | docker tag microservice-spring-order:1 ewolff/microservice-spring-order:latest 7 | docker push ewolff/microservice-spring-apache:latest 8 | docker push ewolff/microservice-spring-postgres:latest 9 | docker push ewolff/microservice-spring-shipping:latest 10 | docker push ewolff/microservice-spring-invoicing:latest 11 | docker push ewolff/microservice-spring-order:latest 12 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/item/ItemRepository.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.item; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.CrudRepository; 7 | import org.springframework.data.repository.PagingAndSortingRepository; 8 | import org.springframework.data.repository.query.Param; 9 | 10 | public interface ItemRepository extends PagingAndSortingRepository, CrudRepository { 11 | 12 | List findByName(@Param("name") String name); 13 | 14 | List findByNameContaining(@Param("name") String name); 15 | 16 | @Query("SELECT price FROM Item i WHERE i.itemId=?1") 17 | double price(long itemId); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /microservice-spring-demo/service-proxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define a function to handle cleanup 4 | cleanup() { 5 | echo "Cleaning up and stopping child processes..." 6 | # Terminate all child processes 7 | kill $(jobs -p) 8 | exit 0 9 | } 10 | 11 | # Register the cleanup function to execute on Ctrl+C (SIGINT) 12 | trap cleanup SIGINT 13 | 14 | # Start your child processes in the background 15 | # Example: Start three background processes 16 | echo Order http://localhost:18081/ 17 | kubectl port-forward service/spring-order 18081:8080 & 18 | echo Shipping http://localhost:18083/ 19 | kubectl port-forward service/spring-shipping 18083:8080 & 20 | echo Invoicing http://localhost:18082/ 21 | kubectl port-forward service/spring-invoicing 18082:8080 & 22 | 23 | # Wait for all background processes to complete 24 | wait 25 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.stereotype.Service; 6 | 7 | @Service 8 | class OrderService { 9 | 10 | private OrderRepository orderRepository; 11 | 12 | public OrderService(OrderRepository orderRepository) { 13 | super(); 14 | this.orderRepository = orderRepository; 15 | } 16 | 17 | public Order order(Order order) { 18 | if (order.getNumberOfLines() == 0) { 19 | throw new IllegalArgumentException("No order lines!"); 20 | } 21 | order.setUpdated(new Date()); 22 | return orderRepository.save(order); 23 | } 24 | 25 | public double getPrice(long orderId) { 26 | return orderRepository.findById(orderId).get().totalPrice(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/InvoiceApp.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.client.RestTemplateBuilder; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.scheduling.annotation.EnableScheduling; 8 | import org.springframework.web.client.RestTemplate; 9 | 10 | @EnableScheduling 11 | @SpringBootApplication 12 | public class InvoiceApp { 13 | 14 | @Bean 15 | public RestTemplate getRestTemplate(RestTemplateBuilder builder) { 16 | return builder.build(); 17 | } 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(InvoiceApp.class, args); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/resources/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Layout 7 | 8 | 9 | 10 | 11 | 12 | 17 |

Layout

18 |
Fake content
19 | 14 | 15 | 16 | 17 |

Order, Shipping, Invoicing Spring

18 |
19 |
20 |
21 | Order 22 |
23 |
Create an order
24 |
25 |
26 |
27 | Shipping 28 |
29 |
Display shipping information
30 |
31 |
32 |
33 | Invoicing 34 |
35 |
Display invoices
36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/OrderApp.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | import com.ewolff.microservice.order.customer.CustomerTestDataGenerator; 7 | import com.ewolff.microservice.order.item.ItemTestDataGenerator; 8 | 9 | import jakarta.annotation.PostConstruct; 10 | 11 | @SpringBootApplication 12 | public class OrderApp { 13 | 14 | private CustomerTestDataGenerator customerTestDataGenerator; 15 | private ItemTestDataGenerator itemTestDataGenerator; 16 | 17 | public OrderApp(CustomerTestDataGenerator customerTestDataGenerator, ItemTestDataGenerator itemTestDataGenerator) { 18 | super(); 19 | this.customerTestDataGenerator = customerTestDataGenerator; 20 | this.itemTestDataGenerator = itemTestDataGenerator; 21 | } 22 | 23 | @PostConstruct 24 | public void generateTestData() { 25 | customerTestDataGenerator.generateTestData(); 26 | itemTestDataGenerator.generateTestData(); 27 | } 28 | 29 | public static void main(String[] args) { 30 | SpringApplication.run(OrderApp.class, args); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/resources/templates/shipmentlist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Shipments : View all 6 | 7 | 8 | 9 |

Shipments : View all

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 |
IDCustomer
No shipments
1Firstname 25 | Name
29 |
30 |
31 | 33 |
34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/web/InvoiceController.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing.web; 2 | 3 | import org.springframework.http.MediaType; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | import com.ewolff.microservice.invoicing.InvoiceRepository; 11 | 12 | @Controller 13 | public class InvoiceController { 14 | 15 | private InvoiceRepository invoiceRepository; 16 | 17 | public InvoiceController(InvoiceRepository invoiceRepository) { 18 | this.invoiceRepository = invoiceRepository; 19 | } 20 | 21 | @GetMapping(value = "/{id}", produces = MediaType.TEXT_HTML_VALUE) 22 | public ModelAndView invoice(@PathVariable long id) { 23 | return new ModelAndView("invoice", "invoice", invoiceRepository.findById(id).get()); 24 | } 25 | 26 | @RequestMapping("/") 27 | public ModelAndView invoiceList() { 28 | return new ModelAndView("invoicelist", "invoices", invoiceRepository.findAll()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/ItemTestDataGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 9 | import org.springframework.test.context.ActiveProfiles; 10 | 11 | import com.ewolff.microservice.order.item.ItemRepository; 12 | import com.ewolff.microservice.order.item.ItemTestDataGenerator; 13 | 14 | @SpringBootTest(classes = OrderApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 15 | @ActiveProfiles("test") 16 | class ItemTestDataGeneratorTest { 17 | 18 | @Autowired 19 | private ItemRepository itemRepository; 20 | 21 | @Autowired 22 | private ItemTestDataGenerator itemTestDataGenerator; 23 | 24 | @Test 25 | void assureTestDataGeneratedOnce() { 26 | assertEquals(1, itemRepository.findByName("iPod").size()); 27 | itemTestDataGenerator.generateTestData(); 28 | assertEquals(1, itemRepository.findByName("iPod").size()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/web/ShippingController.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping.web; 2 | 3 | import org.springframework.http.MediaType; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | import com.ewolff.microservice.shipping.ShipmentRepository; 11 | 12 | @Controller 13 | public class ShippingController { 14 | 15 | private ShipmentRepository shipmentRepository; 16 | 17 | public ShippingController(ShipmentRepository shipmentRepository) { 18 | this.shipmentRepository = shipmentRepository; 19 | } 20 | 21 | @GetMapping(value = "/{id}", produces = MediaType.TEXT_HTML_VALUE) 22 | public ModelAndView shipment(@PathVariable long id) { 23 | return new ModelAndView("shipment", "shipment", shipmentRepository.findById(id).get()); 24 | } 25 | 26 | @RequestMapping("/") 27 | public ModelAndView shipmentList() { 28 | return new ModelAndView("shipmentlist", "shipments", shipmentRepository.findAll()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/logic/OrderServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 9 | import org.springframework.test.context.ActiveProfiles; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import com.ewolff.microservice.order.OrderApp; 13 | 14 | @SpringBootTest(classes = OrderApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 15 | @ActiveProfiles("test") 16 | class OrderServiceTest { 17 | 18 | @Autowired 19 | private OrderRepository orderRepository; 20 | 21 | @Test 22 | @Transactional 23 | void lastCreatedIsUpdated() { 24 | Order order = new Order(); 25 | order = orderRepository.save(order); 26 | assertEquals(order.getUpdated(), orderRepository.lastUpdate()); 27 | order = new Order(); 28 | order = orderRepository.save(order); 29 | assertEquals(order.getUpdated(), orderRepository.lastUpdate()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/CustomerTestDataGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 9 | import org.springframework.test.context.ActiveProfiles; 10 | 11 | import com.ewolff.microservice.order.customer.CustomerRepository; 12 | import com.ewolff.microservice.order.customer.CustomerTestDataGenerator; 13 | 14 | @SpringBootTest(classes = OrderApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 15 | @ActiveProfiles("test") 16 | class CustomerTestDataGeneratorTest { 17 | 18 | @Autowired 19 | private CustomerRepository customerRepository; 20 | 21 | @Autowired 22 | private CustomerTestDataGenerator customerTestDataGenerator; 23 | 24 | @Test 25 | void assureTestDataGeneratedOnce() { 26 | assertEquals(1, customerRepository.findByName("Wolff").size()); 27 | customerTestDataGenerator.generateTestData(); 28 | assertEquals(1, customerRepository.findByName("Wolff").size()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/resources/templates/invoicelist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Invoicing : View all 6 | 7 | 8 | 9 |

Invoicing : View all

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 |
IDCustomerTotal Amount
No invoices
1Firstname 26 | Name42.0
31 |
32 |
33 | 35 |
36 |
37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /microservice-spring-demo/infrastructure.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: postgres-spring 7 | version: "1.0" 8 | name: postgres-spring 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: postgres-spring 14 | strategy: {} 15 | template: 16 | metadata: 17 | creationTimestamp: null 18 | labels: 19 | app: postgres-spring 20 | spec: 21 | containers: 22 | - name: postgres-spring 23 | image: microservice-spring-postgres:1 24 | imagePullPolicy: Never 25 | ports: 26 | - containerPort: 5432 27 | env: 28 | - name: POSTGRES_USER 29 | value: "dbuser" 30 | - name: POSTGRES_PASSWORD 31 | value: "dbpass" 32 | resources: {} 33 | volumeMounts: 34 | - name: postgres-spring 35 | mountPath: /var/lib/postgresql/data 36 | volumes: 37 | - name: postgres-spring 38 | emptyDir: {} 39 | status: {} 40 | 41 | --- 42 | 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | creationTimestamp: null 47 | labels: 48 | app: postgres-spring 49 | name: postgres-spring 50 | spec: 51 | ports: 52 | - port: 5432 53 | protocol: TCP 54 | targetPort: 5432 55 | selector: 56 | app: postgres-spring 57 | type: NodePort 58 | status: 59 | loadBalancer: {} -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/PactTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.TestTemplate; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 8 | import org.springframework.test.context.ActiveProfiles; 9 | 10 | import au.com.dius.pact.provider.junit5.HttpTestTarget; 11 | import au.com.dius.pact.provider.junit5.PactVerificationContext; 12 | import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider; 13 | import au.com.dius.pact.provider.junitsupport.Provider; 14 | import au.com.dius.pact.provider.junitsupport.loader.PactFolder; 15 | 16 | @SpringBootTest(classes = OrderApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 17 | @ActiveProfiles("test") 18 | @Provider("OrderProvider") 19 | @PactFolder("pacts") 20 | class PactTest { 21 | 22 | @BeforeEach 23 | void setupTestTarget(PactVerificationContext context) { 24 | context.setTarget(new HttpTestTarget("localhost", 8080, "/")); 25 | } 26 | 27 | @TestTemplate 28 | @ExtendWith(PactVerificationInvocationContextProvider.class) 29 | void pactVerificationTestTemplate(PactVerificationContext context) { 30 | context.verifyInteraction(); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/resources/templates/orderlist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Order: View all 6 | 7 | 8 | 9 |

Order: View all

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 34 | 35 | 36 |
IDCustomerTotal Price
No orders
1Firstname 27 | Name42.0 30 |
31 | 32 |
33 |
37 |
38 |
39 | Add Order 40 |
41 |
42 | Feed 43 |
44 |
45 |
46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/RandomlyFailingFilter.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import java.io.IOException; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Component; 9 | 10 | import jakarta.servlet.Filter; 11 | import jakarta.servlet.FilterChain; 12 | import jakarta.servlet.FilterConfig; 13 | import jakarta.servlet.ServletException; 14 | import jakarta.servlet.ServletRequest; 15 | import jakarta.servlet.ServletResponse; 16 | import jakarta.servlet.http.HttpServletResponse; 17 | 18 | @Component 19 | public class RandomlyFailingFilter implements Filter { 20 | 21 | private final Logger log = LoggerFactory.getLogger(RandomlyFailingFilter.class); 22 | 23 | @Value("${failrandomly:false}") 24 | private boolean failRandomly = false; 25 | 26 | @Override 27 | public void init(FilterConfig filterConfig) throws ServletException { 28 | } 29 | 30 | @Override 31 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 32 | throws IOException, ServletException { 33 | if ((Math.random() <= 0.5D) || (!failRandomly)) { 34 | chain.doFilter(request, response); 35 | } else { 36 | log.trace("Made HTTP Request fail with 500"); 37 | ((HttpServletResponse) response).sendError(500); 38 | } 39 | } 40 | 41 | @Override 42 | public void destroy() { 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/Item.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import jakarta.persistence.Embeddable; 4 | 5 | @Embeddable 6 | public class Item { 7 | 8 | private Long itemId; 9 | 10 | private String name; 11 | 12 | public Item() { 13 | super(); 14 | itemId = 0l; 15 | } 16 | 17 | public Long getItemId() { 18 | return itemId; 19 | } 20 | 21 | public void setItemId(Long itemId) { 22 | this.itemId = itemId; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | final int prime = 31; 36 | int result = 1; 37 | result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); 38 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 39 | return result; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (this == obj) 45 | return true; 46 | if (obj == null) 47 | return false; 48 | if (getClass() != obj.getClass()) 49 | return false; 50 | Item other = (Item) obj; 51 | if (itemId == null) { 52 | if (other.itemId != null) 53 | return false; 54 | } else if (!itemId.equals(other.itemId)) 55 | return false; 56 | if (name == null) { 57 | if (other.name != null) 58 | return false; 59 | } else if (!name.equals(other.name)) 60 | return false; 61 | return true; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "Item [itemId=" + itemId + ", name=" + name + "]"; 67 | } 68 | 69 | 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /microservice-spring-demo/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | postgres-spring: 5 | image: spring-microservices/postgres 6 | container_name: spring-postgres 7 | ports: 8 | - "5432:5432" 9 | build: 10 | context: ./postgres 11 | dockerfile: Dockerfile 12 | environment: 13 | POSTGRES_USER: dbuser 14 | POSTGRES_PASSWORD: dbpass 15 | spring-order: 16 | image: spring-microservices/order 17 | container_name: spring-order 18 | ports: 19 | - "18081:8080" 20 | links: 21 | - postgres-spring 22 | - zipkin 23 | build: 24 | context: ./microservice-spring-order 25 | dockerfile: Dockerfile 26 | spring-shipping: 27 | image: spring-microservices/shipping 28 | container_name: spring-shipping 29 | ports: 30 | - "18083:8080" 31 | links: 32 | - postgres-spring 33 | - spring-order 34 | - zipkin 35 | build: 36 | context: ./microservice-spring-shipping 37 | dockerfile: Dockerfile 38 | spring-invoicing: 39 | image: spring-microservices/invoicing 40 | container_name: spring-invoicing 41 | ports: 42 | - "18082:8080" 43 | links: 44 | - postgres-spring 45 | - spring-order 46 | - zipkin 47 | build: 48 | context: ./microservice-spring-invoicing 49 | dockerfile: Dockerfile 50 | prometheus: 51 | image: spring-microservices/prometheus 52 | container_name: spring-prometheus 53 | ports: 54 | - "9090:9090" 55 | links: 56 | - spring-order 57 | - spring-invoicing 58 | - spring-shipping 59 | build: 60 | context: ./prometheus 61 | dockerfile: Dockerfile 62 | grafana: 63 | image: 'docker.io/grafana/grafana:10.2.0' 64 | links: 65 | - prometheus 66 | ports: 67 | - '3000:3000' 68 | zipkin: 69 | image: docker.io/openzipkin/zipkin:2.24 70 | ports: 71 | - 9412:9411 72 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/test/java/com/ewolff/microservice/invoicing/InvoiceWebIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.containsString; 5 | 6 | import java.util.Arrays; 7 | 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 11 | import org.springframework.boot.test.web.server.LocalServerPort; 12 | import org.springframework.http.HttpEntity; 13 | import org.springframework.http.HttpHeaders; 14 | import org.springframework.http.HttpMethod; 15 | import org.springframework.http.MediaType; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.test.context.ActiveProfiles; 18 | import org.springframework.web.client.RestTemplate; 19 | 20 | @SpringBootTest(classes = InvoiceTestApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 21 | @ActiveProfiles("test") 22 | class InvoiceWebIntegrationTest { 23 | 24 | @LocalServerPort 25 | private int serverPort; 26 | 27 | private RestTemplate restTemplate = new RestTemplate(); 28 | 29 | @Test 30 | void isHTMLReturned() { 31 | String body = getForMediaType(String.class, MediaType.TEXT_HTML, shippingURL()); 32 | 33 | assertThat(body, containsString(" T getForMediaType(Class value, MediaType mediaType, String url) { 41 | HttpHeaders headers = new HttpHeaders(); 42 | headers.setAccept(Arrays.asList(mediaType)); 43 | 44 | HttpEntity entity = new HttpEntity("parameters", headers); 45 | 46 | ResponseEntity resultEntity = restTemplate.exchange(url, HttpMethod.GET, entity, value); 47 | 48 | return resultEntity.getBody(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/test/java/com/ewolff/microservice/shipping/ShippingWebIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.containsString; 5 | 6 | import java.util.Arrays; 7 | 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 11 | import org.springframework.boot.test.web.server.LocalServerPort; 12 | import org.springframework.http.HttpEntity; 13 | import org.springframework.http.HttpHeaders; 14 | import org.springframework.http.HttpMethod; 15 | import org.springframework.http.MediaType; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.test.context.ActiveProfiles; 18 | import org.springframework.web.client.RestTemplate; 19 | 20 | @SpringBootTest(classes = ShippingTestApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 21 | @ActiveProfiles("test") 22 | class ShippingWebIntegrationTest { 23 | 24 | @LocalServerPort 25 | private int serverPort; 26 | 27 | private RestTemplate restTemplate = new RestTemplate(); 28 | 29 | @Test 30 | void isHTMLReturned() { 31 | String body = getForMediaType(String.class, MediaType.TEXT_HTML, shippingURL()); 32 | 33 | assertThat(body, containsString(" T getForMediaType(Class value, MediaType mediaType, String url) { 41 | HttpHeaders headers = new HttpHeaders(); 42 | headers.setAccept(Arrays.asList(mediaType)); 43 | 44 | HttpEntity entity = new HttpEntity("parameters", headers); 45 | 46 | ResponseEntity resultEntity = restTemplate.exchange(url, HttpMethod.GET, entity, value); 47 | 48 | return resultEntity.getBody(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /microservice-spring-demo/fail.yaml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | postgres-spring: 5 | image: spring-microservices/postgres 6 | container_name: spring-postgres 7 | ports: 8 | - "5432:5432" 9 | build: 10 | context: ./postgres 11 | dockerfile: Dockerfile 12 | environment: 13 | POSTGRES_USER: dbuser 14 | POSTGRES_PASSWORD: dbpass 15 | spring-order: 16 | image: spring-microservices/order 17 | container_name: spring-order 18 | ports: 19 | - "18081:8080" 20 | links: 21 | - postgres-spring 22 | - zipkin 23 | build: 24 | context: ./microservice-spring-order 25 | dockerfile: Dockerfile 26 | environment: 27 | FAILRANDOMLY: "true" 28 | spring-shipping: 29 | image: spring-microservices/shipping 30 | container_name: spring-shipping 31 | ports: 32 | - "18083:8080" 33 | links: 34 | - postgres-spring 35 | - spring-order 36 | - zipkin 37 | build: 38 | context: ./microservice-spring-shipping 39 | dockerfile: Dockerfile 40 | spring-invoicing: 41 | image: spring-microservices/invoicing 42 | container_name: spring-invoicing 43 | ports: 44 | - "18082:8080" 45 | links: 46 | - postgres-spring 47 | - spring-order 48 | - zipkin 49 | build: 50 | context: ./microservice-spring-invoicing 51 | dockerfile: Dockerfile 52 | prometheus: 53 | image: spring-microservices/prometheus 54 | container_name: spring-prometheus 55 | ports: 56 | - "9090:9090" 57 | links: 58 | - spring-order 59 | - spring-invoicing 60 | - spring-shipping 61 | build: 62 | context: ./prometheus 63 | dockerfile: Dockerfile 64 | grafana: 65 | image: 'grafana/grafana:10.2.0' 66 | links: 67 | - prometheus 68 | ports: 69 | - '3000:3000' 70 | zipkin: 71 | image: openzipkin/zipkin:2.24 72 | ports: 73 | - 9412:9411 74 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/poller/OrderFeed.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping.poller; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | public class OrderFeed { 8 | 9 | private Date updated; 10 | 11 | private List orders; 12 | 13 | public OrderFeed() { 14 | super(); 15 | orders = new ArrayList(); 16 | } 17 | 18 | public Date getUpdated() { 19 | return updated; 20 | } 21 | 22 | public void setUpdated(Date updated) { 23 | this.updated = updated; 24 | } 25 | 26 | public List getOrders() { 27 | return orders; 28 | } 29 | 30 | public void setOrders(List orders) { 31 | this.orders = orders; 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | final int prime = 31; 37 | int result = 1; 38 | result = prime * result + ((orders == null) ? 0 : orders.hashCode()); 39 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 40 | return result; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object obj) { 45 | if (this == obj) 46 | return true; 47 | if (obj == null) 48 | return false; 49 | if (getClass() != obj.getClass()) 50 | return false; 51 | OrderFeed other = (OrderFeed) obj; 52 | if (orders == null) { 53 | if (other.orders != null) 54 | return false; 55 | } else if (!orders.equals(other.orders)) 56 | return false; 57 | if (updated == null) { 58 | if (other.updated != null) 59 | return false; 60 | } else if (!updated.equals(other.updated)) 61 | return false; 62 | return true; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "OrderFeed [updated=" + updated + ", orders=" + orders + "]"; 68 | } 69 | 70 | 71 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/test/java/com/ewolff/microservice/invoicing/InvoicingServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.equalTo; 5 | import static org.hamcrest.Matchers.is; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Date; 9 | 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 14 | import org.springframework.test.context.ActiveProfiles; 15 | 16 | @SpringBootTest(classes = InvoiceTestApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 17 | @ActiveProfiles("test") 18 | class InvoicingServiceTest { 19 | 20 | @Autowired 21 | private InvoiceRepository invoiceRepository; 22 | 23 | @Autowired 24 | private InvoiceService invoiceService; 25 | 26 | @Test 27 | void ensureIdempotencySecondCallIgnored() { 28 | long countBefore = invoiceRepository.count(); 29 | Invoice invoice = new Invoice(42, 30 | new Customer(23, "Eberhard", "Wolff", "eberhard.wolff@innoq.com"), 31 | new Date(0L), new Address("Krischstr. 100", "40789", "Monheim am Rhein"), new ArrayList()); 32 | invoiceService.generateInvoice(invoice); 33 | assertThat(invoiceRepository.count(), is(countBefore + 1)); 34 | assertThat(invoiceRepository.findById(42L).get().getUpdated().getTime(), equalTo(0L)); 35 | invoice = new Invoice(42, 36 | new Customer(23, "Eberhard", "Wolff", "eberhard.wolff@innoq.com"), 37 | new Date(), new Address("Krischstr. 100", "40789", "Monheim am Rhein"), new ArrayList()); 38 | invoiceService.generateInvoice(invoice); 39 | assertThat(invoiceRepository.count(), is(countBefore + 1)); 40 | assertThat(invoiceRepository.findById(42L).get().getUpdated().getTime(), equalTo(0L)); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/poller/OrderFeed.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing.poller; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | public class OrderFeed { 8 | 9 | private Date updated; 10 | 11 | private List orders; 12 | 13 | public OrderFeed() { 14 | super(); 15 | orders = new ArrayList(); 16 | } 17 | 18 | public Date getUpdated() { 19 | return updated; 20 | } 21 | 22 | public void setUpdated(Date updated) { 23 | this.updated = updated; 24 | } 25 | 26 | public List getOrders() { 27 | return orders; 28 | } 29 | 30 | public void setOrders(List orders) { 31 | this.orders = orders; 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | final int prime = 31; 37 | int result = 1; 38 | result = prime * result + ((orders == null) ? 0 : orders.hashCode()); 39 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 40 | return result; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object obj) { 45 | if (this == obj) 46 | return true; 47 | if (obj == null) 48 | return false; 49 | if (getClass() != obj.getClass()) 50 | return false; 51 | OrderFeed other = (OrderFeed) obj; 52 | if (orders == null) { 53 | if (other.orders != null) 54 | return false; 55 | } else if (!orders.equals(other.orders)) 56 | return false; 57 | if (updated == null) { 58 | if (other.updated != null) 59 | return false; 60 | } else if (!updated.equals(other.updated)) 61 | return false; 62 | return true; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "OrderFeed [updated=" + updated + ", orders=" + orders + "]"; 68 | } 69 | 70 | 71 | 72 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Microservice Spring Sample 2 | ===================== 3 | 4 | 5 | This demo uses [Spring Boot](https://spring.io/projects/spring-boot) 6 | to solve the typical problems of microservices. 7 | 8 | This project creates a complete microservice demo system. 9 | are implemented in Java using Spring Boot. 10 | 11 | It uses three microservices: 12 | - `Order` to accept orders. 13 | - `Shipping` to ship the orders. 14 | - `Invoicing` to ship invoices. 15 | 16 | How to run 17 | --------- 18 | 19 | See [How to run](HOW-TO-RUN.md). 20 | 21 | 22 | Video / Podcast 23 | -------- 24 | 25 | Check out the video [Microservices with Spring](https://www.youtube.com/watch?v=9q2Qlp0IDZY) (German with auto-translated subtitles in your preferred language). 26 | Other formats including a Podcast are available at [Software 27 | Architektur im 28 | Stream](https://software-architektur.tv/2023/12/01/folge190.html). 29 | 30 | Remarks on the Code 31 | ------------------- 32 | 33 | The microservices are: 34 | - [microservice-spring-order](microservice-spring-demo/microservice-spring-order) to create the orders 35 | - [microserivce-spring-shipping](microservice-spring-demo/microservice-spring-shipping) for the shipping 36 | - [microservice-spring-invoicing](microservice-spring-demo/microservice-spring-invoicing) for the invoices 37 | 38 | The microservices have an Java main application in `src/test/java` to 39 | run them stand alone. microservice-demo-shipping and 40 | microservice-demo-invoicing both use a stub for the 41 | other order service for the tests. 42 | 43 | The data of an order is copied - including the data of the customer 44 | and the items. So if a customer or item changes in the order system 45 | this does not influence existing shipments and invoices. It would be 46 | odd if a change to a price would also change existing invoices. Also 47 | only the information needed for the shipment and the invoice are 48 | copied over to the other systems. 49 | 50 | The job to poll the order feed is run every 30 seconds. 51 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/ShipmentLine.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Embedded; 5 | import jakarta.persistence.Entity; 6 | import jakarta.persistence.GeneratedValue; 7 | import jakarta.persistence.Id; 8 | 9 | @Entity 10 | public class ShipmentLine { 11 | 12 | @Column(name = "F_COUNT") 13 | private int count; 14 | 15 | @Embedded 16 | private Item item; 17 | 18 | @Id 19 | @GeneratedValue 20 | private long id; 21 | 22 | public void setCount(int count) { 23 | this.count = count; 24 | } 25 | 26 | public void setItem(Item item) { 27 | this.item = item; 28 | } 29 | 30 | public ShipmentLine() { 31 | } 32 | 33 | public ShipmentLine(int count, Item item) { 34 | this.count = count; 35 | this.item = item; 36 | } 37 | 38 | public int getCount() { 39 | return count; 40 | } 41 | 42 | public Item getItem() { 43 | return item; 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | final int prime = 31; 49 | int result = 1; 50 | result = prime * result + count; 51 | result = prime * result + (int) (id ^ (id >>> 32)); 52 | result = prime * result + ((item == null) ? 0 : item.hashCode()); 53 | return result; 54 | } 55 | 56 | @Override 57 | public boolean equals(Object obj) { 58 | if (this == obj) 59 | return true; 60 | if (obj == null) 61 | return false; 62 | if (getClass() != obj.getClass()) 63 | return false; 64 | ShipmentLine other = (ShipmentLine) obj; 65 | if (count != other.count) 66 | return false; 67 | if (id != other.id) 68 | return false; 69 | if (item == null) { 70 | if (other.item != null) 71 | return false; 72 | } else if (!item.equals(other.item)) 73 | return false; 74 | return true; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return "ShipmentLine [count=" + count + ", item=" + item + ", id=" + id + "]"; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/poller/OrderFeedEntry.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping.poller; 2 | 3 | import java.util.Date; 4 | 5 | public class OrderFeedEntry { 6 | 7 | private long id; 8 | 9 | private String link; 10 | 11 | private Date updated; 12 | 13 | public long getId() { 14 | return id; 15 | } 16 | 17 | public void setId(long id) { 18 | this.id = id; 19 | } 20 | 21 | public String getLink() { 22 | return link; 23 | } 24 | 25 | public void setLink(String link) { 26 | this.link = link; 27 | } 28 | 29 | public Date getUpdated() { 30 | return updated; 31 | } 32 | 33 | public void setUpdated(Date updated) { 34 | this.updated = updated; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | final int prime = 31; 40 | int result = 1; 41 | result = prime * result + (int) (id ^ (id >>> 32)); 42 | result = prime * result + ((link == null) ? 0 : link.hashCode()); 43 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 44 | return result; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object obj) { 49 | if (this == obj) 50 | return true; 51 | if (obj == null) 52 | return false; 53 | if (getClass() != obj.getClass()) 54 | return false; 55 | OrderFeedEntry other = (OrderFeedEntry) obj; 56 | if (id != other.id) 57 | return false; 58 | if (link == null) { 59 | if (other.link != null) 60 | return false; 61 | } else if (!link.equals(other.link)) 62 | return false; 63 | if (updated == null) { 64 | if (other.updated != null) 65 | return false; 66 | } else if (!updated.equals(other.updated)) 67 | return false; 68 | return true; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "OrderFeedEntry [id=" + id + ", link=" + link + ", updated=" + updated + "]"; 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/OrderLine.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import com.ewolff.microservice.order.item.Item; 4 | 5 | import jakarta.persistence.Column; 6 | import jakarta.persistence.Entity; 7 | import jakarta.persistence.GeneratedValue; 8 | import jakarta.persistence.Id; 9 | import jakarta.persistence.ManyToOne; 10 | 11 | @Entity 12 | public class OrderLine { 13 | 14 | @Column(name = "F_COUNT") 15 | private int count; 16 | 17 | @ManyToOne 18 | private Item item; 19 | 20 | @Id 21 | @GeneratedValue 22 | private long id; 23 | 24 | public void setCount(int count) { 25 | this.count = count; 26 | } 27 | 28 | public void setItem(Item item) { 29 | this.item = item; 30 | } 31 | 32 | public OrderLine() { 33 | } 34 | 35 | public OrderLine(int count, Item item) { 36 | this.count = count; 37 | this.item = item; 38 | } 39 | 40 | public int getCount() { 41 | return count; 42 | } 43 | 44 | public Item getItem() { 45 | return item; 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | final int prime = 31; 51 | int result = 1; 52 | result = prime * result + count; 53 | result = prime * result + (int) (id ^ (id >>> 32)); 54 | result = prime * result + ((item == null) ? 0 : item.hashCode()); 55 | return result; 56 | } 57 | 58 | @Override 59 | public boolean equals(Object obj) { 60 | if (this == obj) 61 | return true; 62 | if (obj == null) 63 | return false; 64 | if (getClass() != obj.getClass()) 65 | return false; 66 | OrderLine other = (OrderLine) obj; 67 | if (count != other.count) 68 | return false; 69 | if (id != other.id) 70 | return false; 71 | if (item == null) { 72 | if (other.item != null) 73 | return false; 74 | } else if (!item.equals(other.item)) 75 | return false; 76 | return true; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "OrderLine [count=" + count + ", item=" + item + ", id=" + id + "]"; 82 | } 83 | 84 | 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/poller/OrderFeedEntry.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing.poller; 2 | 3 | import java.util.Date; 4 | 5 | public class OrderFeedEntry { 6 | 7 | private long id; 8 | 9 | private String link; 10 | 11 | private Date updated; 12 | 13 | public long getId() { 14 | return id; 15 | } 16 | 17 | public void setId(long id) { 18 | this.id = id; 19 | } 20 | 21 | public String getLink() { 22 | return link; 23 | } 24 | 25 | public void setLink(String link) { 26 | this.link = link; 27 | } 28 | 29 | public Date getUpdated() { 30 | return updated; 31 | } 32 | 33 | public void setUpdated(Date updated) { 34 | this.updated = updated; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | final int prime = 31; 40 | int result = 1; 41 | result = prime * result + (int) (id ^ (id >>> 32)); 42 | result = prime * result + ((link == null) ? 0 : link.hashCode()); 43 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 44 | return result; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object obj) { 49 | if (this == obj) 50 | return true; 51 | if (obj == null) 52 | return false; 53 | if (getClass() != obj.getClass()) 54 | return false; 55 | OrderFeedEntry other = (OrderFeedEntry) obj; 56 | if (id != other.id) 57 | return false; 58 | if (link == null) { 59 | if (other.link != null) 60 | return false; 61 | } else if (!link.equals(other.link)) 62 | return false; 63 | if (updated == null) { 64 | if (other.updated != null) 65 | return false; 66 | } else if (!updated.equals(other.updated)) 67 | return false; 68 | return true; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "OrderFeedEntry [id=" + id + ", link=" + link + ", updated=" + updated + "]"; 74 | } 75 | 76 | 77 | 78 | 79 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/OrderFeed.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | public class OrderFeed { 8 | 9 | private Date updated; 10 | 11 | private List orders; 12 | 13 | public OrderFeed() { 14 | super(); 15 | } 16 | 17 | public OrderFeed(Date updated) { 18 | super(); 19 | this.updated = updated; 20 | orders = new ArrayList(); 21 | } 22 | 23 | public Date getUpdated() { 24 | return updated; 25 | } 26 | 27 | public void setUpdated(Date updated) { 28 | this.updated = updated; 29 | } 30 | 31 | public List getOrders() { 32 | return orders; 33 | } 34 | 35 | public void setOrders(List orders) { 36 | this.orders = orders; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | final int prime = 31; 42 | int result = 1; 43 | result = prime * result + ((orders == null) ? 0 : orders.hashCode()); 44 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 45 | return result; 46 | } 47 | 48 | @Override 49 | public boolean equals(Object obj) { 50 | if (this == obj) 51 | return true; 52 | if (obj == null) 53 | return false; 54 | if (getClass() != obj.getClass()) 55 | return false; 56 | OrderFeed other = (OrderFeed) obj; 57 | if (orders == null) { 58 | if (other.orders != null) 59 | return false; 60 | } else if (!orders.equals(other.orders)) 61 | return false; 62 | if (updated == null) { 63 | if (other.updated != null) 64 | return false; 65 | } else if (!updated.equals(other.updated)) 66 | return false; 67 | return true; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "OrderFeed [updated=" + updated + ", orders=" + orders + "]"; 73 | } 74 | 75 | 76 | 77 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/InvoiceLine.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Embedded; 5 | import jakarta.persistence.Entity; 6 | import jakarta.persistence.GeneratedValue; 7 | import jakarta.persistence.Id; 8 | 9 | @Entity 10 | public class InvoiceLine { 11 | 12 | @Column(name = "F_COUNT") 13 | private int count; 14 | 15 | @Embedded 16 | private Item item; 17 | 18 | @Id 19 | @GeneratedValue 20 | private long id; 21 | 22 | public void setCount(int count) { 23 | this.count = count; 24 | } 25 | 26 | public void setItem(Item item) { 27 | this.item = item; 28 | } 29 | 30 | public InvoiceLine() { 31 | } 32 | 33 | public InvoiceLine(int count, Item item) { 34 | this.count = count; 35 | this.item = item; 36 | } 37 | 38 | public int getCount() { 39 | return count; 40 | } 41 | 42 | public Item getItem() { 43 | return item; 44 | } 45 | 46 | public double totalAmount() { 47 | return getCount() * getItem().getPrice(); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | final int prime = 31; 53 | int result = 1; 54 | result = prime * result + count; 55 | result = prime * result + (int) (id ^ (id >>> 32)); 56 | result = prime * result + ((item == null) ? 0 : item.hashCode()); 57 | return result; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if (this == obj) 63 | return true; 64 | if (obj == null) 65 | return false; 66 | if (getClass() != obj.getClass()) 67 | return false; 68 | InvoiceLine other = (InvoiceLine) obj; 69 | if (count != other.count) 70 | return false; 71 | if (id != other.id) 72 | return false; 73 | if (item == null) { 74 | if (other.item != null) 75 | return false; 76 | } else if (!item.equals(other.item)) 77 | return false; 78 | return true; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "InvoiceLine [count=" + count + ", item=" + item + ", id=" + id + "]"; 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /microservice-spring-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | org.springframework.boot 6 | spring-boot-starter-parent 7 | 3.4.4 8 | 9 | 10 | 4.0.0 11 | com.ewolff 12 | microservice-spring 13 | 0.0.1-SNAPSHOT 14 | pom 15 | 16 | microservice-spring-shipping 17 | microservice-spring-invoicing 18 | microservice-spring-order 19 | 20 | 21 | 17 22 | 2.1.0 23 | 24 | 25 | 26 | 27 | org.webjars 28 | bootstrap 29 | 3.3.6 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-surefire-plugin 38 | 39 | -XX:TieredStopAtLevel=1 -noverify 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | -XX:TieredStopAtLevel=1 -noverify 47 | 48 | 49 | 50 | 51 | 52 | 53 | spring-releases 54 | Spring Releases 55 | https://repo.spring.io/release 56 | 57 | 58 | 59 | 60 | spring-releases 61 | Spring Releases 62 | https://repo.spring.io/release 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/Address.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import jakarta.persistence.Embeddable; 4 | 5 | @Embeddable 6 | public class Address { 7 | 8 | private String street; 9 | private String zip; 10 | private String city; 11 | 12 | public Address() { 13 | super(); 14 | } 15 | 16 | public Address(String street, String zip, String city) { 17 | super(); 18 | this.street = street; 19 | this.zip = zip; 20 | this.city = city; 21 | } 22 | 23 | public String getStreet() { 24 | return street; 25 | } 26 | 27 | public void setStreet(String street) { 28 | this.street = street; 29 | } 30 | 31 | public String getZip() { 32 | return zip; 33 | } 34 | 35 | public void setZip(String zip) { 36 | this.zip = zip; 37 | } 38 | 39 | public String getCity() { 40 | return city; 41 | } 42 | 43 | public void setCity(String city) { 44 | this.city = city; 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | final int prime = 31; 50 | int result = 1; 51 | result = prime * result + ((city == null) ? 0 : city.hashCode()); 52 | result = prime * result + ((street == null) ? 0 : street.hashCode()); 53 | result = prime * result + ((zip == null) ? 0 : zip.hashCode()); 54 | return result; 55 | } 56 | 57 | @Override 58 | public boolean equals(Object obj) { 59 | if (this == obj) 60 | return true; 61 | if (obj == null) 62 | return false; 63 | if (getClass() != obj.getClass()) 64 | return false; 65 | Address other = (Address) obj; 66 | if (city == null) { 67 | if (other.city != null) 68 | return false; 69 | } else if (!city.equals(other.city)) 70 | return false; 71 | if (street == null) { 72 | if (other.street != null) 73 | return false; 74 | } else if (!street.equals(other.street)) 75 | return false; 76 | if (zip == null) { 77 | if (other.zip != null) 78 | return false; 79 | } else if (!zip.equals(other.zip)) 80 | return false; 81 | return true; 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | return "Address [street=" + street + ", zip=" + zip + ", city=" + city + "]"; 87 | } 88 | 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/OrderRestController.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.context.request.WebRequest; 12 | 13 | import com.ewolff.microservice.order.OrderFeed; 14 | import com.ewolff.microservice.order.OrderFeedEntry; 15 | 16 | import jakarta.servlet.http.HttpServletRequest; 17 | 18 | @RestController 19 | public class OrderRestController { 20 | 21 | private final Logger log = LoggerFactory.getLogger(OrderRestController.class); 22 | 23 | private OrderRepository orderRepository; 24 | 25 | public OrderRestController(OrderRepository orderRepository) { 26 | this.orderRepository = orderRepository; 27 | } 28 | 29 | private String baseUrl(HttpServletRequest request) { 30 | return "%s://%s:%d%s/".formatted(request.getScheme(), request.getServerName(), request.getServerPort(), 31 | request.getContextPath()); 32 | } 33 | 34 | @RequestMapping(value = "/feed", produces = "application/json") 35 | public OrderFeed orderFeed(WebRequest webRequest, HttpServletRequest httpRequest) { 36 | if ((orderRepository.lastUpdate() != null) 37 | && (webRequest.checkNotModified(orderRepository.lastUpdate().getTime()))) { 38 | log.trace("Not Modified returned - request with If-Modified-Since {}", 39 | webRequest.getHeader(HttpHeaders.IF_MODIFIED_SINCE)); 40 | return null; 41 | } 42 | log.trace("Returned Feed"); 43 | List orderFeedEntries = new ArrayList(); 44 | for (Order order : orderRepository.findAll()) { 45 | orderFeedEntries.add(new OrderFeedEntry(order.getId(), 46 | baseUrl(httpRequest) + "order/" + Long.toString(order.getId()), order.getUpdated())); 47 | } 48 | OrderFeed orderFeed = new OrderFeed(orderRepository.lastUpdate()); 49 | orderFeed.setOrders(orderFeedEntries); 50 | return orderFeed; 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/OrderFeedEntry.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import java.util.Date; 4 | 5 | public class OrderFeedEntry { 6 | 7 | private long id; 8 | 9 | private String link; 10 | 11 | private Date updated; 12 | 13 | public OrderFeedEntry() { 14 | } 15 | 16 | public OrderFeedEntry(long id, String link, Date updated) { 17 | this.id = id; 18 | this.link = link; 19 | this.updated = updated; 20 | } 21 | 22 | public long getId() { 23 | return id; 24 | } 25 | 26 | public void setId(long id) { 27 | this.id = id; 28 | } 29 | 30 | public String getLink() { 31 | return link; 32 | } 33 | 34 | public void setLink(String link) { 35 | this.link = link; 36 | } 37 | 38 | public Date getUpdated() { 39 | return updated; 40 | } 41 | 42 | public void setUpdated(Date updated) { 43 | this.updated = updated; 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | final int prime = 31; 49 | int result = 1; 50 | result = prime * result + (int) (id ^ (id >>> 32)); 51 | result = prime * result + ((link == null) ? 0 : link.hashCode()); 52 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 53 | return result; 54 | } 55 | 56 | @Override 57 | public boolean equals(Object obj) { 58 | if (this == obj) 59 | return true; 60 | if (obj == null) 61 | return false; 62 | if (getClass() != obj.getClass()) 63 | return false; 64 | OrderFeedEntry other = (OrderFeedEntry) obj; 65 | if (id != other.id) 66 | return false; 67 | if (link == null) { 68 | if (other.link != null) 69 | return false; 70 | } else if (!link.equals(other.link)) 71 | return false; 72 | if (updated == null) { 73 | if (other.updated != null) 74 | return false; 75 | } else if (!updated.equals(other.updated)) 76 | return false; 77 | return true; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "OrderFeedEntry [id=" + id + ", link=" + link + ", updated=" + updated + "]"; 83 | } 84 | 85 | 86 | 87 | 88 | } -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/Item.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Embeddable; 5 | 6 | @Embeddable 7 | public class Item { 8 | 9 | @Column(nullable = false) 10 | private Long itemId; 11 | 12 | @Column(nullable = false) 13 | private String name; 14 | 15 | @Column(nullable = false) 16 | private double price; 17 | 18 | public Item() { 19 | super(); 20 | itemId = 0l; 21 | } 22 | 23 | public Item(String name, double price) { 24 | super(); 25 | this.name = name; 26 | this.price = price; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public double getPrice() { 38 | return price; 39 | } 40 | 41 | public void setPrice(double price) { 42 | this.price = price; 43 | } 44 | 45 | public Long getItemId() { 46 | return itemId; 47 | } 48 | 49 | public void setItemId(Long id) { 50 | this.itemId = id; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | final int prime = 31; 56 | int result = 1; 57 | result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); 58 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 59 | long temp; 60 | temp = Double.doubleToLongBits(price); 61 | result = prime * result + (int) (temp ^ (temp >>> 32)); 62 | return result; 63 | } 64 | 65 | @Override 66 | public boolean equals(Object obj) { 67 | if (this == obj) 68 | return true; 69 | if (obj == null) 70 | return false; 71 | if (getClass() != obj.getClass()) 72 | return false; 73 | Item other = (Item) obj; 74 | if (itemId == null) { 75 | if (other.itemId != null) 76 | return false; 77 | } else if (!itemId.equals(other.itemId)) 78 | return false; 79 | if (name == null) { 80 | if (other.name != null) 81 | return false; 82 | } else if (!name.equals(other.name)) 83 | return false; 84 | if (Double.doubleToLongBits(price) != Double.doubleToLongBits(other.price)) 85 | return false; 86 | return true; 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | return "Item [itemId=" + itemId + ", name=" + name + ", price=" + price + "]"; 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/OrderTestDataGenerator.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import org.springframework.context.annotation.DependsOn; 5 | import org.springframework.context.annotation.Profile; 6 | import org.springframework.data.domain.Sort; 7 | import org.springframework.stereotype.Component; 8 | 9 | import com.ewolff.microservice.order.customer.CustomerRepository; 10 | import com.ewolff.microservice.order.customer.CustomerTestDataGenerator; 11 | import com.ewolff.microservice.order.item.ItemRepository; 12 | import com.ewolff.microservice.order.item.ItemTestDataGenerator; 13 | import com.ewolff.microservice.order.logic.Address; 14 | import com.ewolff.microservice.order.logic.Order; 15 | import com.ewolff.microservice.order.logic.OrderRepository; 16 | 17 | @Component 18 | @Profile("test") 19 | @DependsOn({ "itemTestDataGenerator", "customerTestDataGenerator" }) 20 | public class OrderTestDataGenerator { 21 | 22 | private final OrderRepository orderRepository; 23 | private ItemRepository itemRepository; 24 | private CustomerRepository customerRepository; 25 | private ItemTestDataGenerator itemTestDataGenerator; 26 | private CustomerTestDataGenerator customerTestDataGenerator; 27 | 28 | public OrderTestDataGenerator(OrderRepository orderRepository, ItemRepository itemRepository, 29 | CustomerRepository customerRepository, CustomerTestDataGenerator customerTestDataGenerator, 30 | ItemTestDataGenerator itemTestDataGenerator) { 31 | this.orderRepository = orderRepository; 32 | this.itemRepository = itemRepository; 33 | this.customerRepository = customerRepository; 34 | this.itemTestDataGenerator = itemTestDataGenerator; 35 | this.customerTestDataGenerator = customerTestDataGenerator; 36 | } 37 | 38 | @PostConstruct 39 | public void generateTestData() { 40 | itemTestDataGenerator.generateTestData(); 41 | customerTestDataGenerator.generateTestData(); 42 | Order order = new Order(customerRepository.findAll(Sort.unsorted()).iterator().next(), 1); 43 | order.setShippingAddress(new Address("Ohlauer Str. 43", "10999", "Berlin")); 44 | order.setBillingAddress(new Address("Krischerstr. 100", "40789", "Monheim am Rhein")); 45 | order.setDeliveryService("Hermes"); 46 | order.addLine(42, itemRepository.findAll(Sort.unsorted()).iterator().next()); 47 | order = orderRepository.save(order); 48 | orderRepository.save(order); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/item/Item.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.item; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Entity; 5 | import jakarta.persistence.GeneratedValue; 6 | import jakarta.persistence.Id; 7 | 8 | @Entity 9 | public class Item { 10 | 11 | @Id 12 | @GeneratedValue 13 | private Long itemId; 14 | 15 | @Column(nullable = false) 16 | private String name; 17 | 18 | @Column(nullable = false) 19 | private double price; 20 | 21 | public Item() { 22 | super(); 23 | itemId = 0l; 24 | } 25 | 26 | public Item(String name, double price) { 27 | super(); 28 | this.name = name; 29 | this.price = price; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public double getPrice() { 41 | return price; 42 | } 43 | 44 | public void setPrice(double price) { 45 | this.price = price; 46 | } 47 | 48 | public Long getItemId() { 49 | return itemId; 50 | } 51 | 52 | public void setItemId(Long id) { 53 | this.itemId = id; 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | final int prime = 31; 59 | int result = 1; 60 | result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); 61 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 62 | long temp; 63 | temp = Double.doubleToLongBits(price); 64 | result = prime * result + (int) (temp ^ (temp >>> 32)); 65 | return result; 66 | } 67 | 68 | @Override 69 | public boolean equals(Object obj) { 70 | if (this == obj) 71 | return true; 72 | if (obj == null) 73 | return false; 74 | if (getClass() != obj.getClass()) 75 | return false; 76 | Item other = (Item) obj; 77 | if (itemId == null) { 78 | if (other.itemId != null) 79 | return false; 80 | } else if (!itemId.equals(other.itemId)) 81 | return false; 82 | if (name == null) { 83 | if (other.name != null) 84 | return false; 85 | } else if (!name.equals(other.name)) 86 | return false; 87 | if (Double.doubleToLongBits(price) != Double.doubleToLongBits(other.price)) 88 | return false; 89 | return true; 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return "Item [itemId=" + itemId + ", name=" + name + ", price=" + price + "]"; 95 | } 96 | 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/Customer.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Embeddable; 5 | 6 | @Embeddable 7 | public class Customer { 8 | 9 | @Column(nullable = false) 10 | private Long customerId; 11 | 12 | @Column(nullable = false) 13 | private String name; 14 | 15 | @Column(nullable = false) 16 | private String firstname; 17 | 18 | public Customer() { 19 | super(); 20 | customerId = 0l; 21 | } 22 | 23 | public Customer(long customerId, String firstname, String name) { 24 | super(); 25 | this.customerId = customerId; 26 | this.name = name; 27 | this.firstname = firstname; 28 | } 29 | 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | public String getFirstname() { 39 | return firstname; 40 | } 41 | 42 | public void setFirstname(String firstname) { 43 | this.firstname = firstname; 44 | } 45 | 46 | public Long getCustomerId() { 47 | return customerId; 48 | } 49 | 50 | public void setCustomerId(Long id) { 51 | this.customerId = id; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | final int prime = 31; 57 | int result = 1; 58 | result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); 59 | result = prime * result + ((firstname == null) ? 0 : firstname.hashCode()); 60 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 61 | return result; 62 | } 63 | 64 | @Override 65 | public boolean equals(Object obj) { 66 | if (this == obj) 67 | return true; 68 | if (obj == null) 69 | return false; 70 | if (getClass() != obj.getClass()) 71 | return false; 72 | Customer other = (Customer) obj; 73 | if (customerId == null) { 74 | if (other.customerId != null) 75 | return false; 76 | } else if (!customerId.equals(other.customerId)) 77 | return false; 78 | if (firstname == null) { 79 | if (other.firstname != null) 80 | return false; 81 | } else if (!firstname.equals(other.firstname)) 82 | return false; 83 | if (name == null) { 84 | if (other.name != null) 85 | return false; 86 | } else if (!name.equals(other.name)) 87 | return false; 88 | return true; 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return "Customer [customerId=" + customerId + ", name=" + name + ", firstname=" + firstname + "]"; 94 | } 95 | 96 | 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/resources/templates/invoice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Invoicing 6 | 7 | 8 | 9 |

Invoicing

10 |
11 | 12 |
13 |
14 |
15 | Customer 16 |
17 |
18 |
19 |
20 | 21 |
22 |

Billing Address

23 |
24 | 25 |
26 |
27 | Street 28 |
29 |
30 |
31 |
32 |
33 | Zip 34 |
35 |
36 |
37 |
38 |
39 | City 40 |
41 |
42 |
43 | 44 |
45 |

Invoice Lines

46 |
47 | 48 |
49 |
50 | Quantity 51 |
52 |
53 | Item 54 |
55 |
56 | Name 57 |
58 |
59 | Price 60 |
61 |
62 | Total Price 63 |
64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 74 |
75 | 76 |
77 |

More Information

78 |
79 | 80 |
81 | 82 | 83 |
84 | 85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/test/java/com/ewolff/microservice/shipping/ShippingServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.equalTo; 5 | import static org.hamcrest.Matchers.is; 6 | import static org.junit.jupiter.api.Assertions.assertThrows; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Date; 10 | 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 15 | import org.springframework.test.context.ActiveProfiles; 16 | 17 | @SpringBootTest(classes = ShippingTestApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 18 | @ActiveProfiles("test") 19 | class ShippingServiceTest { 20 | 21 | @Autowired 22 | private ShipmentRepository shipmentRepository; 23 | 24 | @Autowired 25 | private ShipmentService shipmentService; 26 | 27 | @Test 28 | void ensureIdempotencySecondCallIgnored() { 29 | long countBefore = shipmentRepository.count(); 30 | Shipment shipment = new Shipment(42L, 31 | new Customer(23L, "Eberhard", "Wolff"), 32 | new Date(0L), new Address("Krischstr. 100", "40789", "Monheim am Rhein"), 33 | new ArrayList(), "DHL"); 34 | shipmentService.ship(shipment); 35 | assertThat(shipmentRepository.count(), is(countBefore + 1)); 36 | assertThat(shipmentRepository.findById(42L).get().getUpdated().getTime(), equalTo(0L)); 37 | shipment = new Shipment(42, 38 | new Customer(23L, "Eberhard", "Wolff"), 39 | new Date(), new Address("Krischstr. 100", "40789", "Monheim am Rhein"), new ArrayList(), 40 | "DHL"); 41 | shipmentService.ship(shipment); 42 | assertThat(shipmentRepository.count(), is(countBefore + 1)); 43 | assertThat(shipmentRepository.findById(42L).get().getUpdated().getTime(), equalTo(0L)); 44 | } 45 | 46 | @Test 47 | void ensureShipmentRateCalculted() { 48 | Shipment shipment = new Shipment(43L, 49 | new Customer(23L, "Eberhard", "Wolff"), 50 | new Date(0L), new Address("Krischstr. 100", "40789", "Monheim am Rhein"), 51 | new ArrayList(), "DHL"); 52 | shipmentService.ship(shipment); 53 | assertThat(shipment.getCost(), is(1)); 54 | } 55 | 56 | @Test 57 | void ensureUnkownShipmentError() { 58 | Shipment shipment = new Shipment(44L, 59 | new Customer(23L, "Eberhard", "Wolff"), 60 | new Date(0L), new Address("Krischstr. 100", "40789", "Monheim am Rhein"), 61 | new ArrayList(), "Unkown Service"); 62 | assertThrows(IllegalArgumentException.class, () -> shipmentService.ship(shipment)); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/resources/templates/shipment.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Shipping 6 | 7 | 8 | 9 |

Shipping

10 |
11 | 12 |
13 |
14 |
15 | Customer 16 |
17 |
18 |
19 | 20 |
21 |
22 | Delivery Service 23 |
24 |
25 |
26 | 27 | 28 |
29 |

Shipping Address

30 |
31 | 32 |
33 |
34 | Street 35 |
36 |
37 |
38 |
39 |
40 | Zip 41 |
42 |
43 |
44 |
45 |
46 | City 47 |
48 |
49 |
50 | 51 |
52 |

Shipment Lines

53 |
54 | 55 |
56 |
57 | Quantity 58 |
59 |
60 | Item Id 61 |
62 |
63 | Item Name 64 |
65 |
66 | 67 |
68 |
69 |
70 |
71 |
72 | 73 |
74 |

Shipment Costs

75 |
76 | 77 |
78 |
79 | Cost 80 |
81 |
82 |
83 | 84 |
85 | 86 |
87 |

More Information

88 |
89 | 90 |
91 | 92 | 93 |
94 | 95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/resources/templates/order.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Order 6 | 7 | 8 | 9 |

Order

10 |
11 | 12 |
13 |
Customer
14 |
15 |
16 | 17 |
18 |
Total price
19 |
20 |
21 | 22 |
23 |

Shipping Address

24 |
25 | 26 |
27 |
Street
28 |
29 |
30 |
31 |
Zip
32 |
33 |
34 |
35 |
City
36 |
37 |
38 | 39 | 40 |
41 |

Billing Address

42 |
43 | 44 |
45 |
Street
46 |
47 |
48 |
49 |
Zip
50 |
51 |
52 |
53 |
City
54 |
55 |
56 | 57 |
58 |

Order Lines

59 |
60 | 61 |
62 |
Quantity
63 |
Item
64 |
Price
65 | 66 |
67 | 68 |
69 |
70 |
71 |
72 |
73 | 74 |
75 |

More Information

76 |
77 | 78 |
79 | 80 | 81 | 82 |
83 | 84 |
85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/Customer.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Embeddable; 5 | 6 | @Embeddable 7 | public class Customer { 8 | 9 | @Column(nullable = false) 10 | private Long customerId; 11 | 12 | @Column(nullable = false) 13 | private String name; 14 | 15 | @Column(nullable = false) 16 | private String firstname; 17 | 18 | @Column(nullable = false) 19 | private String email; 20 | 21 | public Customer() { 22 | super(); 23 | customerId = 0l; 24 | } 25 | 26 | public Customer(long customerId, String firstname, String name, String email) { 27 | super(); 28 | this.customerId = customerId; 29 | this.name = name; 30 | this.firstname = firstname; 31 | this.email = email; 32 | } 33 | 34 | public String getEmail() { 35 | return email; 36 | } 37 | 38 | public void setEmail(String email) { 39 | this.email = email; 40 | } 41 | 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | public void setName(String name) { 47 | this.name = name; 48 | } 49 | 50 | public String getFirstname() { 51 | return firstname; 52 | } 53 | 54 | public void setFirstname(String firstname) { 55 | this.firstname = firstname; 56 | } 57 | 58 | public Long getCustomerId() { 59 | return customerId; 60 | } 61 | 62 | public void setCustomerId(Long id) { 63 | this.customerId = id; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | final int prime = 31; 69 | int result = 1; 70 | result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); 71 | result = prime * result + ((email == null) ? 0 : email.hashCode()); 72 | result = prime * result + ((firstname == null) ? 0 : firstname.hashCode()); 73 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 74 | return result; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object obj) { 79 | if (this == obj) 80 | return true; 81 | if (obj == null) 82 | return false; 83 | if (getClass() != obj.getClass()) 84 | return false; 85 | Customer other = (Customer) obj; 86 | if (customerId == null) { 87 | if (other.customerId != null) 88 | return false; 89 | } else if (!customerId.equals(other.customerId)) 90 | return false; 91 | if (email == null) { 92 | if (other.email != null) 93 | return false; 94 | } else if (!email.equals(other.email)) 95 | return false; 96 | if (firstname == null) { 97 | if (other.firstname != null) 98 | return false; 99 | } else if (!firstname.equals(other.firstname)) 100 | return false; 101 | if (name == null) { 102 | if (other.name != null) 103 | return false; 104 | } else if (!name.equals(other.name)) 105 | return false; 106 | return true; 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | return "Customer [customerId=" + customerId + ", name=" + name + ", firstname=" + firstname + ", email=" + email 112 | + "]"; 113 | } 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/poller/InvoicePoller.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing.poller; 2 | 3 | import java.time.ZonedDateTime; 4 | import java.util.Date; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.http.HttpEntity; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.http.HttpMethod; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.scheduling.annotation.Scheduled; 15 | import org.springframework.stereotype.Component; 16 | import org.springframework.web.client.RestTemplate; 17 | 18 | import com.ewolff.microservice.invoicing.Invoice; 19 | import com.ewolff.microservice.invoicing.InvoiceService; 20 | 21 | import io.github.resilience4j.retry.annotation.Retry; 22 | 23 | @Component 24 | public class InvoicePoller { 25 | 26 | private final Logger log = LoggerFactory.getLogger(InvoicePoller.class); 27 | 28 | private String url = ""; 29 | 30 | private RestTemplate restTemplate = null; 31 | 32 | private ZonedDateTime lastModified = null; 33 | 34 | private boolean pollingActivated; 35 | 36 | private InvoiceService invoiceService; 37 | 38 | public InvoicePoller(@Value("${order.url}") String url, @Value("${poller.actived:true}") boolean pollingActivated, 39 | InvoiceService invoiceService, RestTemplate restTemplate) { 40 | super(); 41 | this.pollingActivated = pollingActivated; 42 | this.url = url; 43 | this.invoiceService = invoiceService; 44 | this.restTemplate = restTemplate; 45 | } 46 | 47 | @Scheduled(fixedDelay = 30000) 48 | @Retry(name = "poller") 49 | public void poll() { 50 | if (pollingActivated) { 51 | pollInternal(); 52 | } 53 | } 54 | 55 | public void pollInternal() { 56 | HttpHeaders requestHeaders = new HttpHeaders(); 57 | requestHeaders.set(HttpHeaders.ACCEPT, "*/*"); 58 | if (lastModified != null) { 59 | requestHeaders.setZonedDateTime(HttpHeaders.IF_MODIFIED_SINCE, lastModified); 60 | } 61 | HttpEntity requestEntity = new HttpEntity(requestHeaders); 62 | ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, OrderFeed.class); 63 | 64 | if (response.getStatusCode() != HttpStatus.NOT_MODIFIED) { 65 | log.trace("data has been modified"); 66 | OrderFeed feed = response.getBody(); 67 | for (OrderFeedEntry entry : feed.getOrders()) { 68 | if ((lastModified == null) || (entry.getUpdated().after(Date.from(lastModified.toInstant())))) { 69 | Invoice invoice = restTemplate.getForEntity(entry.getLink(), Invoice.class).getBody(); 70 | log.trace("saving invoice {}", invoice.getId()); 71 | invoiceService.generateInvoice(invoice); 72 | } 73 | } 74 | if (response.getHeaders().getFirst(HttpHeaders.LAST_MODIFIED) != null) { 75 | lastModified = response.getHeaders().getFirstZonedDateTime(HttpHeaders.LAST_MODIFIED); 76 | log.trace("Last-Modified header {}", lastModified); 77 | } 78 | } else { 79 | log.trace("no new data"); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/poller/ShippingPoller.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping.poller; 2 | 3 | import java.time.ZonedDateTime; 4 | import java.util.Date; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.http.HttpEntity; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.http.HttpMethod; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.scheduling.annotation.Scheduled; 15 | import org.springframework.stereotype.Component; 16 | import org.springframework.web.client.RestTemplate; 17 | 18 | import com.ewolff.microservice.shipping.Shipment; 19 | import com.ewolff.microservice.shipping.ShipmentService; 20 | 21 | import io.github.resilience4j.retry.annotation.Retry; 22 | 23 | @Component 24 | public class ShippingPoller { 25 | 26 | private final Logger log = LoggerFactory.getLogger(ShippingPoller.class); 27 | 28 | private String url = ""; 29 | 30 | private RestTemplate restTemplate = null; 31 | 32 | private ZonedDateTime lastModified = null; 33 | 34 | private ShipmentService shipmentService; 35 | 36 | private boolean pollingActivated = true; 37 | 38 | public ShippingPoller(@Value("${order.url}") String url, @Value("${poller.actived:true}") boolean pollingActivated, 39 | ShipmentService shipmentService, RestTemplate restTemplate) { 40 | super(); 41 | this.url = url; 42 | this.shipmentService = shipmentService; 43 | this.pollingActivated = pollingActivated; 44 | this.restTemplate = restTemplate; 45 | } 46 | 47 | @Scheduled(fixedDelay = 30000) 48 | @Retry(name = "poller") 49 | public void poll() { 50 | if (pollingActivated) { 51 | pollInternal(); 52 | } 53 | } 54 | 55 | public void pollInternal() { 56 | HttpHeaders requestHeaders = new HttpHeaders(); 57 | requestHeaders.set(HttpHeaders.ACCEPT, "*/*"); 58 | if (lastModified != null) { 59 | requestHeaders.setZonedDateTime(HttpHeaders.IF_MODIFIED_SINCE, lastModified); 60 | } 61 | HttpEntity requestEntity = new HttpEntity(requestHeaders); 62 | ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, OrderFeed.class); 63 | 64 | if (response.getStatusCode() != HttpStatus.NOT_MODIFIED) { 65 | log.trace("data has been modified"); 66 | OrderFeed feed = response.getBody(); 67 | for (OrderFeedEntry entry : feed.getOrders()) { 68 | if ((lastModified == null) || (entry.getUpdated().after(Date.from(lastModified.toInstant())))) { 69 | Shipment shipping = restTemplate.getForEntity(entry.getLink(), Shipment.class).getBody(); 70 | log.trace("saving shipping {}", shipping.getId()); 71 | shipmentService.ship(shipping); 72 | } 73 | } 74 | if (response.getHeaders().getFirst("Last-Modified") != null) { 75 | lastModified = response.getHeaders().getFirstZonedDateTime(HttpHeaders.LAST_MODIFIED); 76 | log.trace("Last-Modified header {}", lastModified); 77 | } 78 | } else { 79 | log.trace("no new data"); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/customer/Customer.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.customer; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Entity; 5 | import jakarta.persistence.GeneratedValue; 6 | import jakarta.persistence.Id; 7 | 8 | @Entity 9 | public class Customer { 10 | 11 | @Id 12 | @GeneratedValue 13 | private Long customerId; 14 | 15 | @Column(nullable = false) 16 | private String name; 17 | 18 | @Column(nullable = false) 19 | private String firstname; 20 | 21 | @Column(nullable = false) 22 | private String email; 23 | 24 | public Customer() { 25 | super(); 26 | customerId = 0l; 27 | } 28 | 29 | public Customer(String firstname, String name, String email, String street, String city) { 30 | super(); 31 | this.name = name; 32 | this.firstname = firstname; 33 | this.email = email; 34 | } 35 | 36 | public String getEmail() { 37 | return email; 38 | } 39 | 40 | public void setEmail(String email) { 41 | this.email = email; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public String getFirstname() { 53 | return firstname; 54 | } 55 | 56 | public void setFirstname(String firstname) { 57 | this.firstname = firstname; 58 | } 59 | 60 | public Long getCustomerId() { 61 | return customerId; 62 | } 63 | 64 | public void setCustomerId(Long id) { 65 | this.customerId = id; 66 | } 67 | 68 | @Override 69 | public int hashCode() { 70 | final int prime = 31; 71 | int result = 1; 72 | result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); 73 | result = prime * result + ((email == null) ? 0 : email.hashCode()); 74 | result = prime * result + ((firstname == null) ? 0 : firstname.hashCode()); 75 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 76 | return result; 77 | } 78 | 79 | @Override 80 | public boolean equals(Object obj) { 81 | if (this == obj) 82 | return true; 83 | if (obj == null) 84 | return false; 85 | if (getClass() != obj.getClass()) 86 | return false; 87 | Customer other = (Customer) obj; 88 | if (customerId == null) { 89 | if (other.customerId != null) 90 | return false; 91 | } else if (!customerId.equals(other.customerId)) 92 | return false; 93 | if (email == null) { 94 | if (other.email != null) 95 | return false; 96 | } else if (!email.equals(other.email)) 97 | return false; 98 | if (firstname == null) { 99 | if (other.firstname != null) 100 | return false; 101 | } else if (!firstname.equals(other.firstname)) 102 | return false; 103 | if (name == null) { 104 | if (other.name != null) 105 | return false; 106 | } else if (!name.equals(other.name)) 107 | return false; 108 | return true; 109 | } 110 | 111 | @Override 112 | public String toString() { 113 | return "Customer [customerId=" + customerId + ", name=" + name + ", firstname=" + firstname + ", email=" + email 114 | + "]"; 115 | } 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.ewolff 7 | microservice-spring 8 | 0.0.1-SNAPSHOT 9 | 10 | microservice-spring-order 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-data-jpa 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-thymeleaf 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-test 27 | test 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-actuator 32 | 33 | 34 | io.micrometer 35 | micrometer-registry-prometheus 36 | runtime 37 | 38 | 39 | io.micrometer 40 | micrometer-tracing-bridge-otel 41 | runtime 42 | 43 | 44 | io.opentelemetry 45 | opentelemetry-exporter-zipkin 46 | runtime 47 | 48 | 49 | io.github.resilience4j 50 | resilience4j-spring-boot3 51 | ${resilience4j.version} 52 | 53 | 54 | io.github.resilience4j 55 | resilience4j-all 56 | ${resilience4j.version} 57 | 58 | 59 | 60 | org.hsqldb 61 | hsqldb 62 | test 63 | 64 | 65 | org.postgresql 66 | postgresql 67 | 68 | 69 | org.webjars 70 | bootstrap 71 | 72 | 73 | org.glassfish.jaxb 74 | jaxb-runtime 75 | provided 76 | 77 | 78 | org.junit.jupiter 79 | junit-jupiter-api 80 | test 81 | 82 | 83 | org.junit.jupiter 84 | junit-jupiter-engine 85 | test 86 | 87 | 88 | au.com.dius.pact.provider 89 | junit5spring 90 | 4.4.2 91 | test 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.ewolff 7 | microservice-spring 8 | 0.0.1-SNAPSHOT 9 | 10 | microservice-spring-shipping 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-data-jpa 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-thymeleaf 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-test 27 | test 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-actuator 32 | 33 | 34 | io.micrometer 35 | micrometer-registry-prometheus 36 | runtime 37 | 38 | 39 | io.micrometer 40 | micrometer-tracing-bridge-otel 41 | runtime 42 | 43 | 44 | io.opentelemetry 45 | opentelemetry-exporter-zipkin 46 | runtime 47 | 48 | 49 | io.github.resilience4j 50 | resilience4j-spring-boot3 51 | ${resilience4j.version} 52 | 53 | 54 | io.github.resilience4j 55 | resilience4j-all 56 | ${resilience4j.version} 57 | 58 | 59 | org.hsqldb 60 | hsqldb 61 | test 62 | 63 | 64 | org.postgresql 65 | postgresql 66 | 67 | 68 | org.webjars 69 | bootstrap 70 | 71 | 72 | org.glassfish.jaxb 73 | jaxb-runtime 74 | provided 75 | 76 | 77 | org.junit.jupiter 78 | junit-jupiter-api 79 | test 80 | 81 | 82 | org.junit.jupiter 83 | junit-jupiter-engine 84 | test 85 | 86 | 87 | au.com.dius.pact.consumer 88 | junit5 89 | 4.4.2 90 | test 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.ewolff 7 | microservice-spring 8 | 0.0.1-SNAPSHOT 9 | 10 | microservice-spring-invoicing 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-data-jpa 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-thymeleaf 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-test 27 | test 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-actuator 32 | 33 | 34 | io.micrometer 35 | micrometer-registry-prometheus 36 | runtime 37 | 38 | 39 | io.micrometer 40 | micrometer-tracing-bridge-otel 41 | runtime 42 | 43 | 44 | io.opentelemetry 45 | opentelemetry-exporter-zipkin 46 | runtime 47 | 48 | 49 | io.github.resilience4j 50 | resilience4j-spring-boot3 51 | ${resilience4j.version} 52 | 53 | 54 | io.github.resilience4j 55 | resilience4j-all 56 | ${resilience4j.version} 57 | 58 | 59 | 60 | org.hsqldb 61 | hsqldb 62 | test 63 | 64 | 65 | org.postgresql 66 | postgresql 67 | 68 | 69 | org.webjars 70 | bootstrap 71 | 72 | 73 | org.glassfish.jaxb 74 | jaxb-runtime 75 | provided 76 | 77 | 78 | org.junit.jupiter 79 | junit-jupiter-api 80 | test 81 | 82 | 83 | org.junit.jupiter 84 | junit-jupiter-engine 85 | test 86 | 87 | 88 | au.com.dius.pact.consumer 89 | junit5 90 | 4.4.2 91 | test 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/FeedClientTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.time.ZonedDateTime; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 13 | import org.springframework.boot.test.web.server.LocalServerPort; 14 | import org.springframework.data.domain.Sort; 15 | import org.springframework.http.HttpEntity; 16 | import org.springframework.http.HttpHeaders; 17 | import org.springframework.http.HttpMethod; 18 | import org.springframework.http.HttpStatus; 19 | import org.springframework.http.ResponseEntity; 20 | import org.springframework.test.context.ActiveProfiles; 21 | import org.springframework.web.client.RestTemplate; 22 | 23 | import com.ewolff.microservice.order.customer.CustomerRepository; 24 | import com.ewolff.microservice.order.logic.Order; 25 | import com.ewolff.microservice.order.logic.OrderRepository; 26 | 27 | @SpringBootTest(classes = OrderApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 28 | @ActiveProfiles("test") 29 | class FeedClientTest { 30 | 31 | @LocalServerPort 32 | private long serverPort; 33 | 34 | @Autowired 35 | private OrderRepository orderRepository; 36 | 37 | @Autowired 38 | private CustomerRepository customerRepository; 39 | 40 | private RestTemplate restTemplate = new RestTemplate(); 41 | 42 | @Test 43 | void feedReturnsBasicInformation() { 44 | OrderFeed feed = retrieveFeed(); 45 | assertNotNull(feed.getUpdated()); 46 | } 47 | 48 | @Test 49 | void requestWithLastModifiedReturns304() { 50 | ResponseEntity response = restTemplate.exchange(feedUrl(), HttpMethod.GET, new HttpEntity(null), 51 | OrderFeed.class); 52 | 53 | ZonedDateTime lastModified = response.getHeaders().getFirstZonedDateTime("Last-Modified"); 54 | 55 | HttpHeaders requestHeaders = new HttpHeaders(); 56 | requestHeaders.setZonedDateTime("If-Modified-Since", lastModified); 57 | HttpEntity requestEntity = new HttpEntity(requestHeaders); 58 | 59 | response = restTemplate.exchange(feedUrl(), HttpMethod.GET, requestEntity, OrderFeed.class); 60 | 61 | assertEquals(HttpStatus.NOT_MODIFIED, response.getStatusCode()); 62 | } 63 | 64 | @Test 65 | void feedReturnsNewlyCreatedOrder() { 66 | Order order = new Order(); 67 | order.setCustomer(customerRepository.findAll(Sort.unsorted()).iterator().next()); 68 | orderRepository.save(order); 69 | OrderFeed feed = retrieveFeed(); 70 | boolean foundLinkToCreatedOrder = false; 71 | for (OrderFeedEntry entry : feed.getOrders()) { 72 | if (entry.getLink().contains(Long.toString(order.getId()))) { 73 | foundLinkToCreatedOrder = true; 74 | } 75 | } 76 | assertTrue(foundLinkToCreatedOrder); 77 | } 78 | 79 | private OrderFeed retrieveFeed() { 80 | return restTemplate.getForEntity(feedUrl(), OrderFeed.class).getBody(); 81 | } 82 | 83 | private String feedUrl() { 84 | return "http://localhost:%d/feed".formatted(serverPort); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: spring-invoicing 7 | version: "1.0" 8 | name: spring-invoicing 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: spring-invoicing 14 | strategy: {} 15 | template: 16 | metadata: 17 | creationTimestamp: null 18 | labels: 19 | app: spring-invoicing 20 | spec: 21 | containers: 22 | - name: spring-invoicing 23 | image: microservice-spring-invoicing:1 24 | imagePullPolicy: Never 25 | ports: 26 | - containerPort: 8080 27 | resources: {} 28 | status: {} 29 | 30 | --- 31 | 32 | apiVersion: apps/v1 33 | kind: Deployment 34 | metadata: 35 | creationTimestamp: null 36 | labels: 37 | app: spring-shipping 38 | version: "1.0" 39 | name: spring-shipping 40 | spec: 41 | replicas: 1 42 | selector: 43 | matchLabels: 44 | app: spring-shipping 45 | strategy: {} 46 | template: 47 | metadata: 48 | creationTimestamp: null 49 | labels: 50 | app: spring-shipping 51 | spec: 52 | containers: 53 | - name: spring-shipping 54 | image: microservice-spring-shipping:1 55 | imagePullPolicy: Never 56 | ports: 57 | - containerPort: 8080 58 | resources: {} 59 | status: {} 60 | 61 | --- 62 | 63 | apiVersion: apps/v1 64 | kind: Deployment 65 | metadata: 66 | creationTimestamp: null 67 | labels: 68 | app: spring-order 69 | version: "1.0" 70 | name: spring-order 71 | spec: 72 | replicas: 1 73 | selector: 74 | matchLabels: 75 | app: spring-order 76 | strategy: {} 77 | template: 78 | metadata: 79 | creationTimestamp: null 80 | labels: 81 | app: spring-order 82 | spec: 83 | containers: 84 | - name: spring-order 85 | image: microservice-spring-order:1 86 | imagePullPolicy: Never 87 | ports: 88 | - containerPort: 8080 89 | resources: {} 90 | status: {} 91 | 92 | --- 93 | 94 | apiVersion: v1 95 | kind: Service 96 | metadata: 97 | creationTimestamp: null 98 | labels: 99 | app: spring-invoicing 100 | name: spring-invoicing 101 | spec: 102 | ports: 103 | - port: 8080 104 | protocol: TCP 105 | targetPort: 8080 106 | name: http 107 | selector: 108 | app: spring-invoicing 109 | type: NodePort 110 | status: 111 | loadBalancer: {} 112 | 113 | --- 114 | 115 | apiVersion: v1 116 | kind: Service 117 | metadata: 118 | creationTimestamp: null 119 | labels: 120 | app: spring-shipping 121 | name: spring-shipping 122 | spec: 123 | ports: 124 | - port: 8080 125 | protocol: TCP 126 | targetPort: 8080 127 | name: http 128 | selector: 129 | app: spring-shipping 130 | type: NodePort 131 | status: 132 | loadBalancer: {} 133 | 134 | --- 135 | 136 | apiVersion: v1 137 | kind: Service 138 | metadata: 139 | creationTimestamp: null 140 | labels: 141 | app: spring-order 142 | visualize: "true" 143 | name: spring-order 144 | spec: 145 | ports: 146 | - port: 8080 147 | protocol: TCP 148 | targetPort: 8080 149 | name: http 150 | selector: 151 | app: spring-order 152 | type: NodePort 153 | status: 154 | loadBalancer: {} 155 | 156 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/resources/templates/order-full.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Order Full Information 6 | 7 | 8 | 9 |

Order Full Information

10 |
11 | 12 |
13 |
Customer
14 |
15 |
16 | 17 |
18 |
Total price
19 |
20 |
21 | 22 |
23 |

Shipping Address

24 |
25 | 26 |
27 |
Street
28 |
29 |
30 |
31 |
Zip
32 |
33 |
34 |
35 |
City
36 |
37 |
38 | 39 |
40 |

Billing Address

41 |
42 | 43 |
44 |
Street
45 |
46 |
47 |
48 |
Zip
49 |
50 |
51 |
52 |
City
53 |
54 |
55 | 56 |
57 |

Order Lines

58 |
59 | 60 |
61 |
Quantity
62 |
Item
63 |
Price
64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 | 72 |
73 | 74 |
75 |

Invoicing

76 |
77 | 78 |
79 | Invoicing 80 |
81 | 82 |
83 | 84 |
85 |

Shipping

86 |
87 | 88 |
89 | Shipping 90 |
91 | 92 | 102 | 103 |
104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/resources/templates/orderForm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Order : Add 6 | 7 | 8 | 9 |

Order : Add

10 |
11 |
12 |
13 | 18 |
19 |
20 | 25 |
26 |

Shipping Address

27 |
28 | 30 |
31 |
32 | 34 |
35 |
36 | 38 |
39 |

Billing Address

40 |
41 | 43 |
44 |
45 | 47 |
48 |
49 | 51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 |
64 |
65 |
1
66 |
67 | 68 |
69 |
70 | 74 |
75 |
76 |
77 | 78 | 79 |
80 |
81 |
82 | 83 | 84 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import java.util.Optional; 4 | 5 | import org.springframework.data.domain.Sort; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.DeleteMapping; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.servlet.ModelAndView; 15 | 16 | import com.ewolff.microservice.order.customer.CustomerRepository; 17 | import com.ewolff.microservice.order.item.ItemRepository; 18 | 19 | @Controller 20 | class OrderController { 21 | 22 | private OrderRepository orderRepository; 23 | 24 | private OrderService orderService; 25 | 26 | private CustomerRepository customerRepository; 27 | private ItemRepository itemRepository; 28 | 29 | public OrderController(OrderService orderService, OrderRepository orderRepository, 30 | CustomerRepository customerRepository, ItemRepository itemRepository) { 31 | super(); 32 | this.orderRepository = orderRepository; 33 | this.customerRepository = customerRepository; 34 | this.itemRepository = itemRepository; 35 | this.orderService = orderService; 36 | } 37 | 38 | @RequestMapping("/") 39 | public ModelAndView orderList() { 40 | return new ModelAndView("orderlist", "orders", orderRepository.findAll()); 41 | } 42 | 43 | @GetMapping("/form.html") 44 | public ModelAndView form() { 45 | ModelAndView modelAndView = new ModelAndView("orderForm", "order", new Order()); 46 | modelAndView.addObject("items", itemRepository.findAll(Sort.unsorted())); 47 | modelAndView.addObject("customers", customerRepository.findAll(Sort.unsorted())); 48 | return modelAndView; 49 | } 50 | 51 | @PostMapping("/line") 52 | public ModelAndView addLine(Order order) { 53 | order.addLine(0, itemRepository.findAll(Sort.unsorted()).iterator().next()); 54 | ModelAndView modelAndView = new ModelAndView("orderForm", "order", order); 55 | modelAndView.addObject("items", itemRepository.findAll(Sort.unsorted())); 56 | modelAndView.addObject("customers", customerRepository.findAll(Sort.unsorted())); 57 | return modelAndView; 58 | } 59 | 60 | @GetMapping("/{id}") 61 | public ModelAndView get(@PathVariable long id) { 62 | return new ModelAndView("order", "order", orderRepository.findById(id).get()); 63 | } 64 | 65 | @GetMapping("/order/{id}") 66 | public ResponseEntity getJSON(@PathVariable long id) { 67 | Optional response = orderRepository.findById(id); 68 | if (response.isEmpty()) { 69 | return new ResponseEntity(HttpStatus.NOT_FOUND); 70 | } else { 71 | return new ResponseEntity(response.get(), HttpStatus.OK); 72 | } 73 | } 74 | 75 | @GetMapping("/full-{id}") 76 | public ModelAndView full(@PathVariable long id) { 77 | return new ModelAndView("order-full", "order", orderRepository.findById(id).get()); 78 | } 79 | 80 | @PostMapping("/") 81 | public ModelAndView post(Order order) { 82 | order = orderService.order(order); 83 | return new ModelAndView("success"); 84 | } 85 | 86 | @DeleteMapping("/{id}") 87 | public ModelAndView delete(@PathVariable long id) { 88 | orderRepository.deleteById(id); 89 | 90 | return new ModelAndView("success"); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/test/java/com/ewolff/microservice/invoicing/PollingTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.greaterThan; 5 | import static org.hamcrest.Matchers.is; 6 | 7 | import java.util.Date; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 16 | import org.springframework.test.context.ActiveProfiles; 17 | 18 | import com.ewolff.microservice.invoicing.poller.InvoicePoller; 19 | 20 | import au.com.dius.pact.consumer.dsl.DslPart; 21 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 22 | import au.com.dius.pact.consumer.dsl.PactDslWithProvider; 23 | import au.com.dius.pact.consumer.junit5.PactConsumerTestExt; 24 | import au.com.dius.pact.consumer.junit5.PactTestFor; 25 | import au.com.dius.pact.core.model.V4Pact; 26 | import au.com.dius.pact.core.model.annotations.Pact; 27 | 28 | @ExtendWith(PactConsumerTestExt.class) 29 | @SpringBootTest(classes = InvoiceTestApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 30 | @PactTestFor(providerName = "OrderProvider", port = "8081") 31 | @ActiveProfiles("test") 32 | public class PollingTest { 33 | 34 | @Autowired 35 | private InvoiceRepository invoiceRepository; 36 | 37 | @Autowired 38 | private InvoicePoller invoicePoller; 39 | 40 | private DslPart feedBody(Date now) { 41 | return new PactDslJsonBody().date("updated", "yyyy-MM-dd'T'kk:mm:ss.SSS+0000", now) 42 | .eachLike("orders") 43 | .numberType("id", 1) 44 | .stringType("link", "http://localhost:8081/order/1") 45 | .date("updated", "yyyy-MM-dd'T'kk:mm:ss.SSS+0000", now) 46 | .closeArray(); 47 | } 48 | 49 | public DslPart order(Date now) { 50 | return new PactDslJsonBody() 51 | .numberType("id", 1) 52 | .numberType("numberOfLines", 1) 53 | .object("customer") 54 | .numberType("customerId", 1) 55 | .stringType("name", "Wolff") 56 | .stringType("firstname", "Eberhard") 57 | .stringType("email", "eberhard.wolff@posteo.net") 58 | .closeObject() 59 | .object("billingAddress") 60 | .stringType("street", "Krischerstr. 100") 61 | .stringType("zip", "40789") 62 | .stringType("city", "Monheim am Rhein") 63 | .closeObject() 64 | .array("orderLine") 65 | .object() 66 | .numberType("count", 42) 67 | .object("item") 68 | .numberType("itemId", 1) 69 | .stringType("name", "iPod") 70 | .numberType("price", 23) 71 | .closeObject() 72 | .closeArray(); 73 | } 74 | 75 | @Pact(consumer = "Invoice") 76 | public V4Pact createFragment(PactDslWithProvider builder) { 77 | Map headers = new HashMap(); 78 | headers.put("Content-Type", "application/json"); 79 | Date now = new Date(); 80 | return builder .uponReceiving("Request for order feed") 81 | .method("GET") 82 | .path("/feed") 83 | .willRespondWith() 84 | .status(200) 85 | .headers(headers) 86 | .body(feedBody(now)) 87 | .uponReceiving("Request for an order") 88 | .method("GET") 89 | .path("/order/1") 90 | .willRespondWith() 91 | .status(200) 92 | .headers(headers) 93 | .body(order(now)) 94 | .toPact(V4Pact.class); 95 | } 96 | 97 | @Test 98 | void orderArePolled() { 99 | long countBeforePoll = invoiceRepository.count(); 100 | invoicePoller.pollInternal(); 101 | assertThat(invoiceRepository.count(), is(greaterThan(countBeforePoll))); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/test/java/com/ewolff/microservice/shipping/PollingTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.Matchers.greaterThan; 5 | import static org.hamcrest.Matchers.is; 6 | 7 | import java.util.Date; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 16 | import org.springframework.test.context.ActiveProfiles; 17 | 18 | import com.ewolff.microservice.shipping.poller.ShippingPoller; 19 | 20 | import au.com.dius.pact.consumer.dsl.DslPart; 21 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 22 | import au.com.dius.pact.consumer.dsl.PactDslWithProvider; 23 | import au.com.dius.pact.consumer.junit5.PactConsumerTestExt; 24 | import au.com.dius.pact.consumer.junit5.PactTestFor; 25 | import au.com.dius.pact.core.model.V4Pact; 26 | import au.com.dius.pact.core.model.annotations.Pact; 27 | 28 | @ExtendWith(PactConsumerTestExt.class) 29 | @SpringBootTest(classes = ShippingTestApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 30 | @PactTestFor(providerName = "OrderProvider", port = "8081") 31 | @ActiveProfiles("test") 32 | public class PollingTest { 33 | 34 | @Autowired 35 | private ShipmentRepository shipmentRepository; 36 | 37 | @Autowired 38 | private ShippingPoller shippingPoller; 39 | 40 | private DslPart feedBody(Date now) { 41 | return new PactDslJsonBody().date("updated", "yyyy-MM-dd'T'kk:mm:ss.SSS+0000", now) 42 | .eachLike("orders") 43 | .numberType("id", 1) 44 | .stringType("link", "http://localhost:8081/order/1") 45 | .date("updated", "yyyy-MM-dd'T'kk:mm:ss.SSS+0000", now) 46 | .closeArray(); 47 | } 48 | 49 | public DslPart order(Date now) { 50 | return new PactDslJsonBody().numberType("id", 1) 51 | .numberType("numberOfLines", 1) 52 | .stringType("deliveryService", "Hermes") 53 | .object("customer") 54 | .numberType("customerId", 1) 55 | .stringType("name", "Wolff") 56 | .stringType("firstname", "Eberhard") 57 | .stringType("email", "eberhard.wolff@posteo.net") 58 | .closeObject() 59 | .object("shippingAddress") 60 | .stringType("street", "Krischerstr. 100") 61 | .stringType("zip", "40789") 62 | .stringType("city", "Monheim am Rhein") 63 | .closeObject() 64 | .array("orderLine") 65 | .object() 66 | .numberType("count", 42) 67 | .object("item") 68 | .numberType("itemId", 1) 69 | .stringType("name", "iPod") 70 | .closeObject() 71 | .closeArray(); 72 | } 73 | 74 | @Pact(consumer = "Shipping") 75 | public V4Pact createFragment(PactDslWithProvider builder) { 76 | Map headers = new HashMap(); 77 | headers.put("Content-Type", "application/json"); 78 | Date now = new Date(); 79 | return builder .uponReceiving("Request for order feed") 80 | .method("GET") 81 | .path("/feed") 82 | .willRespondWith() 83 | .status(200) 84 | .headers(headers) 85 | .body(feedBody(now)) 86 | .uponReceiving("Request for an order") 87 | .method("GET") 88 | .path("/order/1") 89 | .willRespondWith() 90 | .status(200) 91 | .headers(headers) 92 | .body(order(now)) 93 | .toPact(V4Pact.class); 94 | } 95 | 96 | @Test 97 | void orderArePolled() { 98 | long countBeforePoll = shipmentRepository.count(); 99 | shippingPoller.pollInternal(); 100 | assertThat(shipmentRepository.count(), is(greaterThan(countBeforePoll))); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-invoicing/src/main/java/com/ewolff/microservice/invoicing/Invoice.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.invoicing; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | import jakarta.persistence.CascadeType; 8 | import jakarta.persistence.Embedded; 9 | import jakarta.persistence.Entity; 10 | import jakarta.persistence.Id; 11 | import jakarta.persistence.OneToMany; 12 | import jakarta.persistence.Transient; 13 | 14 | @Entity 15 | public class Invoice { 16 | 17 | @Id 18 | private long id; 19 | 20 | @Embedded 21 | private Customer customer; 22 | 23 | private Date updated; 24 | 25 | @Embedded 26 | private Address billingAddress = new Address(); 27 | 28 | @OneToMany(cascade = CascadeType.ALL) 29 | private List invoiceLine; 30 | 31 | public Invoice() { 32 | super(); 33 | invoiceLine = new ArrayList(); 34 | } 35 | 36 | public Invoice(long id, Customer customer, Date updated, Address billingAddress, List invoiceLine) { 37 | super(); 38 | this.id = id; 39 | this.customer = customer; 40 | this.updated = updated; 41 | this.billingAddress = billingAddress; 42 | this.invoiceLine = invoiceLine; 43 | } 44 | 45 | public Address getBillingAddress() { 46 | return billingAddress; 47 | } 48 | 49 | public void setBillingAddress(Address billingAddress) { 50 | this.billingAddress = billingAddress; 51 | } 52 | 53 | public void setId(long id) { 54 | this.id = id; 55 | } 56 | 57 | public long getId() { 58 | return id; 59 | } 60 | 61 | public Date getUpdated() { 62 | return updated; 63 | } 64 | 65 | public void setUpdated(Date created) { 66 | this.updated = created; 67 | } 68 | 69 | public Customer getCustomer() { 70 | return customer; 71 | } 72 | 73 | public void setCustomer(Customer customer) { 74 | this.customer = customer; 75 | } 76 | 77 | public List getInvoiceLine() { 78 | return invoiceLine; 79 | } 80 | 81 | public void setInvoiceLine(List invoiceLine) { 82 | this.invoiceLine = invoiceLine; 83 | } 84 | 85 | @Transient 86 | public void setOrderLine(List orderLine) { 87 | this.invoiceLine = orderLine; 88 | } 89 | 90 | public void addLine(int count, Item item) { 91 | this.invoiceLine.add(new InvoiceLine(count, item)); 92 | } 93 | 94 | public int getNumberOfLines() { 95 | return invoiceLine.size(); 96 | } 97 | 98 | public double totalAmount() { 99 | return invoiceLine.stream().map((ol) -> ol.getCount() * ol.getItem().getPrice()).reduce(0.0, 100 | (d1, d2) -> d1 + d2); 101 | } 102 | 103 | @Override 104 | public int hashCode() { 105 | final int prime = 31; 106 | int result = 1; 107 | result = prime * result + ((billingAddress == null) ? 0 : billingAddress.hashCode()); 108 | result = prime * result + ((customer == null) ? 0 : customer.hashCode()); 109 | result = prime * result + (int) (id ^ (id >>> 32)); 110 | result = prime * result + ((invoiceLine == null) ? 0 : invoiceLine.hashCode()); 111 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 112 | return result; 113 | } 114 | 115 | @Override 116 | public boolean equals(Object obj) { 117 | if (this == obj) 118 | return true; 119 | if (obj == null) 120 | return false; 121 | if (getClass() != obj.getClass()) 122 | return false; 123 | Invoice other = (Invoice) obj; 124 | if (billingAddress == null) { 125 | if (other.billingAddress != null) 126 | return false; 127 | } else if (!billingAddress.equals(other.billingAddress)) 128 | return false; 129 | if (customer == null) { 130 | if (other.customer != null) 131 | return false; 132 | } else if (!customer.equals(other.customer)) 133 | return false; 134 | if (id != other.id) 135 | return false; 136 | if (invoiceLine == null) { 137 | if (other.invoiceLine != null) 138 | return false; 139 | } else if (!invoiceLine.equals(other.invoiceLine)) 140 | return false; 141 | if (updated == null) { 142 | if (other.updated != null) 143 | return false; 144 | } else if (!updated.equals(other.updated)) 145 | return false; 146 | return true; 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return "Invoice [id=" + id + ", customer=" + customer + ", updated=" + updated + ", billingAddress=" 152 | + billingAddress + ", invoiceLine=" + invoiceLine + "]"; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/java/com/ewolff/microservice/order/logic/OrderWebIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.util.stream.StreamSupport; 8 | 9 | import org.junit.jupiter.api.BeforeAll; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.TestInstance; 12 | import org.junit.jupiter.api.TestInstance.Lifecycle; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 16 | import org.springframework.boot.test.web.server.LocalServerPort; 17 | import org.springframework.data.domain.Sort; 18 | import org.springframework.http.ResponseEntity; 19 | import org.springframework.test.context.ActiveProfiles; 20 | import org.springframework.util.LinkedMultiValueMap; 21 | import org.springframework.util.MultiValueMap; 22 | import org.springframework.web.client.RestTemplate; 23 | 24 | import com.ewolff.microservice.order.OrderApp; 25 | import com.ewolff.microservice.order.customer.Customer; 26 | import com.ewolff.microservice.order.customer.CustomerRepository; 27 | import com.ewolff.microservice.order.item.Item; 28 | import com.ewolff.microservice.order.item.ItemRepository; 29 | 30 | @SpringBootTest(classes = OrderApp.class, webEnvironment = WebEnvironment.DEFINED_PORT) 31 | @ActiveProfiles("test") 32 | @TestInstance(Lifecycle.PER_CLASS) 33 | class OrderWebIntegrationTest { 34 | 35 | private RestTemplate restTemplate = new RestTemplate(); 36 | 37 | @LocalServerPort 38 | private long serverPort; 39 | 40 | @Autowired 41 | private ItemRepository itemRepository; 42 | 43 | @Autowired 44 | private CustomerRepository customerRepository; 45 | 46 | @Autowired 47 | private OrderRepository orderRepository; 48 | 49 | private Item item; 50 | 51 | private Customer customer; 52 | 53 | @BeforeAll 54 | void setup() { 55 | item = itemRepository.findAll(Sort.unsorted()).iterator().next(); 56 | customer = new Customer("RZA", "GZA", "rza@wutang.com", "Chamber", "Shaolin"); 57 | customer = customerRepository.save(customer); 58 | } 59 | 60 | @Test 61 | void IsTestOrderReturned() { 62 | ResponseEntity resultEntity = restTemplate.getForEntity(orderURL() + "/order/1", String.class); 63 | assertTrue(resultEntity.getStatusCode().is2xxSuccessful()); 64 | String order = resultEntity.getBody(); 65 | assertTrue(order.contains("Berlin")); 66 | } 67 | 68 | @Test 69 | void IsOrderListReturned() { 70 | Order order = null; 71 | try { 72 | Iterable orders = orderRepository.findAll(); 73 | assertTrue(StreamSupport.stream(orders.spliterator(), false) 74 | .noneMatch(o -> ((o.getCustomer() != null) && (o.getCustomer().equals(customer))))); 75 | ResponseEntity resultEntity = restTemplate.getForEntity(orderURL(), String.class); 76 | assertTrue(resultEntity.getStatusCode().is2xxSuccessful()); 77 | String orderList = resultEntity.getBody(); 78 | assertFalse(orderList.contains("RZA")); 79 | order = new Order(customer); 80 | order.addLine(42, item); 81 | orderRepository.save(order); 82 | orderList = restTemplate.getForObject(orderURL(), String.class); 83 | assertTrue(orderList.contains("Eberhard")); 84 | } finally { 85 | if (order != null) { 86 | orderRepository.delete(order); 87 | } 88 | } 89 | } 90 | 91 | private String orderURL() { 92 | return "http://localhost:" + serverPort; 93 | } 94 | 95 | @Test 96 | void IsOrderFormDisplayed() { 97 | ResponseEntity resultEntity = restTemplate.getForEntity(orderURL() + "/form.html", String.class); 98 | assertTrue(resultEntity.getStatusCode().is2xxSuccessful()); 99 | assertTrue(resultEntity.getBody().contains(" map = new LinkedMultiValueMap(); 106 | map.add("submit", ""); 107 | map.add("customer", Long.toString(customer.getCustomerId())); 108 | map.add("orderLine[0].item", Long.toString(item.getItemId())); 109 | map.add("orderLine[0].count", "42"); 110 | restTemplate.postForLocation(orderURL(), map, String.class); 111 | assertEquals(before + 1, orderRepository.count()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-shipping/src/main/java/com/ewolff/microservice/shipping/Shipment.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.shipping; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | import jakarta.persistence.CascadeType; 8 | import jakarta.persistence.Embedded; 9 | import jakarta.persistence.Entity; 10 | import jakarta.persistence.Id; 11 | import jakarta.persistence.OneToMany; 12 | import jakarta.persistence.Transient; 13 | 14 | @Entity 15 | public class Shipment { 16 | 17 | @Id 18 | private long id; 19 | 20 | @Embedded 21 | private Customer customer; 22 | 23 | private Date updated; 24 | 25 | @Embedded 26 | private Address shippingAddress = new Address(); 27 | 28 | @OneToMany(cascade = CascadeType.ALL) 29 | private List shipmentLine; 30 | 31 | private String deliveryService; 32 | 33 | private int cost; 34 | 35 | public Shipment() { 36 | super(); 37 | shipmentLine = new ArrayList(); 38 | } 39 | 40 | public Shipment(long id, Customer customer, Date updated, Address shippingAddress, List shipmentLine, 41 | String deliveryService) { 42 | super(); 43 | this.id = id; 44 | this.customer = customer; 45 | this.updated = updated; 46 | this.shippingAddress = shippingAddress; 47 | this.shipmentLine = shipmentLine; 48 | this.deliveryService = deliveryService; 49 | } 50 | 51 | public Address getShippingAddress() { 52 | return shippingAddress; 53 | } 54 | 55 | public void setShippingAddress(Address shippingAddress) { 56 | this.shippingAddress = shippingAddress; 57 | } 58 | 59 | public String getDeliveryService() { 60 | return deliveryService; 61 | } 62 | 63 | public void setDeliveryService(String deliveryService) { 64 | this.deliveryService = deliveryService; 65 | } 66 | 67 | public void setId(long id) { 68 | this.id = id; 69 | } 70 | 71 | public long getId() { 72 | return id; 73 | } 74 | 75 | public Date getUpdated() { 76 | return updated; 77 | } 78 | 79 | public void setUpdated(Date created) { 80 | this.updated = created; 81 | } 82 | 83 | public Customer getCustomer() { 84 | return customer; 85 | } 86 | 87 | public void setCustomer(Customer customerId) { 88 | this.customer = customerId; 89 | } 90 | 91 | public List getShipmentLine() { 92 | return shipmentLine; 93 | } 94 | 95 | public Shipment(Customer customer) { 96 | super(); 97 | this.customer = customer; 98 | this.shipmentLine = new ArrayList(); 99 | } 100 | 101 | public void setShipmentLine(List shipmentLine) { 102 | this.shipmentLine = shipmentLine; 103 | } 104 | 105 | @Transient 106 | public void setOrderLine(List orderLine) { 107 | this.shipmentLine = orderLine; 108 | } 109 | 110 | public int getNumberOfLines() { 111 | return shipmentLine.size(); 112 | } 113 | 114 | public void setCost(int cost) { 115 | this.cost = cost; 116 | } 117 | 118 | public int getCost() { 119 | return cost; 120 | } 121 | 122 | public void calculateShippingCost() { 123 | if (getDeliveryService().equalsIgnoreCase("DHL")) { 124 | setCost(1); 125 | } else if (getDeliveryService().equalsIgnoreCase("Hermes")) { 126 | setCost(2); 127 | } else { 128 | throw new IllegalArgumentException("Unknown Delivery Service!"); 129 | } 130 | } 131 | 132 | @Override 133 | public int hashCode() { 134 | final int prime = 31; 135 | int result = 1; 136 | result = prime * result + cost; 137 | result = prime * result + ((customer == null) ? 0 : customer.hashCode()); 138 | result = prime * result + ((deliveryService == null) ? 0 : deliveryService.hashCode()); 139 | result = prime * result + (int) (id ^ (id >>> 32)); 140 | result = prime * result + ((shipmentLine == null) ? 0 : shipmentLine.hashCode()); 141 | result = prime * result + ((shippingAddress == null) ? 0 : shippingAddress.hashCode()); 142 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 143 | return result; 144 | } 145 | 146 | @Override 147 | public boolean equals(Object obj) { 148 | if (this == obj) 149 | return true; 150 | if (obj == null) 151 | return false; 152 | if (getClass() != obj.getClass()) 153 | return false; 154 | Shipment other = (Shipment) obj; 155 | if (cost != other.cost) 156 | return false; 157 | if (customer == null) { 158 | if (other.customer != null) 159 | return false; 160 | } else if (!customer.equals(other.customer)) 161 | return false; 162 | if (deliveryService == null) { 163 | if (other.deliveryService != null) 164 | return false; 165 | } else if (!deliveryService.equals(other.deliveryService)) 166 | return false; 167 | if (id != other.id) 168 | return false; 169 | if (shipmentLine == null) { 170 | if (other.shipmentLine != null) 171 | return false; 172 | } else if (!shipmentLine.equals(other.shipmentLine)) 173 | return false; 174 | if (shippingAddress == null) { 175 | if (other.shippingAddress != null) 176 | return false; 177 | } else if (!shippingAddress.equals(other.shippingAddress)) 178 | return false; 179 | if (updated == null) { 180 | if (other.updated != null) 181 | return false; 182 | } else if (!updated.equals(other.updated)) 183 | return false; 184 | return true; 185 | } 186 | 187 | @Override 188 | public String toString() { 189 | return "Shipment [id=" + id + ", customer=" + customer + ", updated=" + updated + ", shippingAddress=" 190 | + shippingAddress + ", shipmentLine=" + shipmentLine + ", deliveryService=" + deliveryService 191 | + ", cost=" + cost + "]"; 192 | } 193 | 194 | 195 | } 196 | -------------------------------------------------------------------------------- /microservice-spring-demo/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% 146 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/main/java/com/ewolff/microservice/order/logic/Order.java: -------------------------------------------------------------------------------- 1 | package com.ewolff.microservice.order.logic; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.UUID; 7 | 8 | import com.ewolff.microservice.order.customer.Customer; 9 | import com.ewolff.microservice.order.item.Item; 10 | 11 | import jakarta.persistence.AttributeOverride; 12 | import jakarta.persistence.AttributeOverrides; 13 | import jakarta.persistence.CascadeType; 14 | import jakarta.persistence.Column; 15 | import jakarta.persistence.Embedded; 16 | import jakarta.persistence.Entity; 17 | import jakarta.persistence.Id; 18 | import jakarta.persistence.ManyToOne; 19 | import jakarta.persistence.OneToMany; 20 | import jakarta.persistence.Table; 21 | 22 | @Entity 23 | @Table(name = "ORDERTABLE") 24 | public class Order { 25 | 26 | @Id 27 | private long id; 28 | 29 | @ManyToOne 30 | private Customer customer; 31 | 32 | private String deliveryService; 33 | 34 | private Date updated; 35 | 36 | @Embedded 37 | @AttributeOverrides({ @AttributeOverride(name = "street", column = @Column(name = "SHIPPING_STREET")), 38 | @AttributeOverride(name = "zip", column = @Column(name = "SHIPPING_ZIP")), 39 | @AttributeOverride(name = "city", column = @Column(name = "SHIPPING_CITY")) }) 40 | private Address shippingAddress = new Address(); 41 | 42 | @Embedded 43 | @AttributeOverrides({ @AttributeOverride(name = "street", column = @Column(name = "BILLING_STREET")), 44 | @AttributeOverride(name = "zip", column = @Column(name = "BILLING_ZIP")), 45 | @AttributeOverride(name = "city", column = @Column(name = "BILLING_CITY")) }) 46 | private Address billingAddress = new Address(); 47 | 48 | @OneToMany(cascade = CascadeType.ALL) 49 | private List orderLine; 50 | 51 | private static long generateID() { 52 | UUID uuid = UUID.randomUUID(); 53 | return Math.abs(uuid.getLeastSignificantBits()); 54 | } 55 | 56 | public Order() { 57 | this(generateID()); 58 | } 59 | 60 | public Order(long id) { 61 | super(); 62 | this.id = id; 63 | orderLine = new ArrayList(); 64 | updated(); 65 | } 66 | 67 | public Order(Customer customer) { 68 | this(customer,generateID()); 69 | } 70 | 71 | public Order(Customer customer, long id) { 72 | super(); 73 | this.customer = customer; 74 | this.orderLine = new ArrayList(); 75 | this.id = id; 76 | updated(); 77 | } 78 | 79 | private void updated() { 80 | updated = new Date(); 81 | } 82 | 83 | public String getDeliveryService() { 84 | return deliveryService; 85 | } 86 | 87 | public void setDeliveryService(String deliveryService) { 88 | this.deliveryService = deliveryService; 89 | } 90 | 91 | public Address getShippingAddress() { 92 | return shippingAddress; 93 | } 94 | 95 | public void setShippingAddress(Address shippingAddress) { 96 | updated(); 97 | this.shippingAddress = shippingAddress; 98 | } 99 | 100 | public Address getBillingAddress() { 101 | return billingAddress; 102 | } 103 | 104 | public void setBillingAddress(Address billingAddress) { 105 | updated(); 106 | this.billingAddress = billingAddress; 107 | } 108 | 109 | public void setId(long id) { 110 | this.id = id; 111 | } 112 | 113 | public long getId() { 114 | return id; 115 | } 116 | 117 | public Date getUpdated() { 118 | return updated; 119 | } 120 | 121 | public void setUpdated(Date created) { 122 | this.updated = created; 123 | } 124 | 125 | public Customer getCustomer() { 126 | return customer; 127 | } 128 | 129 | public void setCustomer(Customer customer) { 130 | this.customer = customer; 131 | updated(); 132 | } 133 | 134 | public List getOrderLine() { 135 | return orderLine; 136 | } 137 | 138 | public void setOrderLine(List orderLine) { 139 | this.orderLine = orderLine; 140 | updated(); 141 | } 142 | 143 | public void addLine(int count, Item item) { 144 | this.orderLine.add(new OrderLine(count, item)); 145 | updated(); 146 | } 147 | 148 | public int getNumberOfLines() { 149 | return orderLine.size(); 150 | } 151 | 152 | public double totalPrice() { 153 | return orderLine.stream().map((ol) -> ol.getCount() * ol.getItem().getPrice()).reduce(0.0, (d1, d2) -> d1 + d2); 154 | } 155 | 156 | @Override 157 | public int hashCode() { 158 | final int prime = 31; 159 | int result = 1; 160 | result = prime * result + ((billingAddress == null) ? 0 : billingAddress.hashCode()); 161 | result = prime * result + ((customer == null) ? 0 : customer.hashCode()); 162 | result = prime * result + ((deliveryService == null) ? 0 : deliveryService.hashCode()); 163 | result = prime * result + (int) (id ^ (id >>> 32)); 164 | result = prime * result + ((orderLine == null) ? 0 : orderLine.hashCode()); 165 | result = prime * result + ((shippingAddress == null) ? 0 : shippingAddress.hashCode()); 166 | result = prime * result + ((updated == null) ? 0 : updated.hashCode()); 167 | return result; 168 | } 169 | 170 | @Override 171 | public boolean equals(Object obj) { 172 | if (this == obj) 173 | return true; 174 | if (obj == null) 175 | return false; 176 | if (getClass() != obj.getClass()) 177 | return false; 178 | Order other = (Order) obj; 179 | if (billingAddress == null) { 180 | if (other.billingAddress != null) 181 | return false; 182 | } else if (!billingAddress.equals(other.billingAddress)) 183 | return false; 184 | if (customer == null) { 185 | if (other.customer != null) 186 | return false; 187 | } else if (!customer.equals(other.customer)) 188 | return false; 189 | if (deliveryService == null) { 190 | if (other.deliveryService != null) 191 | return false; 192 | } else if (!deliveryService.equals(other.deliveryService)) 193 | return false; 194 | if (id != other.id) 195 | return false; 196 | if (orderLine == null) { 197 | if (other.orderLine != null) 198 | return false; 199 | } else if (!orderLine.equals(other.orderLine)) 200 | return false; 201 | if (shippingAddress == null) { 202 | if (other.shippingAddress != null) 203 | return false; 204 | } else if (!shippingAddress.equals(other.shippingAddress)) 205 | return false; 206 | if (updated == null) { 207 | if (other.updated != null) 208 | return false; 209 | } else if (!updated.equals(other.updated)) 210 | return false; 211 | return true; 212 | } 213 | 214 | @Override 215 | public String toString() { 216 | return "Order [id=" + id + ", customer=" + customer + ", deliveryService=" + deliveryService + ", updated=" 217 | + updated + ", shippingAddress=" + shippingAddress + ", billingAddress=" + billingAddress 218 | + ", orderLine=" + orderLine + "]"; 219 | } 220 | 221 | 222 | 223 | } 224 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/resources/pacts/Invoice-OrderProvider.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": { 3 | "name": "OrderProvider" 4 | }, 5 | "consumer": { 6 | "name": "Invoice" 7 | }, 8 | "interactions": [ 9 | { 10 | "description": "Request for an order", 11 | "request": { 12 | "method": "GET", 13 | "path": "/order/1" 14 | }, 15 | "response": { 16 | "status": 200, 17 | "headers": { 18 | "Content-Type": "application/json" 19 | }, 20 | "body": { 21 | "numberOfLines": 1, 22 | "orderLine": [ 23 | { 24 | "item": { 25 | "itemId": 1, 26 | "price": 23, 27 | "name": "iPod" 28 | }, 29 | "count": 42 30 | } 31 | ], 32 | "id": 1, 33 | "billingAddress": { 34 | "zip": "40789", 35 | "city": "Monheim am Rhein", 36 | "street": "Krischerstr. 100" 37 | }, 38 | "customer": { 39 | "firstname": "Eberhard", 40 | "customerId": 1, 41 | "name": "Wolff", 42 | "email": "eberhard.wolff@posteo.net" 43 | } 44 | }, 45 | "matchingRules": { 46 | "body": { 47 | "$.id": { 48 | "matchers": [ 49 | { 50 | "match": "number" 51 | } 52 | ], 53 | "combine": "AND" 54 | }, 55 | "$.numberOfLines": { 56 | "matchers": [ 57 | { 58 | "match": "number" 59 | } 60 | ], 61 | "combine": "AND" 62 | }, 63 | "$.customer.customerId": { 64 | "matchers": [ 65 | { 66 | "match": "number" 67 | } 68 | ], 69 | "combine": "AND" 70 | }, 71 | "$.customer.name": { 72 | "matchers": [ 73 | { 74 | "match": "type" 75 | } 76 | ], 77 | "combine": "AND" 78 | }, 79 | "$.customer.firstname": { 80 | "matchers": [ 81 | { 82 | "match": "type" 83 | } 84 | ], 85 | "combine": "AND" 86 | }, 87 | "$.customer.email": { 88 | "matchers": [ 89 | { 90 | "match": "type" 91 | } 92 | ], 93 | "combine": "AND" 94 | }, 95 | "$.billingAddress.street": { 96 | "matchers": [ 97 | { 98 | "match": "type" 99 | } 100 | ], 101 | "combine": "AND" 102 | }, 103 | "$.billingAddress.zip": { 104 | "matchers": [ 105 | { 106 | "match": "type" 107 | } 108 | ], 109 | "combine": "AND" 110 | }, 111 | "$.billingAddress.city": { 112 | "matchers": [ 113 | { 114 | "match": "type" 115 | } 116 | ], 117 | "combine": "AND" 118 | }, 119 | "$.orderLine[0].count": { 120 | "matchers": [ 121 | { 122 | "match": "number" 123 | } 124 | ], 125 | "combine": "AND" 126 | }, 127 | "$.orderLine[0].item.itemId": { 128 | "matchers": [ 129 | { 130 | "match": "number" 131 | } 132 | ], 133 | "combine": "AND" 134 | }, 135 | "$.orderLine[0].item.name": { 136 | "matchers": [ 137 | { 138 | "match": "type" 139 | } 140 | ], 141 | "combine": "AND" 142 | }, 143 | "$.orderLine[0].item.price": { 144 | "matchers": [ 145 | { 146 | "match": "number" 147 | } 148 | ], 149 | "combine": "AND" 150 | } 151 | } 152 | } 153 | } 154 | }, 155 | { 156 | "description": "Request for order feed", 157 | "request": { 158 | "method": "GET", 159 | "path": "/feed" 160 | }, 161 | "response": { 162 | "status": 200, 163 | "headers": { 164 | "Content-Type": "application/json" 165 | }, 166 | "body": { 167 | "orders": [ 168 | { 169 | "link": "http://localhost:8081/order/1", 170 | "id": 1, 171 | "updated": "2019-05-20T11:49:22.111+00:00" 172 | } 173 | ], 174 | "updated": "2019-05-20T11:49:22.111+00:00" 175 | }, 176 | "matchingRules": { 177 | "body": { 178 | "$.updated": { 179 | "matchers": [ 180 | { 181 | "match": "date", 182 | "date": "yyyy-MM-dd\u0027T\u0027kk:mm:ss.SSS+00:00" 183 | } 184 | ], 185 | "combine": "AND" 186 | }, 187 | "$.orders": { 188 | "matchers": [ 189 | { 190 | "match": "type", 191 | "min": 0 192 | } 193 | ], 194 | "combine": "AND" 195 | }, 196 | "$.orders[*].id": { 197 | "matchers": [ 198 | { 199 | "match": "number" 200 | } 201 | ], 202 | "combine": "AND" 203 | }, 204 | "$.orders[*].link": { 205 | "matchers": [ 206 | { 207 | "match": "type" 208 | } 209 | ], 210 | "combine": "AND" 211 | }, 212 | "$.orders[*].updated": { 213 | "matchers": [ 214 | { 215 | "match": "date", 216 | "date": "yyyy-MM-dd\u0027T\u0027kk:mm:ss.SSS+00:00" 217 | } 218 | ], 219 | "combine": "AND" 220 | } 221 | } 222 | } 223 | } 224 | } 225 | ], 226 | "metadata": { 227 | "pactSpecification": { 228 | "version": "3.0.0" 229 | }, 230 | "pact-jvm": { 231 | "version": "4.0.0-beta.2" 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/resources/pacts/Shipping-OrderProvider.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": { 3 | "name": "OrderProvider" 4 | }, 5 | "consumer": { 6 | "name": "Shipping" 7 | }, 8 | "interactions": [ 9 | { 10 | "description": "Request for an order", 11 | "request": { 12 | "method": "GET", 13 | "path": "/order/1" 14 | }, 15 | "response": { 16 | "status": 200, 17 | "headers": { 18 | "Content-Type": "application/json" 19 | }, 20 | "body": { 21 | "deliveryService": "Hermes", 22 | "numberOfLines": 1, 23 | "orderLine": [ 24 | { 25 | "item": { 26 | "itemId": 1, 27 | "name": "iPod" 28 | }, 29 | "count": 42 30 | } 31 | ], 32 | "shippingAddress": { 33 | "zip": "40789", 34 | "city": "Monheim am Rhein", 35 | "street": "Krischerstr. 100" 36 | }, 37 | "id": 1, 38 | "customer": { 39 | "firstname": "Eberhard", 40 | "customerId": 1, 41 | "name": "Wolff", 42 | "email": "eberhard.wolff@posteo.net" 43 | } 44 | }, 45 | "matchingRules": { 46 | "body": { 47 | "$.id": { 48 | "matchers": [ 49 | { 50 | "match": "number" 51 | } 52 | ], 53 | "combine": "AND" 54 | }, 55 | "$.numberOfLines": { 56 | "matchers": [ 57 | { 58 | "match": "number" 59 | } 60 | ], 61 | "combine": "AND" 62 | }, 63 | "$.deliveryService": { 64 | "matchers": [ 65 | { 66 | "match": "type" 67 | } 68 | ], 69 | "combine": "AND" 70 | }, 71 | "$.customer.customerId": { 72 | "matchers": [ 73 | { 74 | "match": "number" 75 | } 76 | ], 77 | "combine": "AND" 78 | }, 79 | "$.customer.name": { 80 | "matchers": [ 81 | { 82 | "match": "type" 83 | } 84 | ], 85 | "combine": "AND" 86 | }, 87 | "$.customer.firstname": { 88 | "matchers": [ 89 | { 90 | "match": "type" 91 | } 92 | ], 93 | "combine": "AND" 94 | }, 95 | "$.customer.email": { 96 | "matchers": [ 97 | { 98 | "match": "type" 99 | } 100 | ], 101 | "combine": "AND" 102 | }, 103 | "$.shippingAddress.street": { 104 | "matchers": [ 105 | { 106 | "match": "type" 107 | } 108 | ], 109 | "combine": "AND" 110 | }, 111 | "$.shippingAddress.zip": { 112 | "matchers": [ 113 | { 114 | "match": "type" 115 | } 116 | ], 117 | "combine": "AND" 118 | }, 119 | "$.shippingAddress.city": { 120 | "matchers": [ 121 | { 122 | "match": "type" 123 | } 124 | ], 125 | "combine": "AND" 126 | }, 127 | "$.orderLine[0].count": { 128 | "matchers": [ 129 | { 130 | "match": "number" 131 | } 132 | ], 133 | "combine": "AND" 134 | }, 135 | "$.orderLine[0].item.itemId": { 136 | "matchers": [ 137 | { 138 | "match": "number" 139 | } 140 | ], 141 | "combine": "AND" 142 | }, 143 | "$.orderLine[0].item.name": { 144 | "matchers": [ 145 | { 146 | "match": "type" 147 | } 148 | ], 149 | "combine": "AND" 150 | } 151 | } 152 | } 153 | } 154 | }, 155 | { 156 | "description": "Request for order feed", 157 | "request": { 158 | "method": "GET", 159 | "path": "/feed" 160 | }, 161 | "response": { 162 | "status": 200, 163 | "headers": { 164 | "Content-Type": "application/json" 165 | }, 166 | "body": { 167 | "orders": [ 168 | { 169 | "link": "http://localhost:8081/order/1", 170 | "id": 1, 171 | "updated": "2019-05-20T12:06:36.376+00:00" 172 | } 173 | ], 174 | "updated": "2019-05-20T12:06:36.376+00:00" 175 | }, 176 | "matchingRules": { 177 | "body": { 178 | "$.updated": { 179 | "matchers": [ 180 | { 181 | "match": "date", 182 | "date": "yyyy-MM-dd\u0027T\u0027kk:mm:ss.SSS+00:00" 183 | } 184 | ], 185 | "combine": "AND" 186 | }, 187 | "$.orders": { 188 | "matchers": [ 189 | { 190 | "match": "type", 191 | "min": 0 192 | } 193 | ], 194 | "combine": "AND" 195 | }, 196 | "$.orders[*].id": { 197 | "matchers": [ 198 | { 199 | "match": "number" 200 | } 201 | ], 202 | "combine": "AND" 203 | }, 204 | "$.orders[*].link": { 205 | "matchers": [ 206 | { 207 | "match": "type" 208 | } 209 | ], 210 | "combine": "AND" 211 | }, 212 | "$.orders[*].updated": { 213 | "matchers": [ 214 | { 215 | "match": "date", 216 | "date": "yyyy-MM-dd\u0027T\u0027kk:mm:ss.SSS+00:00" 217 | } 218 | ], 219 | "combine": "AND" 220 | } 221 | } 222 | } 223 | } 224 | } 225 | ], 226 | "metadata": { 227 | "pactSpecification": { 228 | "version": "3.0.0" 229 | }, 230 | "pact-jvm": { 231 | "version": "4.0.0-beta.2" 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /microservice-spring-demo/microservice-spring-order/src/test/resources/pacts/#Shipping-OrderProvider.json#: -------------------------------------------------------------------------------- 1 | { 2 | "provider": { 3 | "name": "OrderProvider" 4 | }, 5 | "consumer": { 6 | "name": "Shipping" 7 | }, 8 | "interactions": [ 9 | { 10 | "description": "Request for an order", 11 | "request": { 12 | "method": "GET", 13 | "path": "/order/1" 14 | }, 15 | "response": { 16 | "status": 200, 17 | "headers": { 18 | "Content-Type": "application/json" 19 | }, 20 | "body": { 21 | "deliveryService": "Hermes", 22 | "numberOfLines": 1, 23 | "orderLine": [ 24 | { 25 | "item": { 26 | "itemId": 1, 27 | "name": "iPod" 28 | }, 29 | "count": 42 30 | } 31 | ], 32 | "shippingAddress": { 33 | "zip": "40789", 34 | "city": "Monheim am Rhein", 35 | "street": "Krischerstr. 100", 36 | "county": "Nordrhein-Westphalen" 37 | }, 38 | "id": 1, 39 | "customer": { 40 | "firstname": "Eberhard", 41 | "customerId": 1, 42 | "name": "Wolff", 43 | "email": "eberhard.wolff@posteo.net" 44 | } 45 | }, 46 | "matchingRules": { 47 | "body": { 48 | "$.id": { 49 | "matchers": [ 50 | { 51 | "match": "number" 52 | } 53 | ], 54 | "combine": "AND" 55 | }, 56 | "$.numberOfLines": { 57 | "matchers": [ 58 | { 59 | "match": "number" 60 | } 61 | ], 62 | "combine": "AND" 63 | }, 64 | "$.deliveryService": { 65 | "matchers": [ 66 | { 67 | "match": "type" 68 | } 69 | ], 70 | "combine": "AND" 71 | }, 72 | "$.customer.customerId": { 73 | "matchers": [ 74 | { 75 | "match": "number" 76 | } 77 | ], 78 | "combine": "AND" 79 | }, 80 | "$.customer.name": { 81 | "matchers": [ 82 | { 83 | "match": "type" 84 | } 85 | ], 86 | "combine": "AND" 87 | }, 88 | "$.customer.firstname": { 89 | "matchers": [ 90 | { 91 | "match": "type" 92 | } 93 | ], 94 | "combine": "AND" 95 | }, 96 | "$.customer.email": { 97 | "matchers": [ 98 | { 99 | "match": "type" 100 | } 101 | ], 102 | "combine": "AND" 103 | }, 104 | "$.shippingAddress.street": { 105 | "matchers": [ 106 | { 107 | "match": "type" 108 | } 109 | ], 110 | "combine": "AND" 111 | }, 112 | "$.shippingAddress.zip": { 113 | "matchers": [ 114 | { 115 | "match": "type" 116 | } 117 | ], 118 | "combine": "AND" 119 | }, 120 | "$.shippingAddress.city": { 121 | "matchers": [ 122 | { 123 | "match": "type" 124 | } 125 | ], 126 | "combine": "AND" 127 | }, 128 | "$.orderLine[0].count": { 129 | "matchers": [ 130 | { 131 | "match": "number" 132 | } 133 | ], 134 | "combine": "AND" 135 | }, 136 | "$.orderLine[0].item.itemId": { 137 | "matchers": [ 138 | { 139 | "match": "number" 140 | } 141 | ], 142 | "combine": "AND" 143 | }, 144 | "$.orderLine[0].item.name": { 145 | "matchers": [ 146 | { 147 | "match": "type" 148 | } 149 | ], 150 | "combine": "AND" 151 | } 152 | } 153 | } 154 | } 155 | }, 156 | { 157 | "description": "Request for order feed", 158 | "request": { 159 | "method": "GET", 160 | "path": "/feed" 161 | }, 162 | "response": { 163 | "status": 200, 164 | "headers": { 165 | "Content-Type": "application/json" 166 | }, 167 | "body": { 168 | "orders": [ 169 | { 170 | "link": "http://localhost:8081/order/1", 171 | "id": 1, 172 | "updated": "2019-05-20T12:06:36.376+00:00" 173 | } 174 | ], 175 | "updated": "2019-05-20T12:06:36.376+00:00" 176 | }, 177 | "matchingRules": { 178 | "body": { 179 | "$.updated": { 180 | "matchers": [ 181 | { 182 | "match": "date", 183 | "date": "yyyy-MM-dd\u0027T\u0027kk:mm:ss.SSS+00:00" 184 | } 185 | ], 186 | "combine": "AND" 187 | }, 188 | "$.orders": { 189 | "matchers": [ 190 | { 191 | "match": "type", 192 | "min": 0 193 | } 194 | ], 195 | "combine": "AND" 196 | }, 197 | "$.orders[*].id": { 198 | "matchers": [ 199 | { 200 | "match": "number" 201 | } 202 | ], 203 | "combine": "AND" 204 | }, 205 | "$.orders[*].link": { 206 | "matchers": [ 207 | { 208 | "match": "type" 209 | } 210 | ], 211 | "combine": "AND" 212 | }, 213 | "$.orders[*].updated": { 214 | "matchers": [ 215 | { 216 | "match": "date", 217 | "date": "yyyy-MM-dd\u0027T\u0027kk:mm:ss.SSS+00:00" 218 | } 219 | ], 220 | "combine": "AND" 221 | } 222 | } 223 | } 224 | } 225 | } 226 | ], 227 | "metadata": { 228 | "pactSpecification": { 229 | "version": "3.0.0" 230 | }, 231 | "pact-jvm": { 232 | "version": "4.0.0-beta.2" 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /microservice-spring-demo/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | if [ "$MVNW_VERBOSE" = true ]; then 205 | echo $MAVEN_PROJECTBASEDIR 206 | fi 207 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 208 | 209 | # For Cygwin, switch paths to Windows format before running java 210 | if $cygwin; then 211 | [ -n "$M2_HOME" ] && 212 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 213 | [ -n "$JAVA_HOME" ] && 214 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 215 | [ -n "$CLASSPATH" ] && 216 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 217 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 218 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 219 | fi 220 | 221 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 222 | 223 | exec "$JAVACMD" \ 224 | $MAVEN_OPTS \ 225 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 226 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 227 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 228 | --------------------------------------------------------------------------------