(handler -> {
136 | evaluate(handler);
137 | });
138 | }
139 |
140 |
141 | public static PortfolioService newInstance(io.vertx.workshop.portfolio.PortfolioService arg) {
142 | return arg != null ? new PortfolioService(arg) : null;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/generated/vertx-workshop-portfolio-js/portfolio_service-proxy.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 |
18 | /**
19 | A service managing a portfolio.
20 |
21 | This service is an event bus service (a.k.a service proxies, or async RPC). The client and server are generated at
22 | compile time.
23 |
24 | @class
25 | */
26 | export default class PortfolioService {
27 |
28 | constructor (eb: any, address: string);
29 |
30 | getPortfolio(resultHandler: (err: any, result: any) => any) : void;
31 |
32 | buy(amount: number, quote: Object, resultHandler: (err: any, result: any) => any) : void;
33 |
34 | sell(amount: number, quote: Object, resultHandler: (err: any, result: any) => any) : void;
35 |
36 | evaluate(resultHandler: (err: any, result: any) => any) : void;
37 | }
--------------------------------------------------------------------------------
/portfolio-service/src/main/generated/vertx-workshop-portfolio-js/portfolio_service-proxy.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | ///
18 |
19 | /** @module vertx-workshop-portfolio-js/portfolio_service */
20 | !function (factory) {
21 | if (typeof require === 'function' && typeof module !== 'undefined') {
22 | factory();
23 | } else if (typeof define === 'function' && define.amd) {
24 | // AMD loader
25 | define('vertx-workshop-portfolio-js/portfolio_service-proxy', [], factory);
26 | } else {
27 | // plain old include
28 | PortfolioService = factory();
29 | }
30 | }(function () {
31 |
32 | /**
33 | A service managing a portfolio.
34 |
35 | This service is an event bus service (a.k.a service proxies, or async RPC). The client and server are generated at
36 | compile time.
37 |
38 | @class
39 | */
40 | var PortfolioService = function(eb, address) {
41 | var j_eb = eb;
42 | var j_address = address;
43 | var closed = false;
44 | var that = this;
45 | var convCharCollection = function(coll) {
46 | var ret = [];
47 | for (var i = 0;i < coll.length;i++) {
48 | ret.push(String.fromCharCode(coll[i]));
49 | }
50 | return ret;
51 | };
52 |
53 | /**
54 | Gets the portfolio.
55 |
56 | @public
57 | @param resultHandler {function} the result handler called when the portfolio has been retrieved. The async result indicates whether the call was successful or not.
58 | */
59 | this.getPortfolio = function(resultHandler) {
60 | var __args = arguments;
61 | if (__args.length === 1 && typeof __args[0] === 'function') {
62 | if (closed) {
63 | throw new Error('Proxy is closed');
64 | }
65 | j_eb.send(j_address, {}, {"action":"getPortfolio"}, function(err, result) { __args[0](err, result && result.body); });
66 | return;
67 | } else throw new TypeError('function invoked with invalid arguments');
68 | };
69 |
70 | /**
71 | Buy `amount` shares of the given shares (quote).
72 |
73 | @public
74 | @param amount {number} the amount
75 | @param quote {Object} the last quote
76 | @param resultHandler {function} the result handler with the updated portfolio. If the action cannot be executed, the async result is market as a failure (not enough money, not enough shares available...)
77 | */
78 | this.buy = function(amount, quote, resultHandler) {
79 | var __args = arguments;
80 | if (__args.length === 3 && typeof __args[0] ==='number' && (typeof __args[1] === 'object' && __args[1] != null) && typeof __args[2] === 'function') {
81 | if (closed) {
82 | throw new Error('Proxy is closed');
83 | }
84 | j_eb.send(j_address, {"amount":__args[0], "quote":__args[1]}, {"action":"buy"}, function(err, result) { __args[2](err, result && result.body); });
85 | return;
86 | } else throw new TypeError('function invoked with invalid arguments');
87 | };
88 |
89 | /**
90 | Sell `amount` shares of the given shares (quote).
91 |
92 | @public
93 | @param amount {number} the amount
94 | @param quote {Object} the last quote
95 | @param resultHandler {function} the result handler with the updated portfolio. If the action cannot be executed, the async result is market as a failure (not enough share...)
96 | */
97 | this.sell = function(amount, quote, resultHandler) {
98 | var __args = arguments;
99 | if (__args.length === 3 && typeof __args[0] ==='number' && (typeof __args[1] === 'object' && __args[1] != null) && typeof __args[2] === 'function') {
100 | if (closed) {
101 | throw new Error('Proxy is closed');
102 | }
103 | j_eb.send(j_address, {"amount":__args[0], "quote":__args[1]}, {"action":"sell"}, function(err, result) { __args[2](err, result && result.body); });
104 | return;
105 | } else throw new TypeError('function invoked with invalid arguments');
106 | };
107 |
108 | /**
109 | Evaluates the current value of the portfolio.
110 |
111 | @public
112 | @param resultHandler {function} the result handler with the valuation
113 | */
114 | this.evaluate = function(resultHandler) {
115 | var __args = arguments;
116 | if (__args.length === 1 && typeof __args[0] === 'function') {
117 | if (closed) {
118 | throw new Error('Proxy is closed');
119 | }
120 | j_eb.send(j_address, {}, {"action":"evaluate"}, function(err, result) { __args[0](err, result && result.body); });
121 | return;
122 | } else throw new TypeError('function invoked with invalid arguments');
123 | };
124 |
125 | };
126 |
127 | if (typeof exports !== 'undefined') {
128 | if (typeof module !== 'undefined' && module.exports) {
129 | exports = module.exports = PortfolioService;
130 | } else {
131 | exports.PortfolioService = PortfolioService;
132 | }
133 | } else {
134 | return PortfolioService;
135 | }
136 | });
--------------------------------------------------------------------------------
/portfolio-service/src/main/generated/vertx-workshop-portfolio-js/portfolio_service.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | /** @module vertx-workshop-portfolio-js/portfolio_service */
18 | var utils = require('vertx-js/util/utils');
19 |
20 | var io = Packages.io;
21 | var JsonObject = io.vertx.core.json.JsonObject;
22 | var JPortfolioService = Java.type('io.vertx.workshop.portfolio.PortfolioService');
23 | var Portfolio = Java.type('io.vertx.workshop.portfolio.Portfolio');
24 |
25 | /**
26 | A service managing a portfolio.
27 |
28 | This service is an event bus service (a.k.a service proxies, or async RPC). The client and server are generated at
29 | compile time.
30 |
31 | @class
32 | */
33 | var PortfolioService = function(j_val) {
34 |
35 | var j_portfolioService = j_val;
36 | var that = this;
37 |
38 | /**
39 | Gets the portfolio.
40 |
41 | @public
42 | @param resultHandler {function} the result handler called when the portfolio has been retrieved. The async result indicates whether the call was successful or not.
43 | */
44 | this.getPortfolio = function(resultHandler) {
45 | var __args = arguments;
46 | if (__args.length === 1 && typeof __args[0] === 'function') {
47 | j_portfolioService["getPortfolio(io.vertx.core.Handler)"](function(ar) {
48 | if (ar.succeeded()) {
49 | resultHandler(utils.convReturnDataObject(ar.result()), null);
50 | } else {
51 | resultHandler(null, ar.cause());
52 | }
53 | });
54 | } else throw new TypeError('function invoked with invalid arguments');
55 | };
56 |
57 | /**
58 | Buy `amount` shares of the given shares (quote).
59 |
60 | @public
61 | @param amount {number} the amount
62 | @param quote {Object} the last quote
63 | @param resultHandler {function} the result handler with the updated portfolio. If the action cannot be executed, the async result is market as a failure (not enough money, not enough shares available...)
64 | */
65 | this.buy = function(amount, quote, resultHandler) {
66 | var __args = arguments;
67 | if (__args.length === 3 && typeof __args[0] ==='number' && (typeof __args[1] === 'object' && __args[1] != null) && typeof __args[2] === 'function') {
68 | j_portfolioService["buy(int,io.vertx.core.json.JsonObject,io.vertx.core.Handler)"](amount, utils.convParamJsonObject(quote), function(ar) {
69 | if (ar.succeeded()) {
70 | resultHandler(utils.convReturnDataObject(ar.result()), null);
71 | } else {
72 | resultHandler(null, ar.cause());
73 | }
74 | });
75 | } else throw new TypeError('function invoked with invalid arguments');
76 | };
77 |
78 | /**
79 | Sell `amount` shares of the given shares (quote).
80 |
81 | @public
82 | @param amount {number} the amount
83 | @param quote {Object} the last quote
84 | @param resultHandler {function} the result handler with the updated portfolio. If the action cannot be executed, the async result is market as a failure (not enough share...)
85 | */
86 | this.sell = function(amount, quote, resultHandler) {
87 | var __args = arguments;
88 | if (__args.length === 3 && typeof __args[0] ==='number' && (typeof __args[1] === 'object' && __args[1] != null) && typeof __args[2] === 'function') {
89 | j_portfolioService["sell(int,io.vertx.core.json.JsonObject,io.vertx.core.Handler)"](amount, utils.convParamJsonObject(quote), function(ar) {
90 | if (ar.succeeded()) {
91 | resultHandler(utils.convReturnDataObject(ar.result()), null);
92 | } else {
93 | resultHandler(null, ar.cause());
94 | }
95 | });
96 | } else throw new TypeError('function invoked with invalid arguments');
97 | };
98 |
99 | /**
100 | Evaluates the current value of the portfolio.
101 |
102 | @public
103 | @param resultHandler {function} the result handler with the valuation
104 | */
105 | this.evaluate = function(resultHandler) {
106 | var __args = arguments;
107 | if (__args.length === 1 && typeof __args[0] === 'function') {
108 | j_portfolioService["evaluate(io.vertx.core.Handler)"](function(ar) {
109 | if (ar.succeeded()) {
110 | resultHandler(ar.result(), null);
111 | } else {
112 | resultHandler(null, ar.cause());
113 | }
114 | });
115 | } else throw new TypeError('function invoked with invalid arguments');
116 | };
117 |
118 | // A reference to the underlying Java delegate
119 | // NOTE! This is an internal API and must not be used in user code.
120 | // If you rely on this property your code is likely to break if we change it / remove it without warning.
121 | this._jdel = j_portfolioService;
122 | };
123 |
124 | PortfolioService._jclass = utils.getJavaClass("io.vertx.workshop.portfolio.PortfolioService");
125 | PortfolioService._jtype = {
126 | accept: function(obj) {
127 | return PortfolioService._jclass.isInstance(obj._jdel);
128 | },
129 | wrap: function(jdel) {
130 | var obj = Object.create(PortfolioService.prototype, {});
131 | PortfolioService.apply(obj, arguments);
132 | return obj;
133 | },
134 | unwrap: function(obj) {
135 | return obj._jdel;
136 | }
137 | };
138 | PortfolioService._create = function(jdel) {
139 | var obj = Object.create(PortfolioService.prototype, {});
140 | PortfolioService.apply(obj, arguments);
141 | return obj;
142 | }
143 | module.exports = PortfolioService;
--------------------------------------------------------------------------------
/portfolio-service/src/main/java/io/vertx/workshop/portfolio/Portfolio.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio;
2 |
3 | import io.vertx.codegen.annotations.DataObject;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | import java.util.Map;
7 | import java.util.TreeMap;
8 |
9 | /**
10 | * Structure representing a portfolio. It stores the available cash and the owned shares.
11 | */
12 | @DataObject(generateConverter = true)
13 | public class Portfolio {
14 |
15 | private Map shares = new TreeMap<>();
16 |
17 | private double cash;
18 |
19 | /**
20 | * Creates a new instance of {@link Portfolio}.
21 | */
22 | public Portfolio() {
23 | // Empty constructor
24 | }
25 |
26 | /**
27 | * Creates a new instance of {@link Portfolio} by copying the other instance.
28 | *
29 | * @param other the instance to copy
30 | */
31 | public Portfolio(Portfolio other) {
32 | this.shares = new TreeMap<>(other.shares);
33 | this.cash = other.cash;
34 | }
35 |
36 | /**
37 | * Creates a new instance of {@link Portfolio} from ths json object.
38 | *
39 | * @param json the json object
40 | */
41 | public Portfolio(JsonObject json) {
42 | // A converter is generated to easy the conversion from and to JSON.
43 | PortfolioConverter.fromJson(json, this);
44 | }
45 |
46 | /**
47 | * @return a JSON representation of the portfolio computed using the converter.
48 | */
49 | public JsonObject toJson() {
50 | JsonObject json = new JsonObject();
51 | PortfolioConverter.toJson(this, json);
52 | return json;
53 | }
54 |
55 | /**
56 | * @return the owned shared (name -> number)
57 | */
58 | public Map getShares() {
59 | return shares;
60 | }
61 |
62 | /**
63 | * Sets the owned shares. Method used by the converter.
64 | *
65 | * @param shares the shares
66 | * @return the current {@link Portfolio}
67 | */
68 | public Portfolio setShares(Map shares) {
69 | this.shares = shares;
70 | return this;
71 | }
72 |
73 | /**
74 | * @return the available cash.
75 | */
76 | public double getCash() {
77 | return cash;
78 | }
79 |
80 | /**
81 | * Sets the available cash. Method used by the converter.
82 | *
83 | * @param cash the cash
84 | * @return the current {@link Portfolio}
85 | */
86 | public Portfolio setCash(double cash) {
87 | this.cash = cash;
88 | return this;
89 | }
90 |
91 | // -- Additional method
92 |
93 | /**
94 | * This method is just a convenient method to get the number of owned shares of the specify company (name of the
95 | * company).
96 | *
97 | * @param name the name of the company
98 | * @return the number of owned shares, {@literal 0} is none.
99 | */
100 | public int getAmount(String name) {
101 | Integer current = shares.get(name);
102 | if (current == null) {
103 | return 0;
104 | }
105 | return current;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/java/io/vertx/workshop/portfolio/PortfolioService.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio;
2 |
3 | import io.vertx.codegen.annotations.ProxyGen;
4 | import io.vertx.codegen.annotations.VertxGen;
5 | import io.vertx.core.AsyncResult;
6 | import io.vertx.core.Handler;
7 | import io.vertx.core.json.JsonObject;
8 |
9 |
10 | /**
11 | * A service managing a portfolio.
12 | *
13 | * This service is an event bus service (a.k.a service proxies, or async RPC). The client and server are generated at
14 | * compile time.
15 | *
16 | * All method are asynchronous and so ends with a {@link Handler} parameter.
17 | */
18 | @VertxGen
19 | @ProxyGen
20 | public interface PortfolioService {
21 |
22 | /**
23 | * The address on which the service is published.
24 | */
25 | String ADDRESS = "service.portfolio";
26 |
27 | /**
28 | * The address on which the successful action are sent.
29 | */
30 | String EVENT_ADDRESS = "portfolio";
31 |
32 | /**
33 | * Gets the portfolio.
34 | *
35 | * @param resultHandler the result handler called when the portfolio has been retrieved. The async result indicates
36 | * whether the call was successful or not.
37 | */
38 | void getPortfolio(Handler> resultHandler);
39 |
40 | /**
41 | * Buy `amount` shares of the given shares (quote).
42 | *
43 | * @param amount the amount
44 | * @param quote the last quote
45 | * @param resultHandler the result handler with the updated portfolio. If the action cannot be executed, the async
46 | * result is market as a failure (not enough money, not enough shares available...)
47 | */
48 | void buy(int amount, JsonObject quote, Handler> resultHandler);
49 |
50 | /**
51 | * Sell `amount` shares of the given shares (quote).
52 | *
53 | * @param amount the amount
54 | * @param quote the last quote
55 | * @param resultHandler the result handler with the updated portfolio. If the action cannot be executed, the async
56 | * result is market as a failure (not enough share...)
57 | */
58 | void sell(int amount, JsonObject quote, Handler> resultHandler);
59 |
60 | /**
61 | * Evaluates the current value of the portfolio.
62 | *
63 | * @param resultHandler the result handler with the valuation
64 | */
65 | void evaluate(Handler> resultHandler);
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/java/io/vertx/workshop/portfolio/impl/PortfolioServiceImpl.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio.impl;
2 |
3 | import io.reactivex.Flowable;
4 | import io.reactivex.Single;
5 | import io.vertx.core.AsyncResult;
6 | import io.vertx.core.Future;
7 | import io.vertx.core.Handler;
8 | import io.vertx.core.json.JsonObject;
9 | import io.vertx.reactivex.core.Vertx;
10 | import io.vertx.reactivex.ext.web.client.HttpResponse;
11 | import io.vertx.reactivex.ext.web.client.WebClient;
12 | import io.vertx.reactivex.ext.web.codec.BodyCodec;
13 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery;
14 | import io.vertx.reactivex.servicediscovery.types.HttpEndpoint;
15 | import io.vertx.workshop.portfolio.Portfolio;
16 | import io.vertx.workshop.portfolio.PortfolioService;
17 |
18 | import java.io.UnsupportedEncodingException;
19 | import java.net.URLEncoder;
20 |
21 | /**
22 | * The portfolio service implementation.
23 | */
24 | public class PortfolioServiceImpl implements PortfolioService {
25 |
26 | private final Vertx vertx;
27 | private final Portfolio portfolio;
28 | private final ServiceDiscovery discovery;
29 |
30 | public PortfolioServiceImpl(Vertx vertx, ServiceDiscovery discovery, double initialCash) {
31 | this.vertx = vertx;
32 | this.portfolio = new Portfolio().setCash(initialCash);
33 | this.discovery = discovery;
34 | }
35 |
36 | @Override
37 | public void getPortfolio(Handler> resultHandler) {
38 | // TODO Call the given handler with a successful Async Result encapsulating the `portfolio` object
39 | // The async result instance is created using `Future.succeededFuture`
40 | // ----
41 |
42 | // ----
43 | }
44 |
45 | private void sendActionOnTheEventBus(String action, int amount, JsonObject quote, int newAmount) {
46 | // TODO Broadcast a JSON message to the `EVENT_ADDRESS` containing the following keys: "action", "quote", "date"
47 | // (use System.currentTimeMillis()), "amount" and "owned" (newAmount)
48 | // ----
49 |
50 | // ----
51 | }
52 |
53 | @Override
54 | public void evaluate(Handler> resultHandler) {
55 | // TODO
56 | // ----
57 |
58 | // ---
59 | }
60 |
61 | private void computeEvaluation(WebClient webClient, Handler> resultHandler) {
62 | // We need to call the service for each company in which we own shares
63 | Flowable.fromIterable(portfolio.getShares().entrySet())
64 | // For each, we retrieve the value
65 | .flatMapSingle(entry -> getValueForCompany(webClient, entry.getKey(), entry.getValue()))
66 | // We accumulate the results
67 | .toList()
68 | // And compute the sum
69 | .map(list -> list.stream().mapToDouble(x -> x).sum())
70 | // We report the result or failure
71 | .subscribe((sum, err) -> {
72 | if (err != null) {
73 | resultHandler.handle(Future.failedFuture(err));
74 | } else {
75 | resultHandler.handle(Future.succeededFuture(sum));
76 | }
77 | });
78 | }
79 |
80 | private Single getValueForCompany(WebClient client, String company, int numberOfShares) {
81 | //TODO
82 | //----
83 |
84 | return Single.just(0.0);
85 | // ---
86 |
87 |
88 | }
89 |
90 |
91 | @Override
92 | public void buy(int amount, JsonObject quote, Handler> resultHandler) {
93 | if (amount <= 0) {
94 | resultHandler.handle(Future.failedFuture("Cannot buy " + quote.getString("name") + " - the amount must be " +
95 | "greater than 0"));
96 | return;
97 | }
98 |
99 | if (quote.getInteger("shares") < amount) {
100 | resultHandler.handle(Future.failedFuture("Cannot buy " + amount + " - not enough " +
101 | "stocks on the market (" + quote.getInteger("shares") + ")"));
102 | return;
103 | }
104 |
105 | double price = amount * quote.getDouble("ask");
106 | String name = quote.getString("name");
107 | // 1) do we have enough money
108 | if (portfolio.getCash() >= price) {
109 | // Yes, buy it
110 | portfolio.setCash(portfolio.getCash() - price);
111 | int current = portfolio.getAmount(name);
112 | int newAmount = current + amount;
113 | portfolio.getShares().put(name, newAmount);
114 | sendActionOnTheEventBus("BUY", amount, quote, newAmount);
115 | resultHandler.handle(Future.succeededFuture(portfolio));
116 | } else {
117 | resultHandler.handle(Future.failedFuture("Cannot buy " + amount + " of " + name + " - " + "not enough money, " +
118 | "need " + price + ", has " + portfolio.getCash()));
119 | }
120 | }
121 |
122 |
123 | @Override
124 | public void sell(int amount, JsonObject quote, Handler> resultHandler) {
125 | if (amount <= 0) {
126 | resultHandler.handle(Future.failedFuture("Cannot sell " + quote.getString("name") + " - the amount must be " +
127 | "greater than 0"));
128 | return;
129 | }
130 |
131 | double price = amount * quote.getDouble("bid");
132 | String name = quote.getString("name");
133 | int current = portfolio.getAmount(name);
134 | // 1) do we have enough stocks
135 | if (current >= amount) {
136 | // Yes, sell it
137 | int newAmount = current - amount;
138 | if (newAmount == 0) {
139 | portfolio.getShares().remove(name);
140 | } else {
141 | portfolio.getShares().put(name, newAmount);
142 | }
143 | portfolio.setCash(portfolio.getCash() + price);
144 | sendActionOnTheEventBus("SELL", amount, quote, newAmount);
145 | resultHandler.handle(Future.succeededFuture(portfolio));
146 | } else {
147 | resultHandler.handle(Future.failedFuture("Cannot sell " + amount + " of " + name + " - " + "not enough stocks " +
148 | "in portfolio"));
149 | }
150 |
151 | }
152 |
153 | private static String encode(String value) {
154 | try {
155 | return URLEncoder.encode(value, "UTF-8");
156 | } catch (UnsupportedEncodingException e) {
157 | throw new RuntimeException("Unsupported encoding");
158 | }
159 | }
160 |
161 |
162 | }
163 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/java/io/vertx/workshop/portfolio/impl/PortfolioVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio.impl;
2 |
3 | import io.vertx.reactivex.core.AbstractVerticle;
4 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery;
5 | import io.vertx.reactivex.servicediscovery.types.EventBusService;
6 | import io.vertx.servicediscovery.Record;
7 | import io.vertx.serviceproxy.ServiceBinder;
8 | import io.vertx.workshop.portfolio.PortfolioService;
9 |
10 | import static io.vertx.workshop.portfolio.PortfolioService.ADDRESS;
11 |
12 | /**
13 | * A verticle publishing the portfolio service.
14 | */
15 | public class PortfolioVerticle extends AbstractVerticle {
16 |
17 | private Record record;
18 | private ServiceDiscovery discovery;
19 |
20 | @Override
21 | public void start() {
22 |
23 | ServiceDiscovery.create(vertx, discovery -> {
24 | this.discovery = discovery;
25 | // Create the service object
26 | PortfolioServiceImpl service = new PortfolioServiceImpl(vertx, discovery, config().getDouble("money", 10000.00));
27 |
28 | // Register the service proxy on the event bus
29 | ServiceBinder binder = new ServiceBinder(vertx.getDelegate()).setAddress(ADDRESS);
30 | binder.register(PortfolioService.class, service);
31 |
32 | Record record = EventBusService.createRecord("portfolio", ADDRESS, PortfolioService.class.getName());
33 | discovery.publish(record, ar -> {
34 | if (ar.succeeded()) {
35 | this.record = record;
36 | System.out.println("Portfolio service published");
37 |
38 | // Used for health check
39 | vertx.createHttpServer().requestHandler(req -> req.response().end("OK")).listen(8080);
40 | } else {
41 | ar.cause().printStackTrace();
42 | }
43 | });
44 |
45 | });
46 | }
47 |
48 | @Override
49 | public void stop() throws Exception {
50 | if (record != null) {
51 | discovery.unpublish(record.getRegistration(), v -> {});
52 | }
53 | discovery.close();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/java/io/vertx/workshop/portfolio/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Indicates that this module contains classes that need to be generated / processed.
3 | */
4 | @ModuleGen(name = "vertx-workshop-portfolio", groupPackage = "io.vertx.workshop.portfolio")
5 | package io.vertx.workshop.portfolio;
6 |
7 | import io.vertx.codegen.annotations.ModuleGen;
--------------------------------------------------------------------------------
/portfolio-service/src/main/kotlin/io/vertx/workshop/portfolio/kotlin/Portfolio.kt:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio.kotlin
2 |
3 | import io.vertx.workshop.portfolio.Portfolio
4 |
5 | /**
6 | * A function providing a DSL for building [io.vertx.workshop.portfolio.Portfolio] objects.
7 | *
8 | * Structure representing a portfolio. It stores the available cash and the owned shares.
9 | *
10 | * @param cash Sets the available cash. Method used by the converter.
11 | * @param shares Sets the owned shares. Method used by the converter.
12 | *
13 | *
14 | * NOTE: This function has been automatically generated from the [io.vertx.workshop.portfolio.Portfolio original] using Vert.x codegen.
15 | */
16 | fun Portfolio(
17 | cash: Double? = null,
18 | shares: Map? = null): Portfolio = io.vertx.workshop.portfolio.Portfolio().apply {
19 |
20 | if (cash != null) {
21 | this.setCash(cash)
22 | }
23 | if (shares != null) {
24 | this.setShares(shares)
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/solution/io/vertx/workshop/portfolio/Portfolio.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio;
2 |
3 | import io.vertx.codegen.annotations.DataObject;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | import java.util.Map;
7 | import java.util.TreeMap;
8 |
9 | /**
10 | * Structure representing a portfolio. It stores the available cash and the owned shares.
11 | */
12 | @DataObject(generateConverter = true)
13 | public class Portfolio {
14 |
15 | private Map shares = new TreeMap<>();
16 |
17 | private double cash;
18 |
19 | /**
20 | * Creates a new instance of {@link Portfolio}.
21 | */
22 | public Portfolio() {
23 | // Empty constructor
24 | }
25 |
26 | /**
27 | * Creates a new instance of {@link Portfolio} by copying the other instance.
28 | *
29 | * @param other the instance to copy
30 | */
31 | public Portfolio(Portfolio other) {
32 | this.shares = new TreeMap<>(other.shares);
33 | this.cash = other.cash;
34 | }
35 |
36 | /**
37 | * Creates a new instance of {@link Portfolio} from ths json object.
38 | *
39 | * @param json the json object
40 | */
41 | public Portfolio(JsonObject json) {
42 | // A converter is generated to easy the conversion from and to JSON.
43 | PortfolioConverter.fromJson(json, this);
44 | }
45 |
46 | /**
47 | * @return a JSON representation of the portfolio computed using the converter.
48 | */
49 | public JsonObject toJson() {
50 | JsonObject json = new JsonObject();
51 | PortfolioConverter.toJson(this, json);
52 | return json;
53 | }
54 |
55 | /**
56 | * @return the owned shared (name -> number)
57 | */
58 | public Map getShares() {
59 | return shares;
60 | }
61 |
62 | /**
63 | * Sets the owned shares. Method used by the converter.
64 | *
65 | * @param shares the shares
66 | * @return the current {@link Portfolio}
67 | */
68 | public Portfolio setShares(Map shares) {
69 | this.shares = shares;
70 | return this;
71 | }
72 |
73 | /**
74 | * @return the available cash.
75 | */
76 | public double getCash() {
77 | return cash;
78 | }
79 |
80 | /**
81 | * Sets the available cash. Method used by the converter.
82 | *
83 | * @param cash the cash
84 | * @return the current {@link Portfolio}
85 | */
86 | public Portfolio setCash(double cash) {
87 | this.cash = cash;
88 | return this;
89 | }
90 |
91 | // -- Additional method
92 |
93 | /**
94 | * This method is just a convenient method to get the number of owned shares of the specify company (name of the
95 | * company).
96 | *
97 | * @param name the name of the company
98 | * @return the number of owned shares, {@literal 0} is none.
99 | */
100 | public int getAmount(String name) {
101 | Integer current = shares.get(name);
102 | if (current == null) {
103 | return 0;
104 | }
105 | return current;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/solution/io/vertx/workshop/portfolio/PortfolioService.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio;
2 |
3 | import io.vertx.codegen.annotations.ProxyGen;
4 | import io.vertx.codegen.annotations.VertxGen;
5 | import io.vertx.core.AsyncResult;
6 | import io.vertx.core.Handler;
7 | import io.vertx.core.json.JsonObject;
8 |
9 |
10 | /**
11 | * A service managing a portfolio.
12 | *
13 | * This service is an event bus service (a.k.a service proxies, or async RPC). The client and server are generated at
14 | * compile time.
15 | *
16 | * All method are asynchronous and so ends with a {@link Handler} parameter.
17 | */
18 | @VertxGen
19 | @ProxyGen
20 | public interface PortfolioService {
21 |
22 | /**
23 | * The address on which the service is published.
24 | */
25 | String ADDRESS = "service.portfolio";
26 |
27 | /**
28 | * The address on which the successful action are sent.
29 | */
30 | String EVENT_ADDRESS = "portfolio";
31 |
32 | /**
33 | * Gets the portfolio.
34 | *
35 | * @param resultHandler the result handler called when the portfolio has been retrieved. The async result indicates
36 | * whether the call was successful or not.
37 | */
38 | void getPortfolio(Handler> resultHandler);
39 |
40 | /**
41 | * Buy `amount` shares of the given shares (quote).
42 | *
43 | * @param amount the amount
44 | * @param quote the last quote
45 | * @param resultHandler the result handler with the updated portfolio. If the action cannot be executed, the async
46 | * result is market as a failure (not enough money, not enough shares available...)
47 | */
48 | void buy(int amount, JsonObject quote, Handler> resultHandler);
49 |
50 | /**
51 | * Sell `amount` shares of the given shares (quote).
52 | *
53 | * @param amount the amount
54 | * @param quote the last quote
55 | * @param resultHandler the result handler with the updated portfolio. If the action cannot be executed, the async
56 | * result is market as a failure (not enough share...)
57 | */
58 | void sell(int amount, JsonObject quote, Handler> resultHandler);
59 |
60 | /**
61 | * Evaluates the current value of the portfolio.
62 | *
63 | * @param resultHandler the result handler with the valuation
64 | */
65 | void evaluate(Handler> resultHandler);
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/solution/io/vertx/workshop/portfolio/impl/PortfolioVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio.impl;
2 |
3 | import io.vertx.reactivex.core.AbstractVerticle;
4 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery;
5 | import io.vertx.reactivex.servicediscovery.types.EventBusService;
6 | import io.vertx.servicediscovery.Record;
7 | import io.vertx.serviceproxy.ServiceBinder;
8 | import io.vertx.workshop.portfolio.PortfolioService;
9 |
10 | import static io.vertx.workshop.portfolio.PortfolioService.ADDRESS;
11 |
12 | /**
13 | * A verticle publishing the portfolio service.
14 | */
15 | public class PortfolioVerticle extends AbstractVerticle {
16 |
17 | private Record record;
18 | private ServiceDiscovery discovery;
19 |
20 | @Override
21 | public void start() {
22 |
23 | ServiceDiscovery.create(vertx, discovery -> {
24 | this.discovery = discovery;
25 | // Create the service object
26 | PortfolioServiceImpl service = new PortfolioServiceImpl(vertx, discovery, config().getDouble("money", 10000.00));
27 |
28 | // Register the service proxy on the event bus
29 | ServiceBinder binder = new ServiceBinder(vertx.getDelegate()).setAddress(ADDRESS);
30 | binder.register(PortfolioService.class, service);
31 |
32 | Record record = EventBusService.createRecord("portfolio", ADDRESS, PortfolioService.class.getName());
33 | discovery.publish(record, ar -> {
34 | if (ar.succeeded()) {
35 | this.record = record;
36 | System.out.println("Portfolio service published");
37 |
38 | // Used for health check
39 | vertx.createHttpServer().requestHandler(req -> req.response().end("OK")).listen(8080);
40 | } else {
41 | ar.cause().printStackTrace();
42 | }
43 | });
44 |
45 | });
46 | }
47 |
48 | @Override
49 | public void stop() throws Exception {
50 | if (record != null) {
51 | discovery.unpublish(record.getRegistration(), v -> {});
52 | }
53 | discovery.close();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/portfolio-service/src/main/solution/io/vertx/workshop/portfolio/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Indicates that this module contains classes that need to be generated / processed.
3 | */
4 | @ModuleGen(name = "vertx-workshop-portfolio", groupPackage = "io.vertx.workshop.portfolio")
5 | package io.vertx.workshop.portfolio;
6 |
7 | import io.vertx.codegen.annotations.ModuleGen;
--------------------------------------------------------------------------------
/portfolio-service/src/test/java/io/vertx/workshop/portfolio/impl/PortfolioServiceImplTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio.impl;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.core.json.JsonObject;
5 | import io.vertx.ext.unit.Async;
6 | import io.vertx.ext.unit.TestContext;
7 | import io.vertx.ext.unit.junit.VertxUnitRunner;
8 | import io.vertx.serviceproxy.ServiceProxyBuilder;
9 | import io.vertx.workshop.portfolio.Portfolio;
10 | import io.vertx.workshop.portfolio.PortfolioService;
11 | import org.junit.After;
12 | import org.junit.Before;
13 | import org.junit.Ignore;
14 | import org.junit.Test;
15 | import org.junit.runner.RunWith;
16 |
17 |
18 | @RunWith(VertxUnitRunner.class)
19 | @Ignore("De-ignore me once you have implemented the methods")
20 | public class PortfolioServiceImplTest {
21 |
22 | private Vertx vertx;
23 | private PortfolioService service;
24 | private Portfolio original;
25 |
26 | @Before
27 | public void setUp(TestContext tc) {
28 | vertx = Vertx.vertx();
29 |
30 | Async async = tc.async();
31 | vertx.deployVerticle(io.vertx.workshop.portfolio.impl.PortfolioVerticle.class.getName(), id -> {
32 | service = new ServiceProxyBuilder(vertx).setAddress(PortfolioService.ADDRESS).build(PortfolioService.class);
33 | service.getPortfolio(ar -> {
34 | tc.assertTrue(ar.succeeded());
35 | original = ar.result();
36 | async.complete();
37 | });
38 | });
39 | }
40 |
41 | @After
42 | public void tearDown() {
43 | vertx.close();
44 | }
45 |
46 | @Test
47 | public void testBuyStocks(TestContext tc) {
48 | Async async = tc.async();
49 | service.buy(10, quote("A", 10, 20, 100), ar -> {
50 | tc.assertTrue(ar.succeeded());
51 | Portfolio portfolio = ar.result();
52 | tc.assertEquals(portfolio.getAmount("A"), 10);
53 | tc.assertEquals(portfolio.getAmount("B"), 0);
54 | tc.assertEquals(portfolio.getCash(), original.getCash() - 10 * 10);
55 | async.complete();
56 | });
57 | }
58 |
59 | @Test
60 | public void testBuyAndSell(TestContext tc) {
61 | Async async = tc.async();
62 | service.buy(10, quote("A", 10, 20, 100), ar -> {
63 | tc.assertTrue(ar.succeeded());
64 | Portfolio portfolio = ar.result();
65 | tc.assertEquals(portfolio.getAmount("A"), 10);
66 | tc.assertEquals(portfolio.getAmount("B"), 0);
67 | tc.assertEquals(portfolio.getCash(), original.getCash() - 10 * 10);
68 |
69 | // Sell the bought stocks immediately
70 | service.sell(5, quote("A", 10, 20, 100), ar2 -> {
71 | tc.assertTrue(ar2.succeeded());
72 | Portfolio portfolio2 = ar2.result();
73 | tc.assertEquals(portfolio2.getAmount("A"), 5);
74 | tc.assertEquals(portfolio2.getAmount("B"), 0);
75 | tc.assertEquals(portfolio2.getCash(), portfolio.getCash() + 5 * 20);
76 | async.complete();
77 | });
78 | });
79 | }
80 |
81 | @Test
82 | public void testThatYouCannotBuyIfYouRunOutOfMoney(TestContext tc) {
83 | Async async = tc.async();
84 | service.buy(10000, quote("A", 10, 20, 100000), ar -> {
85 | tc.assertTrue(ar.failed());
86 | tc.assertTrue(ar.cause().getMessage().contains("not enough money"));
87 | async.complete();
88 | });
89 | }
90 |
91 | @Test
92 | public void testThatYouCannotBuyIfThereIsNotEnoughShare(TestContext tc) {
93 | Async async = tc.async();
94 | service.buy(100, quote("A", 10, 20, 10), ar -> {
95 | tc.assertTrue(ar.failed());
96 | tc.assertTrue(ar.cause().getMessage().contains("not enough stocks"));
97 | async.complete();
98 | });
99 | }
100 |
101 | @Test
102 | public void testThatYouCannotSellMoreThanWhatYouOwn(TestContext tc) {
103 | Async async = tc.async();
104 | service.buy(100, quote("A", 10, 20, 100), ar -> {
105 | service.sell(100, quote("A", 10, 20, 0), ar2 -> {
106 | tc.assertTrue(ar2.succeeded());
107 | service.sell(1, quote("A", 10, 20, 0), ar3 -> {
108 | tc.assertTrue(ar3.failed());
109 | tc.assertTrue(ar3.cause().getMessage().contains("not enough stocks"));
110 | async.complete();
111 | });
112 | });
113 |
114 | });
115 | }
116 |
117 | @Test
118 | public void testYouCannotBuyANegativeAmount(TestContext tc) {
119 | Async async = tc.async();
120 | service.buy(-1, quote("A", 10, 20, 100), ar -> {
121 | tc.assertTrue(ar.failed());
122 | async.complete();
123 | });
124 | }
125 |
126 | @Test
127 | public void testYouCannotSellANegativeAmount(TestContext tc) {
128 | Async async = tc.async();
129 | service.sell(-1, quote("A", 10, 20, 100), ar -> {
130 | tc.assertTrue(ar.failed());
131 | async.complete();
132 | });
133 | }
134 |
135 | private JsonObject quote(String name, double ask, double bid, int available) {
136 | return new JsonObject()
137 | .put("name", name)
138 | .put("ask", ask)
139 | .put("bid", bid)
140 | .put("shares", available);
141 | }
142 |
143 | }
--------------------------------------------------------------------------------
/portfolio-service/src/test/java/io/vertx/workshop/portfolio/impl/PortfolioVerticleTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.portfolio.impl;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.serviceproxy.ServiceProxyBuilder;
5 | import io.vertx.workshop.portfolio.Portfolio;
6 | import io.vertx.workshop.portfolio.PortfolioService;
7 | import org.junit.Ignore;
8 | import org.junit.Test;
9 |
10 | import java.util.concurrent.atomic.AtomicReference;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 | import static org.awaitility.Awaitility.await;
14 | import static org.hamcrest.CoreMatchers.not;
15 | import static org.hamcrest.CoreMatchers.nullValue;
16 |
17 | @Ignore("De-ignore me once you have implemented the methods")
18 | public class PortfolioVerticleTest {
19 |
20 | @Test
21 | public void testServiceAccess() {
22 | Vertx vertx = Vertx.vertx();
23 | vertx.deployVerticle(PortfolioVerticle.class.getName());
24 |
25 | PortfolioService proxy = new ServiceProxyBuilder(vertx).setAddress(PortfolioService.ADDRESS).build(PortfolioService.class);
26 |
27 | assertThat(proxy).isNotNull();
28 | AtomicReference reference = new AtomicReference<>();
29 | proxy.getPortfolio(ar -> reference.set(ar.result()));
30 |
31 | await().untilAtomic(reference, not(nullValue()));
32 |
33 | vertx.close();
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/quote-generator/README.md:
--------------------------------------------------------------------------------
1 | # Quote generator
2 |
3 | The quote generator simulates the evolution of the values of 3 companies. Every quote is sent on the event bus. It
4 | also exposes a HTTP endpoint to retrieve the last quote of each company.
5 |
6 |
7 | ## Build
8 |
9 | ```
10 | mvn clean package
11 | ```
12 |
13 | ## Deploy
14 |
15 | ```
16 | mvn fabric8:deploy
17 | ```
18 |
--------------------------------------------------------------------------------
/quote-generator/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 |
9 | io.vertx.workshop
10 | vertx-kubernetes-workshop
11 | 1.0-SNAPSHOT
12 |
13 |
14 | quote-generator
15 |
16 |
17 |
18 | io.vertx.workshop.quote.GeneratorConfigVerticle
19 |
20 | vertx-cluster
21 |
22 |
23 |
24 |
25 | io.vertx
26 | vertx-config
27 |
28 |
29 | io.vertx
30 | vertx-infinispan
31 |
32 |
33 | org.infinispan
34 | infinispan-cloud
35 |
36 |
37 |
38 |
39 |
40 |
41 | io.reactiverse
42 | vertx-maven-plugin
43 |
44 |
45 | io.fabric8
46 | fabric8-maven-plugin
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/quote-generator/src/kubernetes/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "http.port": 35000,
3 | "companies": [
4 | {
5 | "name": "MacroHard",
6 | "symbol": "MCH",
7 | "volume": 95000,
8 | "price": 600,
9 | "variation": 100
10 | },
11 | {
12 | "name": "Divinator",
13 | "symbol": "DVN",
14 | "volume": 98000,
15 | "price": 650,
16 | "variation": 50
17 | },
18 | {
19 | "name": "Black Coat",
20 | "symbol": "BCT",
21 | "volume": 90000,
22 | "price": 550,
23 | "variation": 150
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/quote-generator/src/main/fabric8/deployment.yml:
--------------------------------------------------------------------------------
1 | spec:
2 | template:
3 | spec:
4 | # Declare a volume mounting the config map
5 | volumes:
6 | - configMap:
7 | # Name of the config map
8 | name: app-config
9 | # Define the items from the config map to mount
10 | items:
11 | - key: config.json
12 | path: config.json
13 | # Volume name (used as reference below)
14 | name: config
15 | containers:
16 | - name: vertx
17 | env:
18 | - name: KUBERNETES_NAMESPACE
19 | valueFrom:
20 | fieldRef:
21 | apiVersion: v1
22 | fieldPath: metadata.namespace
23 | - name: JAVA_OPTIONS
24 | value: '-Dvertx.cacheDirBase=/tmp -Dvertx.jgroups.config=default-configs/default-jgroups-kubernetes.xml -Djava.net.preferIPv4Stack=true'
25 | - name: JAVA_ARGS
26 | value: '-cluster'
27 | # Mount the volume define above in /deployments/config
28 | volumeMounts:
29 | - name: config
30 | mountPath: /deployments/config
31 |
--------------------------------------------------------------------------------
/quote-generator/src/main/java/io/vertx/workshop/quote/GeneratorConfigVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.reactivex.Observable;
4 | import io.vertx.config.ConfigRetrieverOptions;
5 | import io.vertx.config.ConfigStoreOptions;
6 | import io.vertx.core.DeploymentOptions;
7 | import io.vertx.core.Future;
8 | import io.vertx.core.json.JsonObject;
9 | import io.vertx.reactivex.CompletableHelper;
10 | import io.vertx.reactivex.config.ConfigRetriever;
11 | import io.vertx.reactivex.core.AbstractVerticle;
12 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery;
13 | import io.vertx.reactivex.servicediscovery.types.MessageSource;
14 | import io.vertx.servicediscovery.Record;
15 |
16 | /**
17 | * a verticle generating "fake" quotes based on the configuration.
18 | */
19 | public class GeneratorConfigVerticle extends AbstractVerticle {
20 |
21 | /**
22 | * The address on which the data are sent.
23 | */
24 | static final String ADDRESS = "market";
25 |
26 | private Record record;
27 | private ServiceDiscovery discovery;
28 |
29 | /**
30 | * This method is called when the verticle is deployed.
31 | */
32 | @Override
33 | public void start(Future future) {
34 | discovery = ServiceDiscovery.create(vertx);
35 | ConfigRetriever retriever = ConfigRetriever.create(vertx, getConfigurationOptions());
36 |
37 | retriever.rxGetConfig()
38 | // Read the configuration, and deploy a MarketDataVerticle for each company listed in the configuration.
39 | .flatMap(config ->
40 | Observable.fromIterable(config.getJsonArray("companies"))
41 | .cast(JsonObject.class)
42 | // Deploy the verticle with a configuration.
43 | .flatMapSingle(company -> vertx.rxDeployVerticle(MarketDataVerticle.class.getName(),
44 | new DeploymentOptions().setConfig(company)))
45 | .toList()
46 | )
47 | // Deploy another verticle
48 | .flatMap(l -> vertx.rxDeployVerticle(RestQuoteAPIVerticle.class.getName()))
49 | // Expose the market-data message source
50 | .flatMap(x -> discovery.rxPublish(MessageSource.createRecord("market-data", ADDRESS)))
51 | .subscribe((rec, err) -> {
52 | if (rec != null) {
53 | this.record = rec;
54 | future.complete();
55 | } else {
56 | future.fail(err);
57 | }
58 | });
59 | }
60 |
61 | @Override
62 | public void stop(Future future) throws Exception {
63 | if (record != null) {
64 | discovery.rxUnpublish(record.getRegistration()).subscribe(CompletableHelper.toObserver(future));
65 | } else {
66 | future.complete();
67 | }
68 | }
69 |
70 | private ConfigRetrieverOptions getConfigurationOptions() {
71 | JsonObject path = new JsonObject().put("path", "config/config.json");
72 | return new ConfigRetrieverOptions()
73 | .addStore(new ConfigStoreOptions().setType("file").setConfig(path));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/quote-generator/src/main/java/io/vertx/workshop/quote/MarketDataVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | import java.util.Objects;
7 | import java.util.Random;
8 |
9 | /**
10 | * A verticle simulating the evaluation of a company evaluation in a very unrealistic and irrational way.
11 | * It emits the new data on the `market` address on the event bus.
12 | */
13 | public class MarketDataVerticle extends AbstractVerticle {
14 |
15 | String name;
16 | int variation;
17 | long period;
18 | String symbol;
19 | int stocks;
20 | double price;
21 |
22 | double bid;
23 | double ask;
24 |
25 | int share;
26 | private double value;
27 |
28 | private final Random random = new Random();
29 |
30 | /**
31 | * Method called when the verticle is deployed.
32 | */
33 | @Override
34 | public void start() {
35 | // Retrieve the configuration, and initialize the verticle.
36 | JsonObject config = config();
37 | init(config);
38 |
39 | // Every `period` ms, the given Handler is called.
40 | vertx.setPeriodic(period, l -> {
41 | compute();
42 | send();
43 | });
44 | }
45 |
46 | /**
47 | * Read the configuration and set the initial values.
48 | * @param config the configuration
49 | */
50 | void init(JsonObject config) {
51 | period = config.getLong("period", 3000L);
52 | variation = config.getInteger("variation", 100);
53 | name = config.getString("name");
54 | Objects.requireNonNull(name);
55 | symbol = config.getString("symbol", name);
56 | stocks = config.getInteger("volume", 10000);
57 | price = config.getDouble("price", 100.0);
58 |
59 | value = price;
60 | ask = price + random.nextInt(variation / 2);
61 | bid = price + random.nextInt(variation / 2);
62 |
63 | share = stocks / 2;
64 |
65 | System.out.println("Initialized " + name);
66 | }
67 |
68 | /**
69 | * Sends the market data on the event bus.
70 | */
71 | private void send() {
72 | vertx.eventBus().publish(GeneratorConfigVerticle.ADDRESS, toJson());
73 | }
74 |
75 | /**
76 | * Compute the new evaluation...
77 | */
78 | void compute() {
79 |
80 | if (random.nextBoolean()) {
81 | value = value + random.nextInt(variation);
82 | ask = value + random.nextInt(variation / 2);
83 | bid = value + random.nextInt(variation / 2);
84 | } else {
85 | value = value - random.nextInt(variation);
86 | ask = value - random.nextInt(variation / 2);
87 | bid = value - random.nextInt(variation / 2);
88 | }
89 |
90 | if (value <= 0) {
91 | value = 1.0;
92 | }
93 | if (ask <= 0) {
94 | ask = 1.0;
95 | }
96 | if (bid <= 0) {
97 | bid = 1.0;
98 | }
99 |
100 | if (random.nextBoolean()) {
101 | // Adjust share
102 | int shareVariation = random.nextInt(100);
103 | if (shareVariation > 0 && share + shareVariation < stocks) {
104 | share += shareVariation;
105 | } else if (shareVariation < 0 && share + shareVariation > 0) {
106 | share += shareVariation;
107 | }
108 | }
109 | }
110 |
111 | /**
112 | * @return a json representation of the market data (quote). The structure is close to
113 | * https://en.wikipedia.org/wiki/Market_data.
114 | */
115 | private JsonObject toJson() {
116 | return new JsonObject()
117 | .put("exchange", "Vert.x stock exchange")
118 | .put("symbol", symbol)
119 | .put("name", name)
120 | .put("bid", bid)
121 | .put("ask", ask)
122 | .put("volume", stocks)
123 | .put("open", price)
124 | .put("shares", share);
125 |
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/quote-generator/src/main/java/io/vertx/workshop/quote/RestQuoteAPIVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.reactivex.Completable;
4 | import io.vertx.core.json.Json;
5 | import io.vertx.core.json.JsonObject;
6 | import io.vertx.reactivex.core.AbstractVerticle;
7 | import io.vertx.reactivex.core.http.HttpServer;
8 | import io.vertx.reactivex.core.http.HttpServerResponse;
9 | import io.vertx.reactivex.core.eventbus.Message;
10 |
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | /**
15 | * This verticle exposes a HTTP endpoint to retrieve the current / last values of the maker data (quotes).
16 | */
17 | public class RestQuoteAPIVerticle extends AbstractVerticle {
18 |
19 | private Map quotes = new HashMap<>();
20 |
21 | @Override
22 | public Completable rxStart() {
23 | // Get the stream of messages sent on the "market" address
24 | vertx.eventBus().consumer(GeneratorConfigVerticle.ADDRESS).toFlowable()
25 | // TODO Extract the body of the message using `.map(msg -> {})`
26 | // ----
27 | //
28 | // ----
29 | // TODO For each message, populate the `quotes` map with the received quote. Use `.doOnNext(json -> {})`
30 | // Quotes are json objects you can retrieve from the message body
31 | // The map is structured as follows: name -> quote
32 | // ----
33 | //
34 | // ----
35 | .subscribe();
36 |
37 | HttpServer server = vertx.createHttpServer();
38 | server.requestStream().toFlowable()
39 | .doOnNext(request -> {
40 | HttpServerResponse response = request.response()
41 | .putHeader("content-type", "application/json");
42 |
43 | // TODO Handle the HTTP request
44 | // The request handler returns a specific quote if the `name` parameter is set, or the whole map if none.
45 | // To write the response use: `response.end(content)`
46 | // If the name is set but not found, you should return 404 (use response.setStatusCode(404)).
47 | // To encode a Json object, use the `encorePrettily` method
48 | // ----
49 |
50 | // Remove this line
51 | response.end(Json.encodePrettily(quotes));
52 |
53 | // ----
54 | })
55 | .subscribe();
56 |
57 | return server.rxListen(config().getInteger("http.port", 8080))
58 | .ignoreElement();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/quote-generator/src/main/solution/io/vertx/workshop/quote/GeneratorConfigVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.reactivex.Observable;
4 | import io.vertx.config.ConfigRetrieverOptions;
5 | import io.vertx.config.ConfigStoreOptions;
6 | import io.vertx.core.DeploymentOptions;
7 | import io.vertx.core.Future;
8 | import io.vertx.core.json.JsonObject;
9 | import io.vertx.reactivex.CompletableHelper;
10 | import io.vertx.reactivex.config.ConfigRetriever;
11 | import io.vertx.reactivex.core.AbstractVerticle;
12 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery;
13 | import io.vertx.reactivex.servicediscovery.types.MessageSource;
14 | import io.vertx.servicediscovery.Record;
15 |
16 | /**
17 | * a verticle generating "fake" quotes based on the configuration.
18 | */
19 | public class GeneratorConfigVerticle extends AbstractVerticle {
20 |
21 | /**
22 | * The address on which the data are sent.
23 | */
24 | static final String ADDRESS = "market";
25 |
26 | private Record record;
27 | private ServiceDiscovery discovery;
28 |
29 | /**
30 | * This method is called when the verticle is deployed.
31 | */
32 | @Override
33 | public void start(Future future) {
34 | discovery = ServiceDiscovery.create(vertx);
35 | ConfigRetriever retriever = ConfigRetriever.create(vertx, getConfigurationOptions());
36 |
37 | retriever.rxGetConfig()
38 | // Read the configuration, and deploy a MarketDataVerticle for each company listed in the configuration.
39 | .flatMap(config ->
40 | Observable.fromIterable(config.getJsonArray("companies"))
41 | .cast(JsonObject.class)
42 | // Deploy the verticle with a configuration.
43 | .flatMapSingle(company -> vertx.rxDeployVerticle(MarketDataVerticle.class.getName(),
44 | new DeploymentOptions().setConfig(company)))
45 | .toList()
46 | )
47 | // Deploy another verticle
48 | .flatMap(l -> vertx.rxDeployVerticle(RestQuoteAPIVerticle.class.getName()))
49 | // Expose the market-data message source
50 | .flatMap(x -> discovery.rxPublish(MessageSource.createRecord("market-data", ADDRESS)))
51 | .subscribe((rec, err) -> {
52 | if (rec != null) {
53 | this.record = rec;
54 | future.complete();
55 | } else {
56 | future.fail(err);
57 | }
58 | });
59 | }
60 |
61 | @Override
62 | public void stop(Future future) throws Exception {
63 | if (record != null) {
64 | discovery.rxUnpublish(record.getRegistration()).subscribe(CompletableHelper.toObserver(future));
65 | } else {
66 | future.complete();
67 | }
68 | }
69 |
70 | private ConfigRetrieverOptions getConfigurationOptions() {
71 | JsonObject path = new JsonObject().put("path", "config/config.json");
72 | return new ConfigRetrieverOptions().addStore(new ConfigStoreOptions().setType("file").setConfig(path));
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/quote-generator/src/main/solution/io/vertx/workshop/quote/MarketDataVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | import java.util.Objects;
7 | import java.util.Random;
8 |
9 | /**
10 | * A verticle simulating the evaluation of a company evaluation in a very unrealistic and irrational way.
11 | * It emits the new data on the `market` address on the event bus.
12 | */
13 | public class MarketDataVerticle extends AbstractVerticle {
14 |
15 | String name;
16 | int variation;
17 | long period;
18 | String symbol;
19 | int stocks;
20 | double price;
21 |
22 | double bid;
23 | double ask;
24 |
25 | int share;
26 | private double value;
27 |
28 | private final Random random = new Random();
29 |
30 | /**
31 | * Method called when the verticle is deployed.
32 | */
33 | @Override
34 | public void start() {
35 | // Retrieve the configuration, and initialize the verticle.
36 | JsonObject config = config();
37 | init(config);
38 |
39 | // Every `period` ms, the given Handler is called.
40 | vertx.setPeriodic(period, l -> {
41 | compute();
42 | send();
43 | });
44 | }
45 |
46 | /**
47 | * Read the configuration and set the initial values.
48 | * @param config the configuration
49 | */
50 | void init(JsonObject config) {
51 | period = config.getLong("period", 3000L);
52 | variation = config.getInteger("variation", 100);
53 | name = config.getString("name");
54 | Objects.requireNonNull(name);
55 | symbol = config.getString("symbol", name);
56 | stocks = config.getInteger("volume", 10000);
57 | price = config.getDouble("price", 100.0);
58 |
59 | value = price;
60 | ask = price + random.nextInt(variation / 2);
61 | bid = price + random.nextInt(variation / 2);
62 |
63 | share = stocks / 2;
64 |
65 | System.out.println("Initialized " + name);
66 | }
67 |
68 | /**
69 | * Sends the market data on the event bus.
70 | */
71 | private void send() {
72 | vertx.eventBus().publish(GeneratorConfigVerticle.ADDRESS, toJson());
73 | }
74 |
75 | /**
76 | * Compute the new evaluation...
77 | */
78 | void compute() {
79 |
80 | if (random.nextBoolean()) {
81 | value = value + random.nextInt(variation);
82 | ask = value + random.nextInt(variation / 2);
83 | bid = value + random.nextInt(variation / 2);
84 | } else {
85 | value = value - random.nextInt(variation);
86 | ask = value - random.nextInt(variation / 2);
87 | bid = value - random.nextInt(variation / 2);
88 | }
89 |
90 | if (value <= 0) {
91 | value = 1.0;
92 | }
93 | if (ask <= 0) {
94 | ask = 1.0;
95 | }
96 | if (bid <= 0) {
97 | bid = 1.0;
98 | }
99 |
100 | if (random.nextBoolean()) {
101 | // Adjust share
102 | int shareVariation = random.nextInt(100);
103 | if (shareVariation > 0 && share + shareVariation < stocks) {
104 | share += shareVariation;
105 | } else if (shareVariation < 0 && share + shareVariation > 0) {
106 | share += shareVariation;
107 | }
108 | }
109 | }
110 |
111 | /**
112 | * @return a json representation of the market data (quote). The structure is close to
113 | * https://en.wikipedia.org/wiki/Market_data.
114 | */
115 | private JsonObject toJson() {
116 | return new JsonObject()
117 | .put("exchange", "Vert.x stock exchange")
118 | .put("symbol", symbol)
119 | .put("name", name)
120 | .put("bid", bid)
121 | .put("ask", ask)
122 | .put("volume", stocks)
123 | .put("open", price)
124 | .put("shares", share);
125 |
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/quote-generator/src/main/solution/io/vertx/workshop/quote/RestQuoteAPIVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.reactivex.Completable;
4 | import io.vertx.core.json.Json;
5 | import io.vertx.core.json.JsonObject;
6 | import io.vertx.reactivex.core.AbstractVerticle;
7 | import io.vertx.reactivex.core.eventbus.Message;
8 | import io.vertx.reactivex.core.http.HttpServer;
9 | import io.vertx.reactivex.core.http.HttpServerResponse;
10 | import io.vertx.reactivex.core.eventbus.Message;
11 |
12 |
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | /**
17 | * This verticle exposes a HTTP endpoint to retrieve the current / last values of the maker data (quotes).
18 | */
19 | public class RestQuoteAPIVerticle extends AbstractVerticle {
20 |
21 | private Map quotes = new HashMap<>();
22 |
23 | @Override
24 | public Completable rxStart() {
25 | // Get the stream of messages sent on the "market" address
26 | vertx.eventBus().consumer(GeneratorConfigVerticle.ADDRESS).toFlowable()
27 | // TODO Extract the body of the message using `.map(msg -> {})`
28 | // ----
29 | .map(Message::body)
30 | // ----
31 | // TODO For each message, populate the `quotes` map with the received quote. Use `.doOnNext(json -> {})`
32 | // Quotes are json objects you can retrieve from the message body
33 | // The map is structured as follows: name -> quote
34 | // ----
35 | .doOnNext(json -> {
36 | quotes.put(json.getString("name"), json); // 2
37 | })
38 | // ----
39 | .subscribe();
40 |
41 | HttpServer server = vertx.createHttpServer();
42 | server.requestStream().toFlowable()
43 | .doOnNext(request -> {
44 | HttpServerResponse response = request.response()
45 | .putHeader("content-type", "application/json");
46 |
47 | // TODO Handle the HTTP request
48 | // The request handler returns a specific quote if the `name` parameter is set, or the whole map if none.
49 | // To write the response use: `response.end(content)`
50 | // If the name is set but not found, you should return 404 (use response.setStatusCode(404)).
51 | // To encode a Json object, use the `encorePrettily` method
52 | // ----
53 |
54 | String company = request.getParam("name");
55 | if (company == null) {
56 | String content = Json.encodePrettily(quotes);
57 | response.end(content);
58 | } else {
59 | JsonObject quote = quotes.get(company);
60 | if (quote == null) {
61 | response.setStatusCode(404).end();
62 | } else {
63 | response.end(quote.encodePrettily());
64 | }
65 | }
66 | // ----
67 | })
68 | .subscribe();
69 |
70 | return server.rxListen(config().getInteger("http.port", 8080))
71 | .ignoreElement();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/quote-generator/src/test/java/io/vertx/workshop/quote/GeneratorConfigVerticleTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.vertx.core.DeploymentOptions;
4 | import io.vertx.core.Vertx;
5 | import io.vertx.core.json.JsonObject;
6 | import org.junit.Test;
7 |
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.nio.file.Files;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import static org.assertj.core.api.Assertions.assertThat;
15 | import static org.awaitility.Awaitility.await;
16 |
17 |
18 | public class GeneratorConfigVerticleTest {
19 |
20 | @Test
21 | public void test() throws IOException {
22 | byte[] bytes = Files.readAllBytes(new File("src/test/resources/config/config.json").toPath());
23 | JsonObject config = new JsonObject(new String(bytes, "UTF-8"));
24 |
25 | Vertx vertx = Vertx.vertx();
26 |
27 | List mch = new ArrayList<>();
28 | List dvn = new ArrayList<>();
29 | List bct = new ArrayList<>();
30 |
31 | vertx.eventBus().consumer(GeneratorConfigVerticle.ADDRESS, message -> {
32 | JsonObject quote = (JsonObject) message.body();
33 | System.out.println(quote.encodePrettily());
34 | assertThat(quote.getDouble("bid")).isGreaterThan(0);
35 | assertThat(quote.getDouble("ask")).isGreaterThan(0);
36 | assertThat(quote.getInteger("volume")).isGreaterThan(0);
37 | assertThat(quote.getInteger("shares")).isGreaterThan(0);
38 | switch (quote.getString("symbol")) {
39 | case "MCH":
40 | mch.add(quote);
41 | break;
42 | case "DVN":
43 | dvn.add(quote);
44 | break;
45 | case "BCT":
46 | bct.add(quote);
47 | break;
48 | }
49 | });
50 |
51 | vertx.deployVerticle(GeneratorConfigVerticle.class.getName(), new DeploymentOptions().setConfig(config));
52 |
53 | await().until(() -> mch.size() > 10);
54 | await().until(() -> dvn.size() > 10);
55 | await().until(() -> bct.size() > 10);
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/quote-generator/src/test/java/io/vertx/workshop/quote/MarketDataVerticleTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.quote;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import org.junit.Test;
5 |
6 | import static org.assertj.core.api.Assertions.assertThat;
7 |
8 |
9 | public class MarketDataVerticleTest {
10 |
11 |
12 | @Test
13 | public void testComputation() {
14 | JsonObject json = new JsonObject()
15 | .put("name", "test")
16 | .put("symbol", "TT");
17 |
18 | MarketDataVerticle verticle = new MarketDataVerticle();
19 | verticle.init(json);
20 |
21 | int volume = verticle.stocks;
22 |
23 | assertThat(verticle.ask).isGreaterThan(0.0);
24 | assertThat(verticle.bid).isGreaterThan(0.0);
25 | assertThat(verticle.share).isGreaterThanOrEqualTo(0).isLessThanOrEqualTo(volume);
26 |
27 | for (int i = 0; i < 1000000; i++) {
28 | verticle.compute();
29 | assertThat(verticle.ask).isGreaterThan(0.0);
30 | assertThat(verticle.bid).isGreaterThan(0.0);
31 | assertThat(verticle.share).isGreaterThanOrEqualTo(0).isLessThanOrEqualTo(volume);
32 | }
33 |
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/quote-generator/src/test/resources/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "companies": [
3 | {
4 | "name": "MacroHard",
5 | "symbol": "MCH",
6 | "volume": 100000,
7 | "price": 600,
8 | "variation": 200,
9 | "period": 100
10 | },
11 | {
12 | "name": "Divinator",
13 | "symbol": "DVN",
14 | "volume": 500000,
15 | "price": 800,
16 | "variation": 50,
17 | "period": 100
18 | },
19 | {
20 | "name": "Black Coat",
21 | "symbol": "BCT",
22 | "volume": 90000,
23 | "price": 300,
24 | "variation": 150,
25 | "period": 100
26 | }
27 | ]
28 |
29 | }
--------------------------------------------------------------------------------
/scripts/cleanup-project.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | PROJECT=$(oc project -q)
3 | echo "Cleaning up current project: ${PROJECT}"
4 | # https://docs.openshift.org/latest/cli_reference/basic_cli_operations.html#object-types
5 | oc delete build --all
6 | oc delete bc --all
7 |
8 | oc delete dc --all
9 | oc delete deploy --all
10 |
11 | oc delete is --all
12 | oc delete istag --all
13 | oc delete isimage --all
14 |
15 | oc delete job --all
16 |
17 | oc delete po --all
18 | oc delete rc --all
19 | oc delete rs --all
20 |
21 | oc delete secrets --all
22 | oc delete configmap --all
23 |
24 | oc delete services --all
25 |
26 | oc delete routes --all
27 |
28 | oc delete template --all
29 |
30 |
--------------------------------------------------------------------------------
/scripts/create-project.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | RED='\033[0;31m'
4 | NC='\033[0m' # No Color
5 | YELLOW='\033[0;33m'
6 | BLUE='\033[0;34m'
7 |
8 | PROJECT=vertx-kubernetes-workshop
9 |
10 | echo -e "${BLUE}Creating project ${PROJECT} ${NC}"
11 | oc new-project ${PROJECT}
12 | echo -e "${BLUE}Adding permissions to ${PROJECT} ${NC}"
13 | oc policy add-role-to-user view admin -n ${PROJECT}
14 | oc policy add-role-to-user view -n ${PROJECT} -z default
15 | oc policy add-role-to-group view system:serviceaccounts -n ${PROJECT}
16 |
17 | oc project
18 | echo -e "${BLUE}Done ! ${NC}"
--------------------------------------------------------------------------------
/scripts/deploy-all.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo "Deploying 3rd party curency service"
3 | cd currency-3rdparty-service
4 | mvn clean fabric8:deploy
5 |
6 | cd ../micro-trader-dashboard
7 | echo "Deploying the dashboard"
8 | mvn clean fabric8:deploy
9 |
10 | cd ../quote-generator
11 | echo "Deploying the quote generator"
12 | oc create configmap app-config --from-file=src/kubernetes/config.json
13 | mvn clean fabric8:deploy -Psolution
14 |
15 | cd ../portfolio-service
16 | echo "Deploying the portfolio service"
17 | mvn clean fabric8:deploy -Psolution
18 |
19 | cd ../compulsive-traders
20 | mvn clean fabric8:deploy -Psolution
21 | oc scale dc compulsive-traders --replicas=2
22 |
23 | cd ../audit-service
24 | echo "Deploying the audit service"
25 | oc create -f src/kubernetes/database-secret.yaml
26 | oc new-app -e POSTGRESQL_USER=admin -ePOSTGRESQL_PASSWORD=secret -ePOSTGRESQL_DATABASE=audit registry.access.redhat.com/rhscl/postgresql-94-rhel7 --name=audit-database
27 | mvn clean fabric8:deploy -Psolution
28 |
29 |
30 | cd ../currency-service
31 | echo "Deploying the currency service proxy"
32 | mvn clean fabric8:deploy -Psolution
--------------------------------------------------------------------------------
/vertx-exercises/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | io.vertx.workshop
9 | vertx-kubernetes-workshop
10 | 1.0-SNAPSHOT
11 |
12 |
13 | vertx-exercises
14 |
15 |
16 |
17 |
18 | solution
19 |
20 | src/main/java
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | org.codehaus.mojo
29 | exec-maven-plugin
30 | 1.6.0
31 |
32 |
33 |
34 | exec
35 |
36 |
37 |
38 |
39 | maven
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise1.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.Vertx;
4 |
5 | /**
6 | * * Create the Vert.x instance
7 | * * Create a HTTP server
8 | */
9 | public class Exercise1 {
10 |
11 | public static void main(String[] args) {
12 | // 1 - Create the Vert.x instance using Vertx.vertx (use io.vertx.core.Vertx)
13 | // TODO
14 |
15 | // 2 - Create a HTTP server using the `createHttpServer` method. Set a request handler doing:
16 | // `req.response().end("hello")`
17 | // Call the listen method with `8080` as parameter.
18 | // TODO
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise2.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.Vertx;
4 |
5 | /**
6 | * * Create the HTTP server in the `Exercise2Verticle`
7 | * * Deploy this verticle from this class
8 | */
9 | public class Exercise2 {
10 |
11 | public static void main(String[] args) {
12 | // 1 - Create the Vert.x instance using Vertx.vertx (use io.vertx.core.Vertx)
13 | // TODO
14 |
15 | // 2 - Deploy the `Exercise2Verticle` verticle using: vertx.deployVerticle(className);
16 | // TODO
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise2Verticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 |
5 | /**
6 | * Create a HTTP server in the `start` method.
7 | */
8 | public class Exercise2Verticle extends AbstractVerticle {
9 |
10 | @Override
11 | public void start() throws Exception {
12 | // You can access the vert.x instance your deployed on using the `vertx` (inherited) field
13 |
14 | // Create a HTTP server
15 | // Instead of "hello", display the name of the thread serving the request (using Thread.currentThread()
16 | // .getName())
17 | // TODO
18 |
19 | }
20 |
21 |
22 | /**
23 | * Method used in the Exercise 3.
24 | */
25 | private void sleep() {
26 | try {
27 | Thread.sleep(3000L);
28 | } catch (InterruptedException e) {
29 | // Ignore me
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise4.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.Vertx;
4 |
5 | /**
6 | * Launcher for the Exercise 4, it just deploys the 2 verticles.
7 | */
8 | public class Exercise4 {
9 |
10 | public static void main(String[] args) {
11 | Vertx vertx = Vertx.vertx();
12 | vertx.deployVerticle(Exercise4SenderVerticle.class.getName());
13 | vertx.deployVerticle(Exercise4ReceiverVerticle.class.getName());
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise4ReceiverVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | /**
7 | * A verticle receiving messages.
8 | */
9 | public class Exercise4ReceiverVerticle extends AbstractVerticle {
10 |
11 | @Override
12 | public void start() throws Exception {
13 | // Retrieve the event bus and register a consumer on the "greetings" address. For each message, print it on
14 | // the console. You can retrieve the message body using `body()`. Use the method `encodePrettily`
15 | // on the retrieved Json body to print it nicely.
16 | // TODO
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise4SenderVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.eventbus.EventBus;
5 | import io.vertx.core.json.JsonObject;
6 |
7 | /**
8 | * A verticle periodically sending a message on the event bus.
9 | */
10 | public class Exercise4SenderVerticle extends AbstractVerticle {
11 |
12 | @Override
13 | public void start() throws Exception {
14 | // Retrieve the event bus
15 | EventBus eventBus = vertx.eventBus();
16 |
17 | // Execute the given handler every 2000 ms
18 | vertx.setPeriodic(2000, l -> {
19 | // Use the eventBus() method to retrieve the event bus and send a "{"message":hello"} JSON message on the
20 | // "greetings" address.
21 |
22 | // 1 - Create the JSON object using the JsonObject class, and `put` the 'message':'hello' entry
23 | // TODO
24 |
25 | // 2 - Use the `send` method of the event bus to _send_ the message. Messages sent with the `send` method
26 | // are received by a single consumer. Messages sent with the `publish` method are received by all
27 | // registered consumers.
28 | // TODO
29 |
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise5.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.Vertx;
4 |
5 | /**
6 | * Launcher for the Exercise 5.
7 | */
8 | public class Exercise5 {
9 |
10 | public static void main(String[] args) {
11 | Vertx vertx = Vertx.vertx();
12 | vertx.deployVerticle(Exercise5HttpVerticle.class.getName());
13 | vertx.deployVerticle(Exercise5ProcessorVerticle.class.getName());
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise5HttpVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.json.JsonObject;
5 |
6 | /**
7 | * A verticle using the request-reply event bus delivery mechanism to handle HTTP requests.
8 | */
9 | public class Exercise5HttpVerticle extends AbstractVerticle {
10 |
11 | @Override
12 | public void start() throws Exception {
13 | vertx.createHttpServer()
14 | .requestHandler(req -> {
15 |
16 | // 1 - Retrieve the `name` (query) parameter, set it to `world if null`. You can retrieve the
17 | // parameter using: `req.getParam()`
18 | // TODO
19 |
20 | // 2 - Send a message on the event bus using the `send` method. Pass a reply handler receiving the
21 | // response. As the expected object is a Json structure, you can use `vertx.eventBus()
22 | // .send(...`).
23 | // In the reply handler, you receive an `AsyncResult`. This structure describes the outcome from an
24 | // asynchronous operation: a success (and a result) or a failure (and a cause). If it's a failure
25 | // (check with the `failed` method), write a 500 HTTP response with the cause (`cause.getMessage()`) as
26 | // payload. On success, write the body into the HTTP response.
27 | // TODO
28 |
29 | })
30 | .listen(8080);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise5ProcessorVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.eventbus.EventBus;
5 | import io.vertx.core.json.JsonObject;
6 |
7 | /**
8 | * A verticle replying to incoming messages.
9 | */
10 | public class Exercise5ProcessorVerticle extends AbstractVerticle {
11 |
12 | @Override
13 | public void start() throws Exception {
14 | EventBus eventBus = vertx.eventBus();
15 |
16 | // Register a consumer and call the `reply` method with a JSON object containing the greeting message. ~
17 | // parameter is passed in the incoming message body (a name). For example, if the incoming message is the
18 | // String "vert.x", the reply contains: `{"message" : "hello vert.x"}`.
19 | // Unlike the previous exercise, the incoming message has a `String` body.
20 | // TODO
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise6.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.vertx.core.Vertx;
4 |
5 | /**
6 | * Launcher for the Exercise 6.
7 | */
8 | public class Exercise6 {
9 |
10 | public static void main(String[] args) {
11 | Vertx vertx = Vertx.vertx();
12 | vertx.deployVerticle(Exercise6HttpVerticle.class.getName());
13 | // Keep using the once developed previously
14 | vertx.deployVerticle(Exercise5ProcessorVerticle.class.getName());
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/vertx-exercises/src/main/java/io/vertx/workshop/exercise/Exercise6HttpVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.workshop.exercise;
2 |
3 | import io.reactivex.Single;
4 | import io.vertx.core.json.JsonObject;
5 | import io.vertx.reactivex.core.AbstractVerticle;
6 | import io.vertx.reactivex.core.http.HttpServer;
7 |
8 | /**
9 | * A verticle using the request-reply event bus delivery mechanism to handle HTTP requests.
10 | */
11 | public class Exercise6HttpVerticle extends AbstractVerticle {
12 |
13 | @Override
14 | public void start() throws Exception {
15 | vertx.createHttpServer()
16 | .requestHandler(req -> {
17 | String name = req.getParam("name");
18 | if (name == null) {
19 | name = "world";
20 | }
21 |
22 | // Send a message on the event bus using the `send` method. Pass a reply handler receiving the
23 | // response. As the expected object is a Json structure, you can use `vertx.eventBus()
24 | // .send(...`).
25 | // Unlike in the previous exercise, we use the `rxSend` method to retrieve a `Single` stream. We then
26 | // _map_ the result to extract the (encoded as String) Json structure.
27 | // In RX, we must `subscribe` to the stream to trigger the processing. Without nothing happens. There
28 | // are several `subscribe` method, but here we recommend the `BiConsumer` format `(res, err) -> ...`
29 | // If it's a failure (err != null), write a 500 HTTP response with the cause (`err.getMessage()`) as
30 | // payload. On success, write the body (`res`) into the HTTP response.
31 | // TODO
32 |
33 | })
34 | .listen(8080);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------