├── app ├── ui │ ├── src │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── camel-logo.png │ │ │ └── plush │ │ │ │ ├── dali.jpg │ │ │ │ ├── jocker.jpg │ │ │ │ ├── lupin.jpg │ │ │ │ ├── e-donkey.jpg │ │ │ │ ├── mr-camel.jpg │ │ │ │ ├── darth-vader.jpg │ │ │ │ ├── sorcio-secco.jpg │ │ │ │ └── grattachecca-fichetto.jpg │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── app-navbar │ │ │ │ ├── app-navbar.component.css │ │ │ │ ├── app-navbar.component.ts │ │ │ │ └── app-navbar.component.html │ │ │ ├── items │ │ │ │ ├── items.component.css │ │ │ │ ├── items.component.html │ │ │ │ └── items.component.ts │ │ │ ├── messages │ │ │ │ ├── messages.component.css │ │ │ │ ├── messages.component.html │ │ │ │ └── messages.component.ts │ │ │ ├── loading │ │ │ │ ├── loading.component.html │ │ │ │ ├── loading.component.css │ │ │ │ └── loading.component.ts │ │ │ ├── app.component.ts │ │ │ ├── purchases │ │ │ │ ├── purchases.component.css │ │ │ │ ├── purchases.component.html │ │ │ │ └── purchases.component.ts │ │ │ ├── app.component.html │ │ │ ├── map-values.pipe.ts │ │ │ ├── payments │ │ │ │ ├── payments.component.css │ │ │ │ ├── payments.component.html │ │ │ │ └── payments.component.ts │ │ │ ├── payment.service.ts │ │ │ ├── purchase.service.ts │ │ │ ├── data.service.ts │ │ │ ├── datatypes.ts │ │ │ ├── item.service.ts │ │ │ └── app.module.ts │ │ ├── favicon.ico │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── styles.css │ │ ├── tsconfig.app.json │ │ ├── index.html │ │ ├── tsconfig.spec.json │ │ ├── tslint.json │ │ ├── browserslist │ │ ├── main.ts │ │ ├── test.ts │ │ ├── karma.conf.js │ │ └── polyfills.ts │ ├── docker_push.sh │ ├── Dockerfile │ ├── docker_build.sh │ ├── e2e │ │ ├── src │ │ │ ├── app.po.ts │ │ │ └── app.e2e-spec.ts │ │ ├── tsconfig.e2e.json │ │ └── protractor.conf.js │ ├── .editorconfig │ ├── tsconfig.json │ ├── .gitignore │ ├── README.md │ ├── default.conf │ ├── package.json │ ├── openshift-ui.yaml │ ├── tslint.json │ └── angular.json ├── spring-boot-ui │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── static │ │ │ │ ├── favicon.ico │ │ │ │ ├── assets │ │ │ │ ├── camel-logo.png │ │ │ │ └── plush │ │ │ │ │ ├── dali.jpg │ │ │ │ │ ├── lupin.jpg │ │ │ │ │ ├── jocker.jpg │ │ │ │ │ ├── e-donkey.jpg │ │ │ │ │ ├── mr-camel.jpg │ │ │ │ │ ├── darth-vader.jpg │ │ │ │ │ ├── sorcio-secco.jpg │ │ │ │ │ └── grattachecca-fichetto.jpg │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── index.html │ │ │ │ └── runtime.js │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── camel │ │ │ └── workshop │ │ │ └── springbootui │ │ │ └── SpringBootUiApplication.java │ ├── .gitignore │ ├── pom.xml │ ├── mvnw.cmd │ └── mvnw ├── credit │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.properties │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── camel │ │ │ └── workshop │ │ │ └── credit │ │ │ ├── CreditStore.java │ │ │ ├── CreditApplication.java │ │ │ ├── CreditRoutes.java │ │ │ ├── Payment.java │ │ │ └── CreditStoreImpl.java │ ├── .gitignore │ ├── pom.xml │ ├── mvnw.cmd │ └── mvnw ├── recommendation │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.properties │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── camel │ │ │ └── workshop │ │ │ └── recommendation │ │ │ └── RecommendationApplication.java │ ├── .gitignore │ ├── pom.xml │ ├── mvnw.cmd │ └── mvnw ├── inventory │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── catalog.json │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── camel │ │ │ └── workshop │ │ │ └── inventory │ │ │ ├── Inventory.java │ │ │ ├── InventoryApplication.java │ │ │ ├── Catalog.java │ │ │ ├── Purchase.java │ │ │ ├── Item.java │ │ │ ├── InventoryRoutes.java │ │ │ └── InventoryImpl.java │ ├── .gitignore │ ├── pom.xml │ ├── mvnw.cmd │ └── mvnw ├── gateway │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── java │ │ │ └── org │ │ │ │ └── apache │ │ │ │ └── camel │ │ │ │ └── workshop │ │ │ │ └── gateway │ │ │ │ ├── GatewayApplication.java │ │ │ │ ├── Catalog.java │ │ │ │ ├── Payment.java │ │ │ │ ├── Item.java │ │ │ │ ├── Order.java │ │ │ │ └── GatewayRoutes.java │ │ │ └── resources │ │ │ └── application.properties │ ├── pom.xml │ ├── mvnw.cmd │ └── mvnw └── pom.xml ├── .gitignore ├── doc ├── slides.pdf ├── catalog.png ├── purchases.png ├── jfuture-workshop-slides.pdf ├── camel-workshop-architecture.png ├── camel-workshop-architecture.xml ├── using-bom.md ├── ui.md ├── saga.md ├── recommendation.md ├── circuit-breaker.md └── credit.md └── README.md /app/ui/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/ui/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/ui/src/app/app-navbar/app-navbar.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=4000 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | 4 | target 5 | .metadata 6 | .project 7 | 8 | .mvn -------------------------------------------------------------------------------- /app/ui/src/app/items/items.component.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .item img { 4 | width: 100px; 5 | } 6 | -------------------------------------------------------------------------------- /doc/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/doc/slides.pdf -------------------------------------------------------------------------------- /app/ui/src/app/messages/messages.component.css: -------------------------------------------------------------------------------- 1 | .row.message { 2 | margin-top: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /doc/catalog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/doc/catalog.png -------------------------------------------------------------------------------- /doc/purchases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/doc/purchases.png -------------------------------------------------------------------------------- /app/ui/docker_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | docker push nferraro/camel-workshop-ui 6 | -------------------------------------------------------------------------------- /app/ui/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/favicon.ico -------------------------------------------------------------------------------- /app/ui/src/assets/camel-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/camel-logo.png -------------------------------------------------------------------------------- /app/ui/src/assets/plush/dali.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/dali.jpg -------------------------------------------------------------------------------- /doc/jfuture-workshop-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/doc/jfuture-workshop-slides.pdf -------------------------------------------------------------------------------- /app/credit/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | camel.component.servlet.mapping.context-path=/api/* 3 | server.port=8082 -------------------------------------------------------------------------------- /app/ui/src/app/loading/loading.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /app/ui/src/assets/plush/jocker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/jocker.jpg -------------------------------------------------------------------------------- /app/ui/src/assets/plush/lupin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/lupin.jpg -------------------------------------------------------------------------------- /app/recommendation/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | camel.component.servlet.mapping.context-path=/api/* 3 | server.port=8083 -------------------------------------------------------------------------------- /app/ui/src/app/loading/loading.component.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | font-size: 36px; 3 | text-align: center; 4 | margin: 50px; 5 | } 6 | -------------------------------------------------------------------------------- /app/ui/src/assets/plush/e-donkey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/e-donkey.jpg -------------------------------------------------------------------------------- /app/ui/src/assets/plush/mr-camel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/mr-camel.jpg -------------------------------------------------------------------------------- /doc/camel-workshop-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/doc/camel-workshop-architecture.png -------------------------------------------------------------------------------- /app/inventory/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | 3 | camel.component.servlet.mapping.context-path=/api/* 4 | server.port=8081 5 | -------------------------------------------------------------------------------- /app/ui/src/assets/plush/darth-vader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/darth-vader.jpg -------------------------------------------------------------------------------- /app/ui/src/assets/plush/sorcio-secco.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/sorcio-secco.jpg -------------------------------------------------------------------------------- /app/ui/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | apiPath: "/api" /* Nginx reverse proxy */ 4 | }; 5 | -------------------------------------------------------------------------------- /app/ui/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginxinc/openshift-nginx:stable 2 | COPY /dist/camel-workshop-ui/ /usr/share/nginx/html 3 | COPY default.conf /etc/nginx/conf.d/ 4 | 5 | -------------------------------------------------------------------------------- /app/ui/src/assets/plush/grattachecca-fichetto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/ui/src/assets/plush/grattachecca-fichetto.jpg -------------------------------------------------------------------------------- /app/ui/docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | npm install 6 | ng build --prod 7 | 8 | docker build --tag nferraro/camel-workshop-ui . 9 | 10 | -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/camel-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/camel-logo.png -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/dali.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/dali.jpg -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/lupin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/lupin.jpg -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/jocker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/jocker.jpg -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/e-donkey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/e-donkey.jpg -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/mr-camel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/mr-camel.jpg -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/darth-vader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/darth-vader.jpg -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/sorcio-secco.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/sorcio-secco.jpg -------------------------------------------------------------------------------- /app/ui/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import "~bootstrap/dist/css/bootstrap.css"; 4 | 5 | @import "~font-awesome/css/font-awesome.css"; 6 | -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/assets/plush/grattachecca-fichetto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaferraro/camel-workshop/HEAD/app/spring-boot-ui/src/main/resources/static/assets/plush/grattachecca-fichetto.jpg -------------------------------------------------------------------------------- /app/ui/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "exclude": [ 9 | "src/test.ts", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /app/ui/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/ui/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /app/ui/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'Camel Workshop'; 10 | } 11 | -------------------------------------------------------------------------------- /app/credit/src/main/java/org/apache/camel/workshop/credit/CreditStore.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.credit; 2 | 3 | import java.util.List; 4 | 5 | public interface CreditStore { 6 | 7 | void add(Payment payment); 8 | 9 | void remove(String ref); 10 | 11 | List list(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/ui/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /app/ui/src/app/loading/loading.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-loading', 5 | templateUrl: './loading.component.html', 6 | styleUrls: ['./loading.component.css'] 7 | }) 8 | export class LoadingComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/ui/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CamelWorkshopUi 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/ui/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /app/ui/src/app/app-navbar/app-navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-navbar', 5 | templateUrl: './app-navbar.component.html', 6 | styleUrls: ['./app-navbar.component.css'] 7 | }) 8 | export class AppNavbarComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/ui/src/app/purchases/purchases.component.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | margin-top: 20px; 3 | } 4 | 5 | .card.purchase { 6 | height: 200px; 7 | margin-bottom: 10px; 8 | } 9 | 10 | .inactive { 11 | text-decoration: line-through; 12 | color: red; 13 | } 14 | 15 | .total { 16 | color: #AAAAAA; 17 | font-size: smaller; 18 | } 19 | 20 | .canceled-bg { 21 | background-color: #EEEEEE; 22 | } 23 | -------------------------------------------------------------------------------- /app/ui/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /app/credit/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /app/gateway/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /app/inventory/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /app/recommendation/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /app/spring-boot-ui/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /app/ui/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/ui/src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /app/ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/Inventory.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | import java.util.List; 4 | 5 | public interface Inventory { 6 | 7 | Catalog getCatalog(); 8 | 9 | void addToPurchase(String purchaseReferenceCode, String itemId, int amount); 10 | 11 | List getPurchases(); 12 | 13 | void cancelPurchase(String purchaseReferenceCode); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/credit/src/main/java/org/apache/camel/workshop/credit/CreditApplication.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.credit; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CreditApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CreditApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/gateway/src/main/java/org/apache/camel/workshop/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.gateway; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GatewayApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GatewayApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/ui/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/InventoryApplication.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InventoryApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(InventoryApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/java/org/apache/camel/workshop/springbootui/SpringBootUiApplication.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.springbootui; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootUiApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootUiApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/ui/src/app/map-values.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import {Entry} from "./datatypes"; 3 | 4 | @Pipe({ 5 | name: 'mapValues' 6 | }) 7 | export class MapValuesPipe implements PipeTransform { 8 | 9 | transform(theMap: any, args?: any[]): Entry[] { 10 | let res = []; 11 | for (let key in theMap) { 12 | res.push({ 13 | key: key, 14 | value: theMap[key] 15 | }); 16 | } 17 | 18 | return res; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/ui/src/app/payments/payments.component.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | margin-top: 20px; 3 | } 4 | 5 | .card.payment { 6 | height: 200px; 7 | margin-bottom: 10px; 8 | } 9 | 10 | .card.payment .card-body { 11 | text-align: center; 12 | vertical-align: middle; 13 | font-size: 36px; 14 | font-weight: bold; 15 | } 16 | 17 | .inactive { 18 | text-decoration: line-through; 19 | } 20 | 21 | .total { 22 | color: #AAAAAA; 23 | font-size: smaller; 24 | } 25 | 26 | .canceled-bg { 27 | background-color: #EEEEEE; 28 | } 29 | -------------------------------------------------------------------------------- /app/gateway/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | camel.component.servlet.mapping.context-path=/api/* 2 | #server.port=8080 3 | 4 | # ServiceCall configuration 5 | camel.cloud.service-call.component=undertow 6 | 7 | inventory.service=localhost:8081 8 | credit.service=localhost:8082 9 | recommendation.service=localhost:8083 10 | 11 | camel.cloud.service-discovery.services[inventory]=${inventory.service} 12 | camel.cloud.service-discovery.services[credit]=${credit.service} 13 | camel.cloud.service-discovery.services[recommendation]=${recommendation.service} 14 | -------------------------------------------------------------------------------- /app/ui/src/app/payment.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Observable} from "rxjs/internal/Observable"; 3 | import {Payment} from "./datatypes"; 4 | import {HttpClient} from "@angular/common/http"; 5 | import {environment} from "../environments/environment"; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class PaymentService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getPayments(): Observable { 15 | return this.http.get(environment.apiPath + "/payments"); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/ui/src/app/purchase.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Observable} from "rxjs/internal/Observable"; 3 | import {HttpClient} from "@angular/common/http"; 4 | import {Purchase} from "./datatypes"; 5 | import {environment} from "../environments/environment"; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class PurchaseService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getPurchases(): Observable { 15 | return this.http.get(environment.apiPath + "/purchases"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/ui/src/app/messages/messages.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CamelWorkshopUi 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /app/gateway/src/main/java/org/apache/camel/workshop/gateway/Catalog.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.gateway; 2 | 3 | import java.util.Map; 4 | 5 | public class Catalog { 6 | 7 | private Map items; 8 | 9 | public Catalog() { 10 | } 11 | 12 | public Catalog(Map items) { 13 | this.items = items; 14 | } 15 | 16 | public Map getItems() { 17 | return items; 18 | } 19 | 20 | public void setItems(Map items) { 21 | this.items = items; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "Catalog{" + 27 | "items=" + items + 28 | '}'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/ui/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/Catalog.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | import java.util.Map; 4 | 5 | public class Catalog { 6 | 7 | private Map items; 8 | 9 | public Catalog() { 10 | } 11 | 12 | public Catalog(Map items) { 13 | this.items = items; 14 | } 15 | 16 | public Map getItems() { 17 | return items; 18 | } 19 | 20 | public void setItems(Map items) { 21 | this.items = items; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "Catalog{" + 27 | "items=" + items + 28 | '}'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/ui/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | apiPath: "http://localhost:8080/api" 8 | }; 9 | 10 | /* 11 | * In development mode, to ignore zone related error stack frames such as 12 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 13 | * import the following file, but please comment it out in production mode 14 | * because it will have performance impact when throw error 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /app/ui/src/app/messages/messages.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {Message} from "../datatypes"; 3 | import {DataService} from "../data.service"; 4 | 5 | @Component({ 6 | selector: 'app-messages', 7 | templateUrl: './messages.component.html', 8 | styleUrls: ['./messages.component.css'] 9 | }) 10 | export class MessagesComponent implements OnInit { 11 | 12 | message: Message; 13 | 14 | constructor(private data: DataService) { } 15 | 16 | ngOnInit() { 17 | this.data.messages.subscribe(message => this.message = message); 18 | } 19 | 20 | refresh(): void { 21 | this.dismiss() 22 | this.data.triggerModelChange(); 23 | } 24 | 25 | dismiss(): void { 26 | this.message = null; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.apache.camel.workshop 8 | camel-workshop-parent 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | 13 | inventory 14 | credit 15 | gateway 16 | recommendation 17 | spring-boot-ui 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/ui/src/app/app-navbar/app-navbar.component.html: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /app/ui/src/app/payments/payments.component.html: -------------------------------------------------------------------------------- 1 |

Payments ({{total | currency}})

2 | 3 |
4 |
5 |
6 |
7 | Payment #{{payment.reference}} 8 |
9 |
10 | {{payment.amount | currency }} 11 |
12 |
13 |
14 |
15 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /app/ui/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /app/ui/src/app/data.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Catalog, Message} from "./datatypes"; 3 | import {Subject} from "rxjs/internal/Subject"; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class DataService { 9 | 10 | private catalogSource = new Subject(); 11 | catalog = this.catalogSource.asObservable(); 12 | 13 | private modelChangesSource = new Subject(); 14 | modelChanges = this.modelChangesSource.asObservable(); 15 | 16 | private messagesSource = new Subject(); 17 | messages = this.messagesSource.asObservable(); 18 | 19 | constructor() { } 20 | 21 | updateCatalog(catalog: Catalog) { 22 | this.catalogSource.next(catalog); 23 | } 24 | 25 | triggerModelChange() { 26 | this.modelChangesSource.next(1); 27 | } 28 | 29 | publishMessage(message: Message) { 30 | this.messagesSource.next(message); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/ui/src/app/datatypes.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Catalog { 3 | items: Map 4 | } 5 | 6 | export class Item { 7 | id: string; 8 | name: string; 9 | image: string; 10 | price: number; 11 | stockUnits: number; 12 | recommended: boolean; 13 | } 14 | 15 | export class Purchase { 16 | id: string; 17 | items: Map; 18 | active: boolean; 19 | } 20 | 21 | export class Entry { 22 | key: string; 23 | value: object; 24 | } 25 | 26 | export class Order { 27 | reference: string; 28 | user: string; 29 | items: OrderItem[]; 30 | price: number; 31 | } 32 | 33 | export class OrderItem { 34 | id: string; 35 | amount: number; 36 | } 37 | 38 | export class Payment { 39 | reference: string; 40 | user: string; 41 | amount: number; 42 | active: boolean; 43 | } 44 | 45 | export class Message { 46 | title: string; 47 | content: string; 48 | error: boolean; 49 | refresh: boolean; 50 | } 51 | -------------------------------------------------------------------------------- /app/ui/src/app/purchases/purchases.component.html: -------------------------------------------------------------------------------- 1 |

Purchases ({{total}})

2 | 3 |
4 |
5 | 6 |
7 |
8 | Purchase #{{purchase.id}} 9 |
10 |
11 |
12 | {{itemEntry.value}}x 13 |    14 |
15 |
16 |
17 |
18 | 19 |
20 | 23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /app/recommendation/src/main/java/org/apache/camel/workshop/recommendation/RecommendationApplication.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.recommendation; 2 | 3 | import org.apache.camel.builder.RouteBuilder; 4 | import org.apache.camel.model.rest.RestBindingMode; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.Collections; 10 | 11 | @SpringBootApplication 12 | public class RecommendationApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(RecommendationApplication.class, args); 16 | } 17 | 18 | 19 | @Component 20 | public static class Routes extends RouteBuilder { 21 | 22 | @Override 23 | public void configure() { 24 | 25 | restConfiguration().bindingMode(RestBindingMode.json); 26 | 27 | rest().get("/recommendations") 28 | .route() 29 | .setBody(constant(Collections.singletonList("i99"))) 30 | .log("Recommending ${body}"); 31 | 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/ui/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /app/credit/src/main/java/org/apache/camel/workshop/credit/CreditRoutes.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.credit; 2 | 3 | import org.apache.camel.builder.RouteBuilder; 4 | import org.apache.camel.model.dataformat.JsonLibrary; 5 | import org.apache.camel.model.rest.RestParamType; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class CreditRoutes extends RouteBuilder { 10 | 11 | @Override 12 | public void configure() { 13 | 14 | rest().get("/payments") 15 | .route() 16 | .bean("creditStore", "list") 17 | .marshal().json(JsonLibrary.Jackson); 18 | 19 | 20 | rest().post("/payments") 21 | .route() 22 | .unmarshal().json(JsonLibrary.Jackson, Payment.class) 23 | .to("bean-validator:validatePayment") 24 | .bean("creditStore", "add") 25 | .marshal().json(JsonLibrary.Jackson); 26 | 27 | 28 | rest().delete("/payments/{ref}") 29 | .param() 30 | .name("ref") 31 | .type(RestParamType.path) 32 | .description("Reference code") 33 | .endParam() 34 | .route() 35 | .bean("creditStore", "remove(${header.ref})"); 36 | 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/ui/README.md: -------------------------------------------------------------------------------- 1 | # CamelWorkshopUi 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /app/ui/src/app/item.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Observable} from "rxjs/internal/Observable"; 3 | import {HttpClient} from "@angular/common/http"; 4 | import {Catalog, Order, OrderItem} from "./datatypes"; 5 | import {environment} from "../environments/environment"; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class ItemService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getCatalog(): Observable { 15 | return this.http.get(environment.apiPath + "/items"); 16 | } 17 | 18 | makeOrder(items: Map): Observable { 19 | let order = new Order(); 20 | order.reference = Date.now() + "-" + 1000 + Math.floor(Math.random() * 1000); 21 | order.user = "theuser"; 22 | order.items = []; 23 | order.price = 0; 24 | items.forEach((v,k) => { 25 | if (v > 0) { 26 | order.items.push({ 27 | id: k, 28 | amount: v 29 | }); 30 | order.price += v; // TODO handle different prices 31 | } 32 | }); 33 | 34 | return this.http.post(environment.apiPath + "/orders", order); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/Purchase.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | public class Purchase { 7 | 8 | private String id; 9 | 10 | private Map items = new ConcurrentHashMap<>(); 11 | 12 | private boolean active = true; 13 | 14 | public Purchase() { 15 | } 16 | 17 | public Purchase(String id) { 18 | this.id = id; 19 | } 20 | 21 | public String getId() { 22 | return id; 23 | } 24 | 25 | public void setId(String id) { 26 | this.id = id; 27 | } 28 | 29 | public Map getItems() { 30 | return items; 31 | } 32 | 33 | public void setItems(Map items) { 34 | this.items = items; 35 | } 36 | 37 | public boolean isActive() { 38 | return active; 39 | } 40 | 41 | public void setActive(boolean active) { 42 | this.active = active; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "Purchase{" + 48 | "id='" + id + '\'' + 49 | ", items=" + items + 50 | ", active=" + active + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/ui/src/app/payments/payments.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {PaymentService} from "../payment.service"; 3 | import {Payment} from "../datatypes"; 4 | import {DataService} from "../data.service"; 5 | 6 | @Component({ 7 | selector: 'app-payments', 8 | templateUrl: './payments.component.html', 9 | styleUrls: ['./payments.component.css'] 10 | }) 11 | export class PaymentsComponent implements OnInit { 12 | 13 | payments: Payment[]; 14 | 15 | total: number; 16 | 17 | loaded: boolean; 18 | 19 | constructor(private payment: PaymentService, private data: DataService) { } 20 | 21 | ngOnInit() { 22 | this.refresh(); 23 | 24 | this.data.modelChanges.subscribe(change => { 25 | this.refresh(); 26 | }); 27 | } 28 | 29 | refresh(): void { 30 | this.payment.getPayments().subscribe(p => { 31 | this.payments = p.reverse(); 32 | this.total = 0; 33 | for (let p of this.payments) { 34 | if (p.active) { 35 | this.total += p.amount; 36 | } 37 | } 38 | this.loaded = true; 39 | }, error => { 40 | this.data.publishMessage({ 41 | title: "Error!", 42 | content: "Cannot load payments", 43 | refresh: true, 44 | error: true 45 | }) 46 | }); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/ui/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { ItemsComponent } from './items/items.component'; 6 | import {HttpClientModule} from "@angular/common/http"; 7 | import { AppNavbarComponent } from './app-navbar/app-navbar.component'; 8 | import {NgbModule} from "@ng-bootstrap/ng-bootstrap"; 9 | import {AngularFontAwesomeModule} from "angular-font-awesome"; 10 | import { PurchasesComponent } from './purchases/purchases.component'; 11 | import { MapValuesPipe } from './map-values.pipe'; 12 | import { PaymentsComponent } from './payments/payments.component'; 13 | import { LoadingComponent } from './loading/loading.component'; 14 | import { MessagesComponent } from './messages/messages.component'; 15 | 16 | @NgModule({ 17 | declarations: [ 18 | AppComponent, 19 | ItemsComponent, 20 | AppNavbarComponent, 21 | PurchasesComponent, 22 | MapValuesPipe, 23 | PaymentsComponent, 24 | LoadingComponent, 25 | MessagesComponent 26 | ], 27 | imports: [ 28 | BrowserModule, 29 | HttpClientModule, 30 | NgbModule.forRoot(), 31 | AngularFontAwesomeModule 32 | ], 33 | providers: [], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule { } 37 | -------------------------------------------------------------------------------- /app/ui/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | server_name localhost; 4 | 5 | #charset koi8-r; 6 | #access_log /var/log/nginx/host.access.log main; 7 | 8 | location / { 9 | root /usr/share/nginx/html; 10 | index index.html index.htm; 11 | } 12 | 13 | #error_page 404 /404.html; 14 | 15 | # redirect server error pages to the static page /50x.html 16 | # 17 | error_page 500 502 503 504 /50x.html; 18 | location = /50x.html { 19 | root /usr/share/nginx/html; 20 | } 21 | 22 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 23 | # 24 | #location ~ \.php$ { 25 | # proxy_pass http://127.0.0.1; 26 | #} 27 | 28 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 29 | # 30 | #location ~ \.php$ { 31 | # root html; 32 | # fastcgi_pass 127.0.0.1:9000; 33 | # fastcgi_index index.php; 34 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 35 | # include fastcgi_params; 36 | #} 37 | 38 | # deny access to .htaccess files, if Apache's document root 39 | # concurs with nginx's one 40 | # 41 | #location ~ /\.ht { 42 | # deny all; 43 | #} 44 | 45 | location /api { 46 | proxy_pass http://gateway:8080; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/inventory/src/main/resources/catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": { 3 | "i1": { 4 | "id": "i1", 5 | "name": "Itchy & Scratchy", 6 | "image": "/assets/plush/grattachecca-fichetto.jpg", 7 | "price": 1, 8 | "stockUnits": 10 9 | }, 10 | "i2": { 11 | "id": "i2", 12 | "name": "The Jocker", 13 | "image": "/assets/plush/jocker.jpg", 14 | "price": 1, 15 | "stockUnits": 10 16 | }, 17 | "i99": { 18 | "id": "i99", 19 | "name": "Mr. Camel", 20 | "image": "/assets/plush/mr-camel.jpg", 21 | "price": 1, 22 | "stockUnits": 10 23 | }, 24 | "i4": { 25 | "id": "i4", 26 | "name": "eDonkey", 27 | "image": "/assets/plush/e-donkey.jpg", 28 | "price": 1, 29 | "stockUnits": 10 30 | }, 31 | "i5": { 32 | "id": "i5", 33 | "name": "Lupin", 34 | "image": "/assets/plush/lupin.jpg", 35 | "price": 1, 36 | "stockUnits": 10 37 | }, 38 | "i6": { 39 | "id": "i6", 40 | "name": "Dalì", 41 | "image": "/assets/plush/dali.jpg", 42 | "price": 1, 43 | "stockUnits": 10 44 | }, 45 | "i7": { 46 | "id": "i7", 47 | "name": "Darth Vader", 48 | "image": "/assets/plush/darth-vader.jpg", 49 | "price": 1, 50 | "stockUnits": 10 51 | }, 52 | "i8": { 53 | "id": "i8", 54 | "name": "Secco", 55 | "image": "/assets/plush/sorcio-secco.jpg", 56 | "price": 1, 57 | "stockUnits": 10 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /app/credit/src/main/java/org/apache/camel/workshop/credit/Payment.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.credit; 2 | 3 | import javax.validation.constraints.Min; 4 | import javax.validation.constraints.NotNull; 5 | 6 | public class Payment { 7 | 8 | @NotNull 9 | private String reference; 10 | 11 | @NotNull 12 | private String user; 13 | 14 | @NotNull 15 | @Min(0) 16 | private Integer amount; 17 | 18 | private boolean active = true; 19 | 20 | public Payment() { 21 | } 22 | 23 | public String getReference() { 24 | return reference; 25 | } 26 | 27 | public void setReference(String reference) { 28 | this.reference = reference; 29 | } 30 | 31 | public String getUser() { 32 | return user; 33 | } 34 | 35 | public void setUser(String user) { 36 | this.user = user; 37 | } 38 | 39 | public Integer getAmount() { 40 | return amount; 41 | } 42 | 43 | public void setAmount(Integer amount) { 44 | this.amount = amount; 45 | } 46 | 47 | public boolean isActive() { 48 | return active; 49 | } 50 | 51 | public void setActive(boolean active) { 52 | this.active = active; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Payment{" + 58 | "reference='" + reference + '\'' + 59 | ", user='" + user + '\'' + 60 | ", amount=" + amount + 61 | ", active=" + active + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/gateway/src/main/java/org/apache/camel/workshop/gateway/Payment.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.gateway; 2 | 3 | import javax.validation.constraints.Min; 4 | import javax.validation.constraints.NotNull; 5 | 6 | public class Payment { 7 | 8 | @NotNull 9 | private String reference; 10 | 11 | @NotNull 12 | private String user; 13 | 14 | @NotNull 15 | @Min(0) 16 | private Integer amount; 17 | 18 | private boolean active = true; 19 | 20 | public Payment() { 21 | } 22 | 23 | public String getReference() { 24 | return reference; 25 | } 26 | 27 | public void setReference(String reference) { 28 | this.reference = reference; 29 | } 30 | 31 | public String getUser() { 32 | return user; 33 | } 34 | 35 | public void setUser(String user) { 36 | this.user = user; 37 | } 38 | 39 | public Integer getAmount() { 40 | return amount; 41 | } 42 | 43 | public void setAmount(Integer amount) { 44 | this.amount = amount; 45 | } 46 | 47 | public boolean isActive() { 48 | return active; 49 | } 50 | 51 | public void setActive(boolean active) { 52 | this.active = active; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Payment{" + 58 | "reference='" + reference + '\'' + 59 | ", user='" + user + '\'' + 60 | ", amount=" + amount + 61 | ", active=" + active + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/ui/src/app/purchases/purchases.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {PurchaseService} from "../purchase.service"; 3 | import {Catalog, Purchase} from "../datatypes"; 4 | import {DataService} from "../data.service"; 5 | 6 | @Component({ 7 | selector: 'app-purchases', 8 | templateUrl: './purchases.component.html', 9 | styleUrls: ['./purchases.component.css'] 10 | }) 11 | export class PurchasesComponent implements OnInit { 12 | 13 | purchases: Purchase[]; 14 | 15 | total: number; 16 | 17 | catalog: Catalog; 18 | 19 | loaded: boolean; 20 | 21 | constructor(private purchaseService: PurchaseService, private data: DataService) { } 22 | 23 | ngOnInit() { 24 | this.data.catalog.subscribe(catalog => this.catalog = catalog); 25 | this.refresh(); 26 | 27 | this.data.modelChanges.subscribe(change => { 28 | this.refresh(); 29 | }); 30 | } 31 | 32 | refresh(): void { 33 | this.purchaseService.getPurchases().subscribe(purchases => { 34 | this.purchases = purchases.sort((a, b) => b.id.localeCompare(a.id)); 35 | this.total = 0; 36 | for (let p of this.purchases) { 37 | for (let i in p.items) { 38 | if (p.active) { 39 | this.total += p.items[i] 40 | } 41 | } 42 | } 43 | this.loaded = true; 44 | }, error => { 45 | this.data.publishMessage({ 46 | title: "Error!", 47 | content: "Cannot load purchases", 48 | refresh: true, 49 | error: true 50 | }) 51 | }); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/Item.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | public class Item { 4 | 5 | private String id; 6 | 7 | private String name; 8 | 9 | private String image; 10 | 11 | private int stockUnits; 12 | 13 | private int price; 14 | 15 | public Item() { 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getImage() { 35 | return image; 36 | } 37 | 38 | public void setImage(String image) { 39 | this.image = image; 40 | } 41 | 42 | public int getPrice() { 43 | return price; 44 | } 45 | 46 | public void setPrice(int price) { 47 | this.price = price; 48 | } 49 | 50 | public int getStockUnits() { 51 | return stockUnits; 52 | } 53 | 54 | public void setStockUnits(int stockUnits) { 55 | this.stockUnits = stockUnits; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "Item{" + 61 | "id='" + id + '\'' + 62 | ", name='" + name + '\'' + 63 | ", image='" + image + '\'' + 64 | ", stockUnits=" + stockUnits + 65 | ", price=" + price + 66 | '}'; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /doc/camel-workshop-architecture.xml: -------------------------------------------------------------------------------- 1 | 5Zpdc5s4FIZ/jW92xh6Q+PJl7NRpOt2ZTjO73b2UQQE1AjFCNvb++pVAYIOcLt3aLk65SNCRBEjPe6SjM57AZbp74ChPfmcRphNgRbsJvJ8AYDvAk/+UZV9bfGjXhpiTSDc6GJ7IP1gbLW3dkAgXnYaCMSpI3jWGLMtwKDo2xDkru82eGe2+NUcxNgxPIaKm9QuJRFJbA+Af7O8xiZPmzbY3r2vWKHyJOdtk+n0TAJ+rq65OUfMsPdAiQRErj0zw3QQuOWOivkt3S0zV3DbTVvdbvVLbfjfHmRjSAdQdtohu9ND/eNRfJvbNbFTjwaqHPYGLMiECP+UoVLWl5C9tiUiprqZojeminYYlo4zLqoxlsv2iEJy94MYoZyfy1p7ryZpnQumRXU4ZCENlZ5nQAgGeLh+1s6pL2s2R68nYYi7w7sikZ+IBsxQLvpdNdC1wNBWt2qZYHiQAPW1LjvA3NqRVF7dPPsy8vNGTfxoENEA8IIFLtL8qDa+6TBoQzuer1X/TWFXXeWhA0KVhz00cwLoQDsfAseQ4IuKqNNaB67jWCd8IQnxt35jDmdvBAQMThw0uhMM1cDxmWzkYxq/rH2MiAtxBSC61YHkGks84ZGmKswgJwrJflosziAsILsTFN7gYJCSiOxUkyVJIUVGQ8DybRl3TBEy2iQNYPlgE37GN4B0Rf0mbNZNLQF38Wz9aAuL7us53m7KqtKpCE1Ta36RasA0PcWcHFojHWHR2ARx1okWT/BFX9wTWxsYxla6x7caYp1jrN3xiRH7vYQnu7oeu3VNLPRjd6Tjm6z3H7oc5fdnVU2A8qFJeO+pBYgzephi/ocVLSdEdlRTbFU1LyFi5BmvRc7pL5fxiWpy/TS36P0GM3rjE2FsYgf9/xeh3xehYFxOjbR6834Qav0eMg9QHTPXBUanvBndl20w2GOIrEpSrW5JWqbKFin9JiOhHJbpPrCBVjA/v10wIlr6uxkMC7PCMO0pi1VcwFfWjIq9TeM9kp44Hi+qVd43VaizyPhFCJQDv1ODBKoyyaVVTTO1ZKk/nm3QmTyBq9Ggn/zoKzcr+Lbn/QO/W4vHPBd/u0caJgg+fP8azPItbERrR/Qldvhrw+1bQC/gdpzEM1OCPBPy2mau4RZoZi/DXYsZ4LAuFkC4Zypuar7yhLGZF22ya4XKao0zItW66pvI7z4fT6+WdHPcEzFOnN2d+BphmpuMWYRJrVuaNM+4jvI0xftHlMp+Gch9RNMBqk1OGIgUWWLY8uq4sNVdFzkkWT9eMiWnO2Vf5uqkSQA15xXFRb0pQbdAuWMKjs/sPsXd7i7A9v6IfmxmV20Hfki/LclaEJCcM80YBMrzAGnGgENtq8UYppkdQz+K5Vp9eYNBz/BOO65+B3oCj7mjp3b7j2lZ/0TZTbhdz3AEny9GiH4Xjtknqlh68muM2u/1N0rt9xwXBz3PcNrd/i+hH4biwf2i9puMOSKGMlt7tO24/VL6q495yAmMUjtsPlS/ouLJ4+D1Tna06/GgMvvsX -------------------------------------------------------------------------------- /app/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "camel-workshop-ui", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.0.2", 15 | "@angular/common": "^6.0.2", 16 | "@angular/compiler": "^6.0.2", 17 | "@angular/core": "^6.0.2", 18 | "@angular/forms": "^6.0.2", 19 | "@angular/http": "^6.0.2", 20 | "@angular/platform-browser": "^6.0.2", 21 | "@angular/platform-browser-dynamic": "^6.0.2", 22 | "@angular/router": "^6.0.2", 23 | "@ng-bootstrap/ng-bootstrap": "^2.0.0", 24 | "angular-font-awesome": "^3.1.2", 25 | "bootstrap": "^4.1.1", 26 | "core-js": "^2.5.4", 27 | "font-awesome": "^4.7.0", 28 | "hoek": "^4.2.1", 29 | "jquery": "^3.3.1", 30 | "popper.js": "^1.14.3", 31 | "rxjs": "^6.0.0", 32 | "zone.js": "^0.8.26" 33 | }, 34 | "devDependencies": { 35 | "@angular/compiler-cli": "^6.0.2", 36 | "@angular-devkit/build-angular": "~0.6.3", 37 | "typescript": "~2.7.2", 38 | "@angular/cli": "~6.0.3", 39 | "@angular/language-service": "^6.0.2", 40 | "@types/jasmine": "~2.8.6", 41 | "@types/jasminewd2": "~2.0.3", 42 | "@types/node": "~8.9.4", 43 | "codelyzer": "~4.2.1", 44 | "jasmine-core": "~2.99.1", 45 | "jasmine-spec-reporter": "~4.2.1", 46 | "karma": "~1.7.1", 47 | "karma-chrome-launcher": "~2.2.0", 48 | "karma-coverage-istanbul-reporter": "~1.4.2", 49 | "karma-jasmine": "~1.1.1", 50 | "karma-jasmine-html-reporter": "^0.2.2", 51 | "protractor": "~5.3.0", 52 | "ts-node": "~5.0.1", 53 | "tslint": "~5.9.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/ui/openshift-ui.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: ui 10 | version: 0.0.1-SNAPSHOT 11 | group: org.apache.camel.workshop 12 | name: ui 13 | spec: 14 | ports: 15 | - name: http 16 | port: 8080 17 | protocol: TCP 18 | targetPort: 8080 19 | selector: 20 | app: ui 21 | group: org.apache.camel.workshop 22 | - apiVersion: apps.openshift.io/v1 23 | kind: DeploymentConfig 24 | metadata: 25 | labels: 26 | app: ui 27 | version: 0.0.1-SNAPSHOT 28 | group: org.apache.camel.workshop 29 | name: ui 30 | spec: 31 | replicas: 1 32 | revisionHistoryLimit: 2 33 | selector: 34 | app: ui 35 | group: org.apache.camel.workshop 36 | template: 37 | metadata: 38 | labels: 39 | app: ui 40 | version: 0.0.1-SNAPSHOT 41 | group: org.apache.camel.workshop 42 | spec: 43 | containers: 44 | - env: 45 | - name: KUBERNETES_NAMESPACE 46 | valueFrom: 47 | fieldRef: 48 | fieldPath: metadata.namespace 49 | image: nferraro/camel-workshop-ui:latest 50 | imagePullPolicy: IfNotPresent 51 | name: nginx 52 | ports: 53 | - containerPort: 8080 54 | name: http 55 | protocol: TCP 56 | securityContext: 57 | privileged: false 58 | - apiVersion: v1 59 | kind: Route 60 | metadata: 61 | labels: 62 | app: ui 63 | expose: 'true' 64 | group: org.apache.camel.workshop 65 | version: 0.0.1-SNAPSHOT 66 | name: ui 67 | spec: 68 | to: 69 | kind: Service 70 | name: ui 71 | -------------------------------------------------------------------------------- /app/gateway/src/main/java/org/apache/camel/workshop/gateway/Item.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.gateway; 2 | 3 | public class Item { 4 | 5 | private String id; 6 | 7 | private String name; 8 | 9 | private String image; 10 | 11 | private int stockUnits; 12 | 13 | private int price; 14 | 15 | private boolean recommended; 16 | 17 | public Item() { 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void setId(String id) { 25 | this.id = id; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public String getImage() { 37 | return image; 38 | } 39 | 40 | public void setImage(String image) { 41 | this.image = image; 42 | } 43 | 44 | public int getPrice() { 45 | return price; 46 | } 47 | 48 | public void setPrice(int price) { 49 | this.price = price; 50 | } 51 | 52 | public int getStockUnits() { 53 | return stockUnits; 54 | } 55 | 56 | public void setStockUnits(int stockUnits) { 57 | this.stockUnits = stockUnits; 58 | } 59 | 60 | public boolean isRecommended() { 61 | return recommended; 62 | } 63 | 64 | public void setRecommended(boolean recommended) { 65 | this.recommended = recommended; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "Item{" + 71 | "id='" + id + '\'' + 72 | ", name='" + name + '\'' + 73 | ", image='" + image + '\'' + 74 | ", stockUnits=" + stockUnits + 75 | ", price=" + price + 76 | '}'; 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/spring-boot-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.apache.camel.workshop 7 | spring-boot-ui 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring-boot-ui 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-tomcat 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-undertow 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /doc/using-bom.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop: Using Camel BOM 2 | 3 | All Camel components are listed in a **BOM** (Bill Of Materials) project that is called 4 | `org.apache.camel:camel-bom`. 5 | 6 | Normally, dependencies on a `pom.xml` file are declared as follows: 7 | 8 | ```xml 9 | 10 | 11 | 12 | org.apache.camel 13 | camel-servlet-starter 14 | 2.22.1 15 | 16 | 17 | org.apache.camel 18 | camel-jackson-starter 19 | 2.22.1 20 | 21 | ``` 22 | 23 | When using the BOM, it's not necessary to specify the version (*2.22.1*) for each dependency and 24 | also some IDEs will provide autocompletion when adding a new Camel component to the project. 25 | 26 | The BOM can be imported using the following declaration: 27 | 28 | ```xml 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.apache.camel 36 | camel-bom 37 | 2.22.1 38 | pom 39 | import 40 | 41 | 42 | 43 | ``` 44 | 45 | After doing this, you can add Camel dependencies in the `` section without specifying the version. 46 | For example: 47 | 48 | ```xml 49 | 50 | 51 | 52 | org.apache.camel 53 | camel-servlet-starter 54 | 55 | 56 | org.apache.camel 57 | camel-jackson-starter 58 | 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /app/ui/src/app/items/items.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Plush Toys Shop

5 |
6 |
7 | 8 |
9 |
10 |
11 |
12 | Item {{item.id}} 13 |
14 |
15 |
{{item.name}}
16 |

{{item.price | currency}}
{{item.stockUnits}} items in stock

17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 |
38 |
39 | {{total}} Items Selected 40 |
41 |
42 |
43 | 44 |
45 |
46 | 47 | 48 |
49 |
50 | 51 |
52 |
53 | -------------------------------------------------------------------------------- /doc/ui.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop: UI 2 | 3 | The UI for the workshop is based on Angular 6. It is *provided* and can be used as is. 4 | 5 | ## The quick way 6 | The quickest way to see the ui is to navigate to: [https://camel-workshop.nicolaferraro.me/](https://camel-workshop.nicolaferraro.me/). 7 | Since we have enabled CORS for all URLs, the browser is allowed to contact localhost, even if the website is hosted on a remote URL. 8 | 9 | 10 | ## The manual way 11 | If you want to run the UI yourself, you can download a spring-boot packaged UI. 12 | 13 | Download the [camel-workshop-spring-boot-ui.jar from here](https://github.com/nicolaferraro/camel-workshop/releases/download/v0.0.1/spring-boot-ui-0.0.1.jar). 14 | 15 | From the folder containing the jar execute: 16 | 17 | ``` 18 | java -jar spring-boot-ui-0.0.1.jar 19 | ``` 20 | 21 | The application is available at the following URL: [http://localhost:4000/](http://localhost:4000/). 22 | 23 | ## Angular UI Source code 24 | The source code of the UI is contained in [app/ui](/app/ui) and can be run from there using the following command: 25 | 26 | ``` 27 | npm install 28 | ng serve --open 29 | ``` 30 | 31 | It requires that [Node JS](https://nodejs.org/en/download/), [npm](https://docs.npmjs.com/cli/install) and [Angular CLI](https://cli.angular.io/) are installed. 32 | 33 | ## Spring-Boot UI source code 34 | 35 | The source code of the spring-boot based UI mentioned above is here: [/app/spring-boot-ui](/app/spring-boot-ui). 36 | 37 | does not contain the Angular source code but but a compiled version of it. 38 | The compiled version is static (no server code, only javascript running on the browser side) and can be obtained using the following command: 39 | 40 | ``` 41 | # from app/ui 42 | npm install 43 | ng build 44 | ``` 45 | 46 | The compiled version of the ui is placed by `ng` in the `app/ui/dist` directory. 47 | The content of that directory has been copied into [/app/spring-boot-ui/src/main/resources/static](/app/spring-boot-ui/src/main/resources/static) directory and is 48 | served automatically by spring-boot when running the application. 49 | 50 | An equivalent way to run the spring-boot UI application is to clone this repository and run `mvn clean spring-boot:run` from the [/app/spring-boot-ui](/app/spring-boot-ui) directory. 51 | 52 | -------------------------------------------------------------------------------- /app/credit/src/main/java/org/apache/camel/workshop/credit/CreditStoreImpl.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.credit; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.TreeMap; 9 | 10 | @Component("creditStore") 11 | public class CreditStoreImpl implements CreditStore { 12 | 13 | private static final int BASE_CREDIT = 30; 14 | 15 | private Map accounts = new TreeMap<>(); 16 | 17 | private Map payments = new TreeMap<>(); 18 | 19 | @Override 20 | public synchronized void add(Payment payment) { 21 | if (payments.containsKey(payment.getReference())) { 22 | throw new IllegalStateException("Order already acquired"); 23 | } 24 | 25 | Integer current = accounts.get(payment.getUser()); 26 | if (current == null) { 27 | current = BASE_CREDIT; 28 | } 29 | 30 | if (current - payment.getAmount() < 0) { 31 | payment.setActive(false); 32 | payments.put(payment.getReference(), payment); 33 | throw new IllegalStateException("Insufficient credit!"); 34 | } 35 | 36 | payment.setActive(true); 37 | 38 | int newCredit = current - payment.getAmount(); 39 | accounts.put(payment.getUser(), newCredit); 40 | payments.put(payment.getReference(), payment); 41 | } 42 | 43 | @Override 44 | public synchronized void remove(String ref) { 45 | if (payments.containsKey(ref) && payments.get(ref).isActive()) { 46 | Payment payment = payments.get(ref); 47 | int current = accounts.get(payment.getUser()); 48 | accounts.put(payment.getUser(), current + payment.getAmount()); 49 | // set inactive for subsequent cancellations 50 | payment.setActive(false); 51 | } else if (!payments.containsKey(ref)) { 52 | // prevents subsequent creation of the payment 53 | Payment dummy = new Payment(); 54 | dummy.setReference(ref); 55 | dummy.setAmount(0); 56 | dummy.setActive(false); 57 | payments.put(ref, dummy); 58 | } 59 | } 60 | 61 | @Override 62 | public synchronized List list() { 63 | return new ArrayList<>(this.payments.values()); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /app/gateway/src/main/java/org/apache/camel/workshop/gateway/Order.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.gateway; 2 | 3 | import javax.validation.Valid; 4 | import javax.validation.constraints.Max; 5 | import javax.validation.constraints.Min; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | public class Order { 12 | 13 | @NotNull 14 | private String reference; 15 | 16 | @NotNull 17 | private String user; 18 | 19 | @NotNull 20 | @Size(min = 1) 21 | @Valid 22 | private List items = new LinkedList<>(); 23 | 24 | private int price; 25 | 26 | public Order() { 27 | } 28 | 29 | public List getItems() { 30 | return items; 31 | } 32 | 33 | public void setItems(List items) { 34 | this.items = items; 35 | } 36 | 37 | 38 | public int getPrice() { 39 | return price; 40 | } 41 | 42 | public void setPrice(int price) { 43 | this.price = price; 44 | } 45 | 46 | public String getReference() { 47 | return reference; 48 | } 49 | 50 | public void setReference(String reference) { 51 | this.reference = reference; 52 | } 53 | 54 | public String getUser() { 55 | return user; 56 | } 57 | 58 | public void setUser(String user) { 59 | this.user = user; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "Order{" + 65 | "reference='" + reference + '\'' + 66 | ", user='" + user + '\'' + 67 | ", items=" + items + 68 | ", price=" + price + 69 | '}'; 70 | } 71 | 72 | public static class OrderItem { 73 | 74 | @NotNull 75 | private String id; 76 | 77 | @Min(1) 78 | @Max(100) 79 | private int amount; 80 | 81 | public OrderItem() { 82 | } 83 | 84 | public OrderItem(String itemId, int amount) { 85 | this.id = itemId; 86 | this.amount = amount; 87 | } 88 | 89 | public String getId() { 90 | return id; 91 | } 92 | 93 | public void setId(String id) { 94 | this.id = id; 95 | } 96 | 97 | public int getAmount() { 98 | return amount; 99 | } 100 | 101 | public void setAmount(int amount) { 102 | this.amount = amount; 103 | } 104 | 105 | @Override 106 | public String toString() { 107 | return "OrderItem{" + 108 | "id='" + id + '\'' + 109 | ", amount=" + amount + 110 | '}'; 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /app/recommendation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.apache.camel.workshop 7 | recommendation 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Recommendation Service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.apache.camel 34 | camel-spring-boot-starter 35 | 2.22.1 36 | 37 | 38 | org.apache.camel 39 | camel-servlet-starter 40 | 2.22.1 41 | 42 | 43 | org.apache.camel 44 | camel-jackson-starter 45 | 2.22.1 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-actuator 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-devtools 56 | runtime 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-test 61 | test 62 | 63 | 64 | 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-maven-plugin 70 | 71 | 72 | 73 | 74 | 75 | 76 | openshift 77 | 78 | 79 | 80 | 81 | io.fabric8 82 | fabric8-maven-plugin 83 | 3.5.39 84 | 85 | 86 | 87 | resource 88 | build 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /app/ui/src/app/items/items.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {ItemService} from '../item.service'; 3 | import {Item} from "../datatypes"; 4 | import {DataService} from "../data.service"; 5 | 6 | @Component({ 7 | selector: 'app-items', 8 | templateUrl: './items.component.html', 9 | styleUrls: ['./items.component.css'] 10 | }) 11 | export class ItemsComponent implements OnInit { 12 | 13 | items: Item[]; 14 | 15 | total: number; 16 | 17 | cart: Map; 18 | 19 | message: string; 20 | 21 | loaded: boolean; 22 | 23 | constructor(private itemsService: ItemService, private data: DataService) { } 24 | 25 | getItems(): void { 26 | 27 | this.itemsService.getCatalog().subscribe(catalog => this.data.updateCatalog(catalog), error => { 28 | this.data.publishMessage({ 29 | title: "Error!", 30 | content: "Cannot load catalog", 31 | refresh: true, 32 | error: true 33 | }) 34 | }); 35 | 36 | this.data.catalog 37 | .subscribe(catalog => { 38 | 39 | let arr = []; 40 | for (let key in catalog.items) { 41 | arr.push(catalog.items[key]); 42 | } 43 | this.items = arr; 44 | 45 | this.items.forEach(item => { 46 | if (!this.cart.get(item.id)) { 47 | this.cart.set(item.id, 0); 48 | } 49 | }); 50 | 51 | this.loaded = true; 52 | }); 53 | } 54 | 55 | more(item: Item): void { 56 | let currentVal = this.cart.get(item.id) || 0; 57 | let newVal = currentVal < item.stockUnits ? currentVal + 1 : currentVal; 58 | this.total+=newVal-currentVal; 59 | this.cart.set(item.id, newVal); 60 | } 61 | 62 | less(item: Item): void { 63 | let currentVal = this.cart.get(item.id) || 0; 64 | let newVal = currentVal > 0 ? currentVal - 1 : 0; 65 | this.total+=newVal-currentVal; 66 | this.cart.set(item.id, newVal); 67 | } 68 | 69 | reset(): void { 70 | this.total = 0; 71 | this.cart.forEach((n,k) => this.cart.set(k, 0)); 72 | } 73 | 74 | checkout(): void { 75 | this.itemsService.makeOrder(this.cart) 76 | .subscribe(next => { 77 | this.message = "Order sent correctly!"; 78 | this.reset(); 79 | this.data.triggerModelChange(); 80 | this.data.publishMessage({ 81 | title: "Done!", 82 | content: "Order submitted successfully", 83 | refresh: false, 84 | error: false 85 | }) 86 | }, error => { 87 | this.message = "There were problems while sending the order!"; 88 | this.data.triggerModelChange(); 89 | this.data.publishMessage({ 90 | title: "Error!", 91 | content: "Errors while submitting the order", 92 | refresh: true, 93 | error: true 94 | }) 95 | }); 96 | 97 | } 98 | 99 | ngOnInit() { 100 | this.total = 0; 101 | this.cart = new Map(); 102 | this.getItems(); 103 | 104 | this.data.modelChanges.subscribe(change => { 105 | this.getItems(); 106 | }); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /app/credit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.apache.camel.workshop 7 | credit 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Credit Service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.apache.camel 34 | camel-spring-boot-starter 35 | 2.22.1 36 | 37 | 38 | org.apache.camel 39 | camel-servlet-starter 40 | 2.22.1 41 | 42 | 43 | org.apache.camel 44 | camel-jackson-starter 45 | 2.22.1 46 | 47 | 48 | org.apache.camel 49 | camel-bean-validator-starter 50 | 2.22.1 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-actuator 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-devtools 61 | runtime 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | 81 | openshift 82 | 83 | 84 | 85 | 86 | io.fabric8 87 | fabric8-maven-plugin 88 | 3.5.39 89 | 90 | 91 | 92 | resource 93 | build 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /app/inventory/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.apache.camel.workshop 7 | inventory 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Inventory Service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.apache.camel 34 | camel-spring-boot-starter 35 | 2.22.1 36 | 37 | 38 | org.apache.camel 39 | camel-servlet-starter 40 | 2.22.1 41 | 42 | 43 | org.apache.camel 44 | camel-jackson-starter 45 | 2.22.1 46 | 47 | 48 | org.apache.camel 49 | camel-swagger-java-starter 50 | 2.22.1 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-actuator 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-devtools 61 | runtime 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | 81 | openshift 82 | 83 | 84 | 85 | 86 | io.fabric8 87 | fabric8-maven-plugin 88 | 3.5.39 89 | 90 | 91 | 92 | resource 93 | build 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /app/ui/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/ui/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/InventoryRoutes.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | import org.apache.camel.builder.RouteBuilder; 4 | import org.apache.camel.model.rest.RestBindingMode; 5 | import org.apache.camel.model.rest.RestParamType; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class InventoryRoutes extends RouteBuilder { 10 | 11 | @Override 12 | public void configure() { 13 | 14 | /* 15 | * Main REST Config 16 | */ 17 | 18 | restConfiguration() 19 | .bindingMode(RestBindingMode.json) 20 | .enableCORS(true) 21 | .apiContextPath("/doc") 22 | .apiProperty("api.title", "Inventory API") 23 | .apiProperty("api.version", "1.0"); 24 | 25 | 26 | /* 27 | * GET /items 28 | */ 29 | 30 | rest().get("/items") 31 | .responseMessage() 32 | .code(200).message("Ok") 33 | .endResponseMessage() 34 | .route().id("getItems") 35 | .bean("inventory", "getCatalog"); 36 | 37 | 38 | /* 39 | * GET /purchases 40 | */ 41 | 42 | rest().get("/purchases") 43 | .responseMessage() 44 | .code(200).message("Ok") 45 | .endResponseMessage() 46 | .route().id("getPurchases") 47 | .bean("inventory", "getPurchases"); 48 | 49 | /* 50 | * POST /purchases/{ref}/items/{id} 51 | */ 52 | 53 | 54 | rest().post("/purchases/{ref}/items/{id}") 55 | .param() 56 | .name("ref") 57 | .description("Reference code of the purchase") 58 | .type(RestParamType.path) 59 | .endParam() 60 | .param() 61 | .name("id") 62 | .description("Item Id") 63 | .type(RestParamType.path) 64 | .endParam() 65 | .param() 66 | .name("amount") 67 | .description("Amount to buy") 68 | .type(RestParamType.query) 69 | .required(false) 70 | .endParam() 71 | .responseMessage() 72 | .code(200).message("Ok") 73 | .endResponseMessage() 74 | .route().id("addToPurchase") 75 | .choice() 76 | .when(simple("${header.amount} == null")) 77 | .setHeader("amount", constant(1)) 78 | .end() 79 | .validate(header("amount").convertTo(Integer.class).isGreaterThan(0)) 80 | .bean("inventory", "addToPurchase(${header.ref}, ${header.id}, ${header.amount})"); 81 | 82 | 83 | /* 84 | * DELETE /purchases/{ref} 85 | */ 86 | 87 | rest().delete("/purchases/{ref}") 88 | .param() 89 | .name("ref") 90 | .description("Reference code of the purchase") 91 | .type(RestParamType.path) 92 | .endParam() 93 | .responseMessage() 94 | .code(200).message("Ok") 95 | .endResponseMessage() 96 | .route().id("deletePurchase") 97 | .bean("inventory", "cancelPurchase(${header.ref})"); 98 | 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/inventory/src/main/java/org/apache/camel/workshop/inventory/InventoryImpl.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.inventory; 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.PostConstruct; 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @Component("inventory") 15 | public class InventoryImpl implements Inventory { 16 | 17 | private Catalog catalog; 18 | 19 | private Map purchases; 20 | 21 | @PostConstruct 22 | public void init() throws IOException { 23 | catalog = new ObjectMapper() 24 | .readValue(getClass().getResourceAsStream("/catalog.json"), Catalog.class); 25 | 26 | purchases = new HashMap<>(); 27 | } 28 | 29 | @Override 30 | public Catalog getCatalog() { 31 | return this.catalog; 32 | } 33 | 34 | @Override 35 | public void addToPurchase(String purchaseReferenceCode, String itemId, int amount) { 36 | synchronized (this) { 37 | if (amount <= 0) { 38 | throw new IllegalArgumentException("Amount must be positive: " + amount); 39 | } 40 | 41 | if (purchaseReferenceCode != null && isCanceled(purchaseReferenceCode)) { 42 | throw new IllegalStateException("Purchase reference code has been cancelled : " + purchaseReferenceCode); 43 | } 44 | 45 | Item item = this.catalog.getItems().get(itemId); 46 | if (item == null) { 47 | throw new IllegalArgumentException("Item " + itemId + " not found"); 48 | } 49 | 50 | if (item.getStockUnits() < amount) { 51 | throw new IllegalStateException("Insufficent items for id: " + itemId); 52 | } 53 | 54 | item.setStockUnits(item.getStockUnits() - amount); 55 | 56 | if (purchaseReferenceCode != null) { 57 | Purchase refPurchase = this.purchases.computeIfAbsent(purchaseReferenceCode, Purchase::new); 58 | int oldAmount = refPurchase.getItems().getOrDefault(itemId, 0); 59 | refPurchase.getItems().put(itemId, oldAmount + amount); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public List getPurchases() { 66 | synchronized (this) { 67 | return new ArrayList<>(this.purchases.values()); 68 | } 69 | } 70 | 71 | @Override 72 | public void cancelPurchase(String purchaseReferenceCode) { 73 | synchronized (this) { 74 | boolean alreadyCanceled = isCanceled(purchaseReferenceCode); 75 | 76 | if (!alreadyCanceled) { 77 | 78 | if (this.purchases.get(purchaseReferenceCode) == null) { 79 | Purchase canceled = new Purchase(purchaseReferenceCode); 80 | canceled.setActive(false); 81 | this.purchases.put(purchaseReferenceCode, canceled); 82 | } else { 83 | Purchase purchase = this.purchases.get(purchaseReferenceCode); 84 | purchase.setActive(false); 85 | for (String itemId : purchase.getItems().keySet()) { 86 | Item item = catalog.getItems().get(itemId); 87 | // restore the stock units 88 | item.setStockUnits(item.getStockUnits() + purchase.getItems().get(itemId)); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | private boolean isCanceled(String purchaseReferenceCode) { 96 | return this.purchases.containsKey(purchaseReferenceCode) && !this.purchases.get(purchaseReferenceCode).isActive(); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/ui/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "camel-workshop-ui": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/camel-workshop-ui", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css" 27 | ], 28 | "scripts": [] 29 | }, 30 | "configurations": { 31 | "production": { 32 | "fileReplacements": [ 33 | { 34 | "replace": "src/environments/environment.ts", 35 | "with": "src/environments/environment.prod.ts" 36 | } 37 | ], 38 | "optimization": true, 39 | "outputHashing": "all", 40 | "sourceMap": false, 41 | "extractCss": true, 42 | "namedChunks": false, 43 | "aot": true, 44 | "extractLicenses": true, 45 | "vendorChunk": false, 46 | "buildOptimizer": true 47 | } 48 | } 49 | }, 50 | "serve": { 51 | "builder": "@angular-devkit/build-angular:dev-server", 52 | "options": { 53 | "browserTarget": "camel-workshop-ui:build" 54 | }, 55 | "configurations": { 56 | "production": { 57 | "browserTarget": "camel-workshop-ui:build:production" 58 | } 59 | } 60 | }, 61 | "extract-i18n": { 62 | "builder": "@angular-devkit/build-angular:extract-i18n", 63 | "options": { 64 | "browserTarget": "camel-workshop-ui:build" 65 | } 66 | }, 67 | "test": { 68 | "builder": "@angular-devkit/build-angular:karma", 69 | "options": { 70 | "main": "src/test.ts", 71 | "polyfills": "src/polyfills.ts", 72 | "tsConfig": "src/tsconfig.spec.json", 73 | "karmaConfig": "src/karma.conf.js", 74 | "styles": [ 75 | "src/styles.css" 76 | ], 77 | "scripts": [], 78 | "assets": [ 79 | "src/favicon.ico", 80 | "src/assets" 81 | ] 82 | } 83 | }, 84 | "lint": { 85 | "builder": "@angular-devkit/build-angular:tslint", 86 | "options": { 87 | "tsConfig": [ 88 | "src/tsconfig.app.json", 89 | "src/tsconfig.spec.json" 90 | ], 91 | "exclude": [ 92 | "**/node_modules/**" 93 | ] 94 | } 95 | } 96 | } 97 | }, 98 | "camel-workshop-ui-e2e": { 99 | "root": "e2e/", 100 | "projectType": "application", 101 | "architect": { 102 | "e2e": { 103 | "builder": "@angular-devkit/build-angular:protractor", 104 | "options": { 105 | "protractorConfig": "e2e/protractor.conf.js", 106 | "devServerTarget": "camel-workshop-ui:serve" 107 | } 108 | }, 109 | "lint": { 110 | "builder": "@angular-devkit/build-angular:tslint", 111 | "options": { 112 | "tsConfig": "e2e/tsconfig.e2e.json", 113 | "exclude": [ 114 | "**/node_modules/**" 115 | ] 116 | } 117 | } 118 | } 119 | } 120 | }, 121 | "defaultProject": "camel-workshop-ui" 122 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop 2 | 3 | This is a Apache Camel Workshop "from zero to hero". 4 | It starts from basic features, like how to expose a REST endpoint, to building a 5 | complex system composed of multiple microservices and coordinating actions 6 | using the *Saga* pattern to obtain consistency. 7 | 8 | All artifacts produced in the workshop are present under the /[app](/app) directory, but we suggest 9 | to follow the step-by-step guide. 10 | 11 | ## Slides 12 | 13 | The slides for this workshop are [located here](/doc/slides.pdf). 14 | 15 | ## Final Result 16 | 17 | You can check the [Youtube video](https://youtu.be/hBbGXnb0N7A) to see all services in action. 18 | 19 | The final result of the workshop is a "Plush Shop Demo". 20 | 21 | ![Plush Shop Catalog](/doc/catalog.png "Plush Shop Catalog") 22 | 23 | 24 | A demo user can make purchases on the Plush Shop, if he has enough credit... 25 | 26 | ![Plush Shop Purchase](/doc/purchases.png "Plush Shop Purchases") 27 | 28 | ## Architecture 29 | 30 | The system is composed of a Angular 6 UI interface backed by **4** microservices. 31 | 32 | ![Plush Shop Architecture](/doc/camel-workshop-architecture.png "Plush Shop Architecture") 33 | 34 | The role of each service is: 35 | - **UI** (Angular 6 on Node.js or any static HTTP server): a graphical user interface that is provided for the workshop and can 36 | be used to test the full application; 37 | - **Recommendation** (Camel on Spring-Boot): a simple recommendation service that provide ratings for the best items; 38 | - **Credit** (Camel on Spring-Boot): manages how much money is owned by the user(s); 39 | - **Inventory** (Camel on Spring-Boot): manages items present in stock and movement out of it; 40 | - **Gateway** (Camel on Spring-Boot): provides uniform access to all other services and orchestrates calls to them. 41 | 42 | ## Requirements 43 | 44 | - Java 8 45 | - Maven 3.5.x 46 | - Your favourite IDE 47 | - HTTPie (to test endpoints) 48 | - Minishift and Openshift client tools (optional) 49 | 50 | HTTPie can be [installed on Linux, OSX and Windows](https://httpie.org/doc#installation). 51 | 52 | **For Windows users:** Installation of HTTPie on Windows requires `pip` that is included in the [Python installer](https://www.python.org/downloads/windows/). 53 | Read [this thread](https://stackoverflow.com/questions/4750806/how-do-i-install-pip-on-windows) if you need to troubleshoot. 54 | Alternatively, you can use a graphical tool like [Postman](https://www.getpostman.com/) to test endpoints, but instructions 55 | on this tutorial are given for **HTTPie** only. 56 | 57 | The **Minishift requirement is optional**. Minishift allows you to start full Openshift pseudo-cluster in your 58 | development machine and run your service as containerized images. 59 | You can [download the binary from the Minishift release page](https://github.com/minishift/minishift/releases/tag/v1.18.0). 60 | 61 | Refer to the ["quick start guide"](https://docs.openshift.org/latest/minishift/getting-started/index.html) to install Minishift on your laptop. 62 | [The section about the Openshift client binary](https://docs.openshift.org/latest/minishift/openshift/openshift-client-binary.html) explains also how to 63 | include the `oc` binary into the path in order to interact with the cluster. 64 | 65 | ## Part 1: writing the services 66 | 67 | ### 1. Recommendation Service 68 | 69 | [Here's how to create the recommendation service](/doc/recommendation.md). 70 | 71 | ### 2. Credit Service 72 | 73 | [Here's how to create the credit service](/doc/credit.md). 74 | 75 | ### 3. Inventory Service 76 | 77 | [Here's how to create the inventory service](/doc/inventory.md). 78 | 79 | ### 4. Gateway Service 80 | 81 | [Here's how to create the gateway service](/doc/gateway.md). 82 | 83 | ### 5. Running the UI 84 | 85 | [Here's how to run the UI](/doc/ui.md). 86 | 87 | ## Part 2: advanced features 88 | 89 | ### 6. Circuit breaking and Caching 90 | 91 | [Let's add a circuit breaker and caching logic](/doc/circuit-breaker.md). 92 | 93 | ### 7. Adding Sagas 94 | 95 | [Let's see how to fix inconsistencies](/doc/saga.md). 96 | 97 | ### 8. Running on Kubernetes and OpenShift (Optional) 98 | 99 | [Let's see how to run everything on Kubernetes and Openshift](/doc/openshift.md). 100 | -------------------------------------------------------------------------------- /doc/saga.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop: Saga 2 | 3 | One of the most difficult problems to solve in a Microservice architecture is: 4 | **how do we obtain consistency in a highly distributed system?** 5 | 6 | Just make the following test: 7 | - Open two tabs in a browser 8 | - On the first tab, buy all items present in stock for a plush 9 | - On the second tab (not synchronized), try to buy the same plush and others 10 | 11 | What you see is called **inconsistency** and fixing it is really hard... without Camel! 12 | 13 | ## Declaring a Saga 14 | 15 | You can declare the POST `/order` action to be part of a Saga. All actions belonging to a saga are 16 | *compensated* in case of issues when executing the normal workflow. 17 | 18 | We can define the saga in the **gateway service**. 19 | 20 | First, let's define the type of Saga service we want to use: 21 | 22 | ```java 23 | // below rest configuration, inside the configure() method, put 24 | getContext().addService(new InMemorySagaService(), true); 25 | ``` 26 | 27 | A `InMemorySagaService` is a Saga service that keeps its state in memory. 28 | This means that it's able to maintain consistency, but if you shut down the *gateway* 29 | all information about in-flight sagas is lost. 30 | 31 | Camel of course has other options that survive failures (see [camel-lra](https://github.com/apache/camel/blob/master/components/camel-lra/src/main/docs/lra.adoc)). 32 | 33 | Now let's wrap the create-order service with a `saga` processor: 34 | 35 | ```java 36 | rest().post("/orders") 37 | .type(Order.class) 38 | .route() 39 | .saga() 40 | .compensation("direct:cancelOrder") 41 | .option("order", simple("${body}")) 42 | .unmarshal().json(JsonLibrary.Jackson, Order.class) 43 | .to("bean-validator:validateOrder") 44 | .multicast().parallelProcessing() 45 | .to("direct:payOrder") 46 | .to("direct:purchaseOrderItems") 47 | .end() 48 | .marshal().json(JsonLibrary.Jackson) 49 | .end(); 50 | ``` 51 | 52 | 53 | Note that we have declared the action to execute in case of failure of the saga main action, 54 | that is `direct:cancelOrder`. 55 | 56 | Camel will pass to that route the order received from the UI in the `order` header. 57 | 58 | Let's write the **compensating action**: 59 | 60 | ```java 61 | from("direct:cancelOrder") 62 | .setBody(header("order")).convertBodyTo(String.class) 63 | .unmarshal().json(JsonLibrary.Jackson, Order.class) 64 | .setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.DELETE)) 65 | .multicast().parallelProcessing() 66 | .serviceCall("credit/api/payments/${body.reference}") 67 | .serviceCall("inventory/api/purchases/${body.reference}"); 68 | ``` 69 | 70 | We'll cancel both payment and purchase **in parallel**. 71 | 72 | If the `direct:cancelOrder` operation will fail, the Saga service will retry again, up to a configurable number of attempts. 73 | This means also that the compensating action must be: 74 | - **Idempotent**: multiple compensations must have the same result as one compensation 75 | - **Commutative**: if compensation happens *before* the normal action for some network issue, the service should refuse to execute the normal action (considering it already compensated). 76 | 77 | If you look at the *credit service* and *inventory service*, you'll see that the implementation of the business logic services 78 | take into account these two properties. And it wasn't so difficult to implement them... 79 | 80 | 81 | Now, considering that the business logic invariants are: 82 | - **You cannot spend more than $30** 83 | - **You cannot buy a item if it's not present in stock** 84 | 85 | **If you don't respect one of them, your order will be automatically canceled.** 86 | 87 | You may see that the order is not canceled immediately, but sometimes you need to refresh the page to see 88 | the result. That is what we call **eventual consistency**. 89 | 90 | **Important Note**: The *"eventual"* word in *"eventual consistency"* does not mean that *"there's eventuality that the result will be consistent"*, 91 | it means *"at one time in the future the result will be consistent for sure, but we it will not happen immediately in some cases (network partitions)"*. 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.apache.camel.workshop 7 | gateway 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Gateway Service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-tomcat 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-undertow 41 | 42 | 43 | 44 | org.apache.camel 45 | camel-spring-boot-starter 46 | 2.22.1 47 | 48 | 49 | org.apache.camel 50 | camel-servlet-starter 51 | 2.22.1 52 | 53 | 54 | org.apache.camel 55 | camel-undertow-starter 56 | 2.22.1 57 | 58 | 59 | org.apache.camel 60 | camel-jackson-starter 61 | 2.22.1 62 | 63 | 64 | org.apache.camel 65 | camel-bean-validator-starter 66 | 2.22.1 67 | 68 | 69 | org.apache.camel 70 | camel-hystrix-starter 71 | 2.22.1 72 | 73 | 74 | org.apache.camel 75 | camel-caffeine-starter 76 | 2.22.1 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-actuator 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-devtools 87 | runtime 88 | 89 | 90 | org.springframework.boot 91 | spring-boot-starter-test 92 | test 93 | 94 | 95 | 96 | 97 | 98 | 99 | org.springframework.boot 100 | spring-boot-maven-plugin 101 | 102 | 103 | 104 | 105 | 106 | 107 | openshift 108 | 109 | 110 | 111 | 112 | io.fabric8 113 | fabric8-maven-plugin 114 | 3.5.39 115 | 116 | 117 | 118 | resource 119 | build 120 | 121 | 122 | 123 | 124 | 125 | 126 | inventory:8081 127 | credit:8082 128 | recommendation:8083 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /doc/recommendation.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop: Recommendation Service 2 | 3 | The recommendation service is a **dummy service** in this demo, but I've shown how to create a fully 4 | fledged recommendation system using Apache Camel and Apache Spark on Openshift in 5 | [some recent talks](https://www.nicolaferraro.me/2017/03/10/voxxed-bucharest-extending-devops-to-big-data-applications-with-kubernetes/) 6 | if you're interested in the topic. 7 | 8 | ## Creating the project 9 | 10 | We'll create a *Camel* on *spring-boot* project: 11 | 12 | - Go to [https://start.spring.io](https://start.spring.io) 13 | - Select Spring-Boot version *2.0.5* 14 | - Add to the list of dependencies: `Apache Camel`, `Web`, `DevTools` (for auto-reload, see below) 15 | - Set groupId to `org.apache.camel.workshop` and project name to `recommendation` 16 | - Download the project archive and extract 17 | - Open it in your IDE 18 | 19 | The `DevTools` dependency is *optional* but it's often a nice addon during development, especially for the **auto-reload** feature. 20 | You can keep your application running (see the final part of this section for how to do it) 21 | while changing your code: each time you save and recompile using your IDE, the running application is reloaded automatically and you see 22 | the changes immediately. 23 | 24 | You can run the following command from the root directory of the project (where `pom.xml` is located) to do a first build. 25 | 26 | ``` 27 | mvn clean install 28 | ``` 29 | 30 | Dependencies are downloaded from Maven repositories, tests are executed (they should pass), 31 | then a standalone *fat-jar* is created in the `target` directory. 32 | 33 | The `fat-jar` contains all required dependencies and is runnable (you can run it with `java -jar ./target/recommendation-0.0.1-SNAPSHOT.jar` if you want, but we haven't added any code yet). 34 | 35 | ## Adding a "Hello World" route 36 | 37 | The timer is the simplest component that you may want to use. 38 | We'll add a simple timer to check that the application is working. 39 | 40 | Open the `RecommendationApplication` class and **right after the main method** put the following code: 41 | 42 | 43 | ```java 44 | @Component 45 | public static class Routes extends RouteBuilder { 46 | 47 | @Override 48 | public void configure() { 49 | 50 | from("timer:hello?period=3s") 51 | .log("Hello world!"); 52 | 53 | } 54 | 55 | } 56 | ``` 57 | 58 | Add the necessary imports, your IDE should suggest them. 59 | Now from the root of the project (where `pom.xml` is located) run the application with: 60 | 61 | ``` 62 | mvn clean spring-boot:run 63 | ``` 64 | 65 | ## Adding the recommendation service 66 | 67 | In order to expose a rest service you need first to add a Camel component that 68 | supports the REST DSL. Camel has multiple components, but when running on a service 69 | with the spring-web module, the best choice is the `camel-servlet` component, together with `camel-jackson` for Json support. 70 | 71 | Add the following to the `pom.xml`: 72 | 73 | ```xml 74 | 75 | org.apache.camel 76 | camel-servlet-starter 77 | 2.22.1 78 | 79 | 80 | org.apache.camel 81 | camel-jackson-starter 82 | 2.22.1 83 | 84 | ``` 85 | 86 | Camel ships also a **BOM** (Bill Of Materials) that helps to manage automatically dependency versions. 87 | [Follow this section if you want to know more](using-bom.md) about this feature. 88 | 89 | 90 | Now replace the hello world route with the rest route. 91 | 92 | ```java 93 | @Component 94 | public static class Routes extends RouteBuilder { 95 | 96 | @Override 97 | public void configure() { 98 | 99 | restConfiguration().bindingMode(RestBindingMode.json); 100 | 101 | rest().get("/recommendations") 102 | .route() 103 | .setBody(constant(Collections.singletonList("i99"))) 104 | .log("Recommending ${body}"); 105 | 106 | } 107 | 108 | } 109 | ``` 110 | 111 | It's a fake recommendation suggesting only item with id `i99` (you'll see what it is). 112 | 113 | Camel will expose the endpoint on the URL: `http://localhost:8080/camel/recommendations`. 114 | For this demo, **we want to use a different context path**, so we configure it on the `application.properties` file. 115 | 116 | We want also to start multiple endpoints on the same development machine, so we set the **server port to 8083** for this service. 117 | 118 | Edit the `src/main/resources/application.properties` file and put the following: 119 | 120 | ```properties 121 | camel.component.servlet.mapping.context-path=/api/* 122 | server.port=8083 123 | ``` 124 | 125 | **Note**: Some IDE, like IntelliJ IDEA Ultimate, provide autocompletion of available properties. 126 | 127 | ## Start the service 128 | 129 | Start the app with `mvn clean spring-boot:run` and go to the following link: [http://localhost:8083/api/recommendations](http://localhost:8083/api/recommendations). 130 | 131 | You'll see the recommendations as json data. 132 | 133 | You can stop the service by just pressing `ctrl + c` on the terminal that started the application. 134 | -------------------------------------------------------------------------------- /app/gateway/src/main/java/org/apache/camel/workshop/gateway/GatewayRoutes.java: -------------------------------------------------------------------------------- 1 | package org.apache.camel.workshop.gateway; 2 | 3 | import org.apache.camel.Exchange; 4 | import org.apache.camel.builder.RouteBuilder; 5 | import org.apache.camel.component.caffeine.CaffeineConstants; 6 | import org.apache.camel.http.common.HttpMethods; 7 | import org.apache.camel.impl.saga.InMemorySagaService; 8 | import org.apache.camel.model.dataformat.JsonLibrary; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | @Component 15 | public class GatewayRoutes extends RouteBuilder { 16 | 17 | 18 | 19 | @Override 20 | public void configure() throws Exception { 21 | 22 | restConfiguration() 23 | .component("servlet") 24 | .enableCORS(true); 25 | 26 | 27 | getContext().addService(new InMemorySagaService(), true); 28 | 29 | /* 30 | * Proxied requests 31 | */ 32 | 33 | rest().get("/payments") 34 | .route() 35 | .serviceCall("credit/api/payments"); 36 | 37 | rest().get("/purchases") 38 | .route() 39 | .serviceCall("inventory/api/purchases"); 40 | 41 | /* 42 | * Items 43 | */ 44 | 45 | rest().get("/items") 46 | .route() 47 | .serviceCall("inventory/api/items") 48 | .unmarshal().json(JsonLibrary.Jackson, Catalog.class) 49 | .enrichWith("direct:recommendation") 50 | .body(Catalog.class, List.class, this::recommend) 51 | .marshal().json(JsonLibrary.Jackson); 52 | 53 | /* 54 | * Recommendations 55 | */ 56 | 57 | from("direct:recommendation") 58 | .hystrix() 59 | .setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.GET)) 60 | .serviceCall("recommendation/api/recommendations") 61 | .unmarshal().json(JsonLibrary.Jackson, List.class) 62 | .setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_PUT)) 63 | .setHeader(CaffeineConstants.KEY, constant("recommendation")) 64 | .to("caffeine-cache:global") 65 | .endHystrix() 66 | .onFallback() 67 | .setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_GET)) 68 | .setHeader(CaffeineConstants.KEY, constant("recommendation")) 69 | .to("caffeine-cache:global") 70 | .choice() 71 | .when(header("CamelCaffeineActionHasResult").isNotEqualTo(true)) 72 | .setBody(constant(Collections.emptyList())) 73 | .end() 74 | .end(); 75 | 76 | 77 | /* 78 | * Orders 79 | */ 80 | 81 | rest().post("/orders") 82 | .type(Order.class) 83 | .route() 84 | .saga() 85 | .compensation("direct:cancelOrder") 86 | .option("order", simple("${body}")) 87 | .unmarshal().json(JsonLibrary.Jackson, Order.class) 88 | .to("bean-validator:validateOrder") 89 | .multicast().parallelProcessing() 90 | .to("direct:payOrder") 91 | .to("direct:purchaseOrderItems") 92 | .end() 93 | .marshal().json(JsonLibrary.Jackson) 94 | .end(); 95 | 96 | from("direct:payOrder") 97 | .setBody().body(Order.class, this::createPayment) 98 | .marshal().json(JsonLibrary.Jackson) 99 | .serviceCall("credit/api/payments"); 100 | 101 | 102 | from("direct:purchaseOrderItems") 103 | .setHeader("reference", simple("${body.reference}")) 104 | .split().simple("${body.items}").parallelProcessing() 105 | .serviceCall("inventory/api/purchases/${header.reference}/items/${body.id}?amount=${body.amount}"); 106 | 107 | 108 | from("direct:cancelOrder") 109 | .setBody(header("order")).convertBodyTo(String.class) 110 | .unmarshal().json(JsonLibrary.Jackson, Order.class) 111 | .setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.DELETE)) 112 | .multicast().parallelProcessing() 113 | .serviceCall("credit/api/payments/${body.reference}") 114 | .serviceCall("inventory/api/purchases/${body.reference}"); 115 | 116 | 117 | } 118 | 119 | private Payment createPayment(Order order) { 120 | Payment payment = new Payment(); 121 | payment.setUser(order.getUser()); 122 | payment.setReference(order.getReference()); 123 | payment.setAmount(order.getPrice()); 124 | return payment; 125 | } 126 | 127 | private Catalog recommend(Catalog catalog, List recomm) { 128 | if (recomm != null && catalog != null && catalog.getItems() != null) { 129 | for (String recommItem : recomm) { 130 | Item item = catalog.getItems().get(recommItem); 131 | if (item != null) { 132 | item.setRecommended(true); 133 | } 134 | } 135 | } 136 | return catalog; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/credit/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 enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /app/gateway/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 enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /app/inventory/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 enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /app/recommendation/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 enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /app/spring-boot-ui/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 enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /app/spring-boot-ui/src/main/resources/static/runtime.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // install a JSONP callback for chunk loading 3 | /******/ function webpackJsonpCallback(data) { 4 | /******/ var chunkIds = data[0]; 5 | /******/ var moreModules = data[1]; 6 | /******/ var executeModules = data[2]; 7 | /******/ // add "moreModules" to the modules object, 8 | /******/ // then flag all "chunkIds" as loaded and fire callback 9 | /******/ var moduleId, chunkId, i = 0, resolves = []; 10 | /******/ for(;i < chunkIds.length; i++) { 11 | /******/ chunkId = chunkIds[i]; 12 | /******/ if(installedChunks[chunkId]) { 13 | /******/ resolves.push(installedChunks[chunkId][0]); 14 | /******/ } 15 | /******/ installedChunks[chunkId] = 0; 16 | /******/ } 17 | /******/ for(moduleId in moreModules) { 18 | /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { 19 | /******/ modules[moduleId] = moreModules[moduleId]; 20 | /******/ } 21 | /******/ } 22 | /******/ if(parentJsonpFunction) parentJsonpFunction(data); 23 | /******/ while(resolves.length) { 24 | /******/ resolves.shift()(); 25 | /******/ } 26 | /******/ 27 | /******/ // add entry modules from loaded chunk to deferred list 28 | /******/ deferredModules.push.apply(deferredModules, executeModules || []); 29 | /******/ 30 | /******/ // run deferred modules when all chunks ready 31 | /******/ return checkDeferredModules(); 32 | /******/ }; 33 | /******/ function checkDeferredModules() { 34 | /******/ var result; 35 | /******/ for(var i = 0; i < deferredModules.length; i++) { 36 | /******/ var deferredModule = deferredModules[i]; 37 | /******/ var fulfilled = true; 38 | /******/ for(var j = 1; j < deferredModule.length; j++) { 39 | /******/ var depId = deferredModule[j]; 40 | /******/ if(installedChunks[depId] !== 0) fulfilled = false; 41 | /******/ } 42 | /******/ if(fulfilled) { 43 | /******/ deferredModules.splice(i--, 1); 44 | /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]); 45 | /******/ } 46 | /******/ } 47 | /******/ return result; 48 | /******/ } 49 | /******/ 50 | /******/ // The module cache 51 | /******/ var installedModules = {}; 52 | /******/ 53 | /******/ // object to store loaded and loading chunks 54 | /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched 55 | /******/ // Promise = chunk loading, 0 = chunk loaded 56 | /******/ var installedChunks = { 57 | /******/ "runtime": 0 58 | /******/ }; 59 | /******/ 60 | /******/ var deferredModules = []; 61 | /******/ 62 | /******/ // The require function 63 | /******/ function __webpack_require__(moduleId) { 64 | /******/ 65 | /******/ // Check if module is in cache 66 | /******/ if(installedModules[moduleId]) { 67 | /******/ return installedModules[moduleId].exports; 68 | /******/ } 69 | /******/ // Create a new module (and put it into the cache) 70 | /******/ var module = installedModules[moduleId] = { 71 | /******/ i: moduleId, 72 | /******/ l: false, 73 | /******/ exports: {} 74 | /******/ }; 75 | /******/ 76 | /******/ // Execute the module function 77 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 78 | /******/ 79 | /******/ // Flag the module as loaded 80 | /******/ module.l = true; 81 | /******/ 82 | /******/ // Return the exports of the module 83 | /******/ return module.exports; 84 | /******/ } 85 | /******/ 86 | /******/ 87 | /******/ // expose the modules object (__webpack_modules__) 88 | /******/ __webpack_require__.m = modules; 89 | /******/ 90 | /******/ // expose the module cache 91 | /******/ __webpack_require__.c = installedModules; 92 | /******/ 93 | /******/ // define getter function for harmony exports 94 | /******/ __webpack_require__.d = function(exports, name, getter) { 95 | /******/ if(!__webpack_require__.o(exports, name)) { 96 | /******/ Object.defineProperty(exports, name, { 97 | /******/ configurable: false, 98 | /******/ enumerable: true, 99 | /******/ get: getter 100 | /******/ }); 101 | /******/ } 102 | /******/ }; 103 | /******/ 104 | /******/ // define __esModule on exports 105 | /******/ __webpack_require__.r = function(exports) { 106 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 107 | /******/ }; 108 | /******/ 109 | /******/ // getDefaultExport function for compatibility with non-harmony modules 110 | /******/ __webpack_require__.n = function(module) { 111 | /******/ var getter = module && module.__esModule ? 112 | /******/ function getDefault() { return module['default']; } : 113 | /******/ function getModuleExports() { return module; }; 114 | /******/ __webpack_require__.d(getter, 'a', getter); 115 | /******/ return getter; 116 | /******/ }; 117 | /******/ 118 | /******/ // Object.prototype.hasOwnProperty.call 119 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 120 | /******/ 121 | /******/ // __webpack_public_path__ 122 | /******/ __webpack_require__.p = ""; 123 | /******/ 124 | /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; 125 | /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); 126 | /******/ jsonpArray.push = webpackJsonpCallback; 127 | /******/ jsonpArray = jsonpArray.slice(); 128 | /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); 129 | /******/ var parentJsonpFunction = oldJsonpFunction; 130 | /******/ 131 | /******/ 132 | /******/ // run deferred modules from other chunks 133 | /******/ checkDeferredModules(); 134 | /******/ }) 135 | /************************************************************************/ 136 | /******/ ([]); 137 | //# sourceMappingURL=runtime.js.map -------------------------------------------------------------------------------- /doc/circuit-breaker.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop: Circuit Breaking and Caching 2 | 3 | So far the gateway application is mostly a dumb proxy (except for the order service), we are 4 | going to make it more complex by mixing inventory data with recommendations. 5 | 6 | We'll **make changes only to the gateway service** in the next sections. 7 | 8 | ## Add dependencies to the Gateway service 9 | 10 | We need the following dependencies in the gateway. 11 | 12 | ```xml 13 | 14 | org.apache.camel 15 | camel-hystrix-starter 16 | 2.22.1 17 | 18 | 19 | org.apache.camel 20 | camel-caffeine-starter 21 | 2.22.1 22 | 23 | ``` 24 | 25 | They are needed for: 26 | - `camel-hystrix`: we'll use it for circuit breaking 27 | - `camel-caffeine`: we'll use it to do simple caching 28 | 29 | 30 | ## Mix inventory data with recommendations 31 | 32 | Let's declare a route that, when called, will fetch data from the recommendation service. 33 | You should add it to the *gateway service* (*inside the `configure()` method*). 34 | 35 | ```java 36 | from("direct:recommendation") 37 | .setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.GET)) 38 | .serviceCall("recommendation/api/recommendations") 39 | .unmarshal().json(JsonLibrary.Jackson, List.class); 40 | ``` 41 | 42 | We can now **replace** the `/items` endpoint with the following to include recommendations: 43 | 44 | ```java 45 | rest().get("/items") 46 | .route() 47 | .serviceCall("inventory/api/items") 48 | .unmarshal().json(JsonLibrary.Jackson, Catalog.class) 49 | .enrichWith("direct:recommendation") 50 | .body(Catalog.class, List.class, this::recommend) 51 | .marshal().json(JsonLibrary.Jackson); 52 | ``` 53 | 54 | We use the `recommend()` method present in the class to mix the catalog with the list of items coming from the recommendation service. 55 | That method will set `recommended=true` on featured items. 56 | 57 | ## Check the new feature 58 | 59 | We assume that all backend services are still running (start them if they are stopped). 60 | 61 | Stop the *gateway service* and start it again in order to apply changes. **If you look at the UI, you should see some stars below the best item of the catalog**. 62 | 63 | Nice, but now **if you stop the recommendation service, the ui wont display the items anymore** if you refresh it (other errors may appear due to the missing catalog). 64 | 65 | How can we solve this issue? 66 | 67 | ## Adding a circuit breaker 68 | 69 | We can protect the app from failures of the recommendation service by adding a circuit breaker. 70 | 71 | Let's **rewrite** the `direct:recommendation` route to add the *Hystrix EIP*: 72 | 73 | ```java 74 | from("direct:recommendation") 75 | .hystrix() 76 | .setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.GET)) 77 | .serviceCall("recommendation/api/recommendations") 78 | .unmarshal().json(JsonLibrary.Jackson, List.class) 79 | .endHystrix() 80 | .onFallback() 81 | .setBody(constant(Collections.emptyList())) 82 | .end(); 83 | ``` 84 | 85 | Note the difference with the previous code: the *hystrix()* EIP has been added around the call to the recommendation service and in the last part of the route we have added a fallback strategy that should be used when the recommendation service is not available (return a *empty* recommendation). 86 | 87 | Now, **restart the gateway service** and try again. You'll see no recommendations if the recommendation service is down. 88 | 89 | It acts as a standard circuit breaker, e.g. it opens temporarily the circuit if too many request 90 | arrive to the recommendation service and its replies are slow. 91 | 92 | ## Adding a cache for recommendations 93 | 94 | Even with the circuit breaker, "stars" are shown only when the recommendation service is active. 95 | If we shut it down, recommendations are not displayed. 96 | 97 | It may be useful to **cache recommendations** and use the *last cached value as fallback* in the hystrix EIP. 98 | 99 | Let's **change** the `direct:recommendation` route to include caching using *caffeine*: 100 | 101 | ```java 102 | from("direct:recommendation") 103 | .hystrix() 104 | .setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.GET)) 105 | .serviceCall("recommendation/api/recommendations") 106 | .unmarshal().json(JsonLibrary.Jackson, List.class) 107 | .setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_PUT)) 108 | .setHeader(CaffeineConstants.KEY, constant("recommendation")) 109 | .to("caffeine-cache:global") 110 | .endHystrix() 111 | .onFallback() 112 | .setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_GET)) 113 | .setHeader(CaffeineConstants.KEY, constant("recommendation")) 114 | .to("caffeine-cache:global") 115 | .choice() 116 | .when(header("CamelCaffeineActionHasResult").isNotEqualTo(true)) 117 | .setBody(constant(Collections.emptyList())) 118 | .end() 119 | .end(); 120 | ``` 121 | 122 | We've used caffeine, but Camel supports **tons of different cache providers, local** (like *Caffeine*, *Ehcache*, ...) or 123 | also **distributed** (like *Infinispan*, *Redis*, *Hazelcast* ...). 124 | 125 | Using a **distributed cache** with *camel-infinispan* (or *camel-redis*, *camel-hazelcast*, ...) we can also substantially reduce the amount of requests forwarded to 126 | downstream services. When data rarely changes this is absolutely useful to improve system **scalability**. 127 | 128 | Caches in Camel have a lot of options to configure, like *data expiration* and *invalidation*. 129 | 130 | Now, you can run all services and refresh the UI. If you shut the recommendation service down, 131 | the **last cached recommendation is shown** in the UI. 132 | -------------------------------------------------------------------------------- /app/credit/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 Migwn, 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 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /app/gateway/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 Migwn, 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 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /app/inventory/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 Migwn, 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 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /app/recommendation/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 Migwn, 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 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /app/spring-boot-ui/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 Migwn, 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 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /doc/credit.md: -------------------------------------------------------------------------------- 1 | # Apache Camel Workshop: Credit Service 2 | 3 | The credit microservice manages payments done by the users. 4 | Each user is given a credit of $30 and can make payments to buy items from the store. 5 | 6 | The credit microservice expose REST API to manage payments. 7 | 8 | 9 | ## Creating the project 10 | 11 | We'll create a *Camel* on *spring-boot* project: 12 | 13 | - Go to [https://start.spring.io](https://start.spring.io) 14 | - Select Spring-Boot version *2.0.5* 15 | - Add to the list of dependencies: `Apache Camel`, `Web`, `DevTools` (for auto-reload) 16 | - Set groupId to `org.apache.camel.workshop` and project name to `credit` 17 | - Download the project archive and extract 18 | - Open it in your IDE 19 | 20 | As done for the previous service, you can do a first build from the root directory of the project (where `pom.xml` is located) with the command: 21 | 22 | ``` 23 | mvn clean install 24 | ``` 25 | 26 | ## Adding the business logic and supporting beans 27 | 28 | We'll not write the business logic, instead we'll start from existing classes and we'll wire APIs. 29 | 30 | Download the supporting classes from here and place them in the `org.apache.camel.workshop.credit` package: 31 | 32 | - [Payment.java](https://raw.githubusercontent.com/nicolaferraro/camel-workshop/master/app/credit/src/main/java/org/apache/camel/workshop/credit/Payment.java) (a bean) 33 | - [CreditStore.java](https://raw.githubusercontent.com/nicolaferraro/camel-workshop/master/app/credit/src/main/java/org/apache/camel/workshop/credit/CreditStore.java) (Credit store interface) 34 | - [CreditStoreImpl.java](https://raw.githubusercontent.com/nicolaferraro/camel-workshop/master/app/credit/src/main/java/org/apache/camel/workshop/credit/CreditStoreImpl.java) (Credit store implementation) 35 | 36 | (*right click, save link as...*) 37 | 38 | ## Adding Camel dependencies 39 | 40 | Let's start now writing REST endpoints with Camel. 41 | 42 | First, let's add the following dependencies to the `pom.xml` file. 43 | 44 | ```xml 45 | 46 | org.apache.camel 47 | camel-servlet-starter 48 | 2.22.1 49 | 50 | 51 | org.apache.camel 52 | camel-jackson-starter 53 | 2.22.1 54 | 55 | 56 | org.apache.camel 57 | camel-bean-validator-starter 58 | 2.22.1 59 | 60 | ``` 61 | 62 | We have seen the first two dependencies in the `recommendation` service. 63 | 64 | The `camel-bean-validator-starter` dependency will be used to validate the input in some services. 65 | 66 | ## Adding a endpoint to LIST Payments 67 | 68 | Create a empty `CreditRoutes` class: 69 | 70 | ```java 71 | package org.apache.camel.workshop.credit; 72 | 73 | import org.apache.camel.builder.RouteBuilder; 74 | import org.apache.camel.model.dataformat.JsonLibrary; 75 | import org.apache.camel.model.rest.RestParamType; 76 | import org.springframework.stereotype.Component; 77 | 78 | @Component 79 | public class CreditRoutes extends RouteBuilder { 80 | 81 | @Override 82 | public void configure() { 83 | 84 | // routes here 85 | 86 | } 87 | 88 | } 89 | ``` 90 | 91 | We will fill that class with all REST routes that we need. 92 | Add a `GET` endpoint like this: 93 | 94 | ```java 95 | rest().get("/payments") 96 | .route() 97 | .bean("creditStore", "list") 98 | .marshal().json(JsonLibrary.Jackson); 99 | ``` 100 | 101 | When Camel receives a request to list payments, we'll call the `list` method on the `creditStore` bean. 102 | The bean is published in the spring-boot registry and Camel retrieve it from there. 103 | 104 | The `marshal()` operation is used here to convert the `List` into the JSON format before sending it on the wire. 105 | 106 | Now, configure the server port (**8082** for this service) and the context path in the `src/main/resources/application.properties` file: 107 | 108 | ```properties 109 | camel.component.servlet.mapping.context-path=/api/* 110 | server.port=8082 111 | ``` 112 | 113 | You can now run the service with `mvn clean spring-boot:run`. 114 | The service is available in [http://localhost:8082/api/payments](http://localhost:8082/api/payments). 115 | 116 | ## Adding a endpoint to CREATE Payments 117 | 118 | When submitting a Payment, the client sends a JSON object and we must process it. 119 | 120 | Let's add the following route (*inside the `configure()` method, right after the previous `rest()` declaration*): 121 | 122 | ```java 123 | rest().post("/payments") 124 | .route() 125 | .unmarshal().json(JsonLibrary.Jackson, Payment.class) 126 | .to("bean-validator:validatePayment") 127 | .bean("creditStore", "add") 128 | .marshal().json(JsonLibrary.Jackson); 129 | 130 | ``` 131 | 132 | First of all, Camel `unmarshal()` the JSON object to convert it to a Java bean. 133 | 134 | Then, we call the `bean-validator:validatePayment` endpoint. It's a Camel endpoint that 135 | does bean validation (of `javax.validation` constraints annotations). [Documentation of the 136 | bean-validator component is here](https://github.com/apache/camel/blob/master/components/camel-bean-validator/src/main/docs/bean-validator-component.adoc). 137 | 138 | If you look at the `Payment` class, you'll notice it's prefilled with validating annotations: 139 | 140 | ```java 141 | public class Payment { 142 | 143 | @NotNull 144 | private String reference; 145 | 146 | @NotNull 147 | private String user; 148 | 149 | @NotNull 150 | @Min(0) 151 | private Integer amount; 152 | //... 153 | ``` 154 | 155 | After validating the bean, we send it to the `add` method of the `creditStore`. 156 | We `marshal()` the response to JSON (we echo the input data). 157 | 158 | ## Adding a endpoint to DELETE Payments 159 | 160 | Payments sometimes should be cancelled because of errors, so we add a specific endpoint for that (*inside the `configure()` method, right after the previous `rest()` declaration*). 161 | 162 | ```java 163 | 164 | rest().delete("/payments/{ref}") 165 | .param() 166 | .name("ref") 167 | .type(RestParamType.path) 168 | .description("Reference code") 169 | .endParam() 170 | .route() 171 | .bean("creditStore", "remove(${header.ref})"); 172 | ``` 173 | 174 | We use here a *path parameter* and we can declare it before the route. 175 | Describing path parameters is useful especially when we want to publish the Swagger/OpenAPI specs, 176 | but also help to make the code cleaner. 177 | 178 | The `{ref}` parameter will be mapped by Camel in a header and we use it to call the `remove` method on the `creditStore` bean. 179 | 180 | Note the new syntax used to pass the parameter: `remove(${header.ref})`. 181 | 182 | 183 | ## Start the service 184 | 185 | Start the app with `mvn clean spring-boot:run` and go to the following link: [http://localhost:8082/api/payments](http://localhost:8082/api/payments) 186 | to check if everything is working fine. 187 | 188 | 189 | ## Test the service 190 | 191 | If you have HTTPie installed you can test the following calls. 192 | 193 | **List payments** 194 | ``` 195 | http GET :8082/api/payments 196 | ``` 197 | 198 | **Create a invalid payment** 199 | ``` 200 | echo '{"reference": "ref1", "user": "nicola", "amount": -2}' | http POST :8082/api/payments 201 | ``` 202 | 203 | If you want to use CURL instead of HTTPie: 204 | ``` 205 | curl -X POST -d '{"reference": "ref1", "user": "nicola", "amount": -2}' -H "Content-Type: application/json" -w "\n" http://localhost:8082/api/payments 206 | ``` 207 | 208 | You should see a error coming back from the service. 209 | 210 | **Create a valid payment** 211 | ``` 212 | echo '{"reference": "ref1", "user": "nicola", "amount": 1}' | http POST :8082/api/payments 213 | ``` 214 | 215 | If using CURL: 216 | ``` 217 | curl -X POST -d '{"reference": "ref2", "user": "nicola", "amount": 1}' -H "Content-Type: application/json" -w "\n" http://localhost:8082/api/payments 218 | ``` 219 | 220 | **Delete a payment** 221 | ``` 222 | http DELETE :8082/api/payments/ref1 223 | ``` 224 | 225 | --------------------------------------------------------------------------------